using HH.WCS.Mobox3.FJJT.api;
|
using HH.WCS.Mobox3.FJJT.util;
|
using Newtonsoft.Json.Linq;
|
using S7.Net;
|
using S7.Net.Types;
|
using System;
|
using System.Collections.Generic;
|
using System.Linq;
|
using System.Text;
|
using System.Threading.Tasks;
|
using System.Web.Services.Description;
|
using static HH.WCS.Mobox3.FJJT.util.Settings;
|
|
namespace HH.WCS.Mobox3.FJJT.device
|
{
|
|
/// <summary>
|
/// 西门子plc
|
/// </summary>
|
public class S7Helper
|
{
|
private static bool debug = true;
|
private static S7.Net.Plc plc = null;
|
static S7Helper()
|
{
|
Init();
|
}
|
private static Dictionary<string, Plc> plcDic = new Dictionary<string, Plc>();
|
private static void Init()
|
{
|
//配置文件读取所有的plc进行初始化
|
try
|
{
|
var plc1 = new Plc(CpuType.S71500, "", 0, 1);
|
plcDic.Add("plc1", plc1);
|
Link(plc1);
|
}
|
catch (Exception ex)
|
{
|
|
Console.WriteLine("S7Helper Init err=" + ex.Message);
|
}
|
}
|
private static Plc GetPlc(string plc)
|
{
|
if (plcDic.ContainsKey(plc))
|
{
|
return plcDic[plc];
|
}
|
else
|
{
|
return null;
|
}
|
}
|
public static Dictionary<string, string> s7TestData = new Dictionary<string, string>();
|
private static void Link(Plc plc)
|
{
|
try
|
{
|
//if (!plc.IsConnected) {
|
plc.Close();
|
plc.Open();
|
if (plc.IsConnected)
|
{
|
Console.WriteLine($"已连接到plc{plc.IP}");
|
}
|
else
|
{
|
Console.WriteLine($"plc{plc.IP}连接失败");
|
LogHelper.Info($"plc{plc.IP}连接失败", "Plc");
|
}
|
|
//}
|
}
|
catch (Exception ex)
|
{
|
Console.WriteLine($"plc{plc.IP}连接失败,err={ex.Message}");
|
LogHelper.Info($"plc{plc.IP}连接失败,err={ex.Message}");
|
//Init();
|
}
|
|
}
|
//https://www.ad.siemens.com.cn/productportal/Prods/S7-1200_PLC_EASY_PLUS/SmartSMS/060.html
|
//https://www.ad.siemens.com.cn/productportal/Prods/S7-1200_PLC_EASY_PLUS/07-Program/02-basic/01-Data_Type/09-String.html
|
|
|
|
internal static short[] ReadInt(string device, int db, int byteAddr, int count)
|
{
|
short[] result = null;
|
try
|
{
|
if (debug)
|
{
|
var s7Key = $"int_{db}_{byteAddr}_{count}";
|
if (s7TestData.ContainsKey(s7Key))
|
{
|
var data = s7TestData[s7Key].Split(',');
|
if (data.Length == count)
|
{
|
result = Array.ConvertAll(data, s => short.Parse(s));
|
}
|
else
|
{
|
result = new short[count];
|
s7TestData[s7Key] = string.Join(",", result);
|
}
|
Console.WriteLine($"读取plc {device}信息成功, addr={byteAddr} data={string.Join(",", result)}");
|
}
|
}
|
else
|
{
|
var plc = GetPlc(device);
|
if (plc != null)
|
{
|
if (plc.IsConnected)
|
{
|
result = (short[])plc.Read(DataType.DataBlock, db, byteAddr, VarType.Int, count, 0);
|
Console.WriteLine($"读取plc {device}信息成功,ip={plc.IP} addr={byteAddr} data={string.Join(",", result)}");
|
if (result.Length == 0)
|
{
|
Console.WriteLine($"plc {device}准备重新连接");
|
Link(plc);
|
}
|
}
|
else
|
{
|
Console.WriteLine($"准备连接plc {device}");
|
Link(plc);
|
}
|
}
|
else
|
{
|
Console.WriteLine($"plc {device}不存在");
|
}
|
|
}
|
|
}
|
catch (Exception ex)
|
{
|
Console.WriteLine($"ReadInt,device={device} addr={byteAddr} count={count} err={ex.Message}");
|
LogHelper.Error($"ReadInt,device={device} addr={byteAddr} count={count} err={ex.Message}", ex);
|
}
|
return result;
|
}
|
|
/// <summary>
|
///
|
/// </summary>
|
/// <param name="db"></param>
|
/// <param name="byteAddr"></param>
|
/// <param name="data"></param>
|
/// <returns></returns>
|
internal static bool WriteInt(int db, int byteAddr, short data)
|
{
|
var result = false;
|
try
|
{
|
if (plc.IsConnected)
|
{
|
plc.Write(DataType.DataBlock, db, byteAddr, data);
|
Console.WriteLine($"写入plc信息,ip={plc.IP} addr={byteAddr} data={data} ");
|
LogHelper.Info($"写入plc信息,ip={plc.IP} addr={byteAddr} data={data} ");
|
if (result)
|
{
|
//写完再读一次确认
|
var readData = (short)plc.Read(DataType.DataBlock, db, byteAddr, VarType.Int, 1, 0);
|
Console.WriteLine($"读取plc信息,ip={plc.IP} addr={byteAddr} data={data} res={string.Join(", ", readData)}");
|
LogHelper.Info($"读取plc信息,ip={plc.IP} addr={byteAddr} data={data} res={string.Join(", ", readData)}", "PLC");
|
result = readData == data;
|
}
|
|
|
}
|
else
|
{
|
Console.WriteLine("准备连接plc1");
|
Link(plc);
|
}
|
}
|
catch (Exception ex)
|
{
|
LogHelper.Error($"写入plc1信息失败,ip={plc.IP} addr={byteAddr} data={data} err={ex.Message}", ex);
|
}
|
return result;
|
}
|
public static object ReadBit(string device, int db, int byteAddr, byte bitAddr)
|
{
|
object result = null;
|
try
|
{
|
if (debug)
|
{
|
var s7Key = $"bit_{db}_{byteAddr}_{bitAddr}";
|
if (s7TestData.ContainsKey(s7Key))
|
{
|
var data = s7TestData[s7Key];
|
if (data == "1")
|
{
|
result = true;
|
}
|
else { result = false; }
|
Console.WriteLine($"读取plc {device}信息成功, addr={byteAddr} data={result.ToString()}");
|
}
|
}
|
else
|
{
|
var plc = GetPlc(device);
|
if (plc != null)
|
{
|
if (plc.IsConnected)
|
{
|
result = plc.Read(DataType.DataBlock, db, byteAddr, VarType.Int, 1, bitAddr);
|
Console.WriteLine($"读取plc {device}信息成功,ip={plc.IP} addr={byteAddr} data={result.ToString()}");
|
}
|
else
|
{
|
Console.WriteLine($"准备连接plc {device}");
|
Link(plc);
|
}
|
}
|
else
|
{
|
Console.WriteLine($"plc {device}不存在");
|
}
|
}
|
}
|
catch (Exception ex)
|
{
|
Console.WriteLine($"ReadBit,device={device} addr={byteAddr} bit={bitAddr} err={ex.Message}");
|
LogHelper.Error($"ReadBit,device={device} addr={byteAddr} bit={bitAddr} err={ex.Message}", ex);
|
}
|
return result;
|
}
|
public static string ReadStr(string device, int db, int byteAddr, int count)
|
{
|
string result = string.Empty;
|
try
|
{
|
if (debug)
|
{
|
var s7Key = $"str_{db}_{byteAddr}_{count}";
|
if (s7TestData.ContainsKey(s7Key))
|
{
|
var data = s7TestData[s7Key];
|
if (data.Length == count)
|
{
|
result = data;
|
Console.WriteLine($"ReadStr 成功, addr={byteAddr} res={result}");
|
}
|
}
|
}
|
else
|
{
|
if (plc.IsConnected)
|
{
|
result = plc.Read(DataType.DataBlock, 100, byteAddr, VarType.String, count, 0).ToString();
|
Console.WriteLine($"ReadStr 成功,ip={plc.IP} addr={byteAddr} res={result}");
|
if (result.Length == 0)
|
{
|
Link(plc);
|
}
|
}
|
else
|
{
|
Console.WriteLine("准备连接plc");
|
Link(plc);
|
}
|
}
|
}
|
catch (Exception ex)
|
{
|
Console.WriteLine($"ReadStr,device={device} addr={byteAddr} count={count} err={ex.Message}");
|
LogHelper.Error($"ReadStr,device={device} addr={byteAddr} count={count} err={ex.Message}", ex);
|
}
|
return result;
|
}
|
|
#region 批量写入 读取
|
|
/// <summary>
|
/// s7写入取值方法
|
/// </summary>
|
public static void Write()
|
{
|
var plcInfo = Settings.linePlcInfo.Where(a => (a.deviceNo == "1") && a.enable == 1).FirstOrDefault();
|
|
//获取配置文件写入的地址
|
List<S7Model> writemodels = Settings.plcValue.Find(a => a.address == plcInfo.address).read;
|
|
if (writemodels.Count > 0)
|
{
|
writemodels.Find(a => a.addr == 0).value = "1";
|
writemodels.Find(a => a.addr == 2).value = "2";
|
writemodels.Find(a => a.addr == 4).value = "4";
|
writemodels.Find(a => a.addr == 18).value = "TP24121108";
|
byte[] bytes = GetWriteByte(writemodels);
|
if (PLCWrite(plcInfo, 100, bytes))
|
{
|
Console.WriteLine("写入成功");
|
//写入成功校验数据
|
Read();
|
if (Dictvalues[plcInfo.address].Find(a => a.addr == 2).value == "2")
|
{
|
Console.WriteLine("数据校验成功,修改任务状态");
|
}
|
}
|
else
|
{
|
Console.WriteLine("写入失败");
|
}
|
}
|
|
|
}
|
|
/// <summary>
|
/// 将要写的数据转换成byte
|
/// </summary>
|
/// <param name="writemodels"></param>
|
/// <returns></returns>
|
public static byte[] GetWriteByte(List<S7Model> writemodels)
|
{
|
var model = writemodels.OrderByDescending(a => a.addr).First();
|
var readlen = model.addr + model.length;
|
byte[] bytes = new byte[readlen];
|
|
foreach (var item in writemodels)
|
{
|
if (!string.IsNullOrEmpty(item.value))
|
{
|
//转换byte
|
switch (item.type)
|
{
|
case "Int":
|
case "Bit":
|
bytes[item.addr] = (byte)((uint)(int.Parse(item.value) >> 8) & 0xFFu);
|
bytes[item.addr + 1] = (byte)((uint)(int.Parse(item.value) >> 0) & 0xFFu);
|
break;
|
case "DInt":
|
case "Real":
|
bytes[item.addr] = (byte)((uint)(int.Parse(item.value) >> 24) & 0xFFu);
|
bytes[item.addr + 1] = (byte)((uint)(int.Parse(item.value) >> 16) & 0xFFu);
|
bytes[item.addr + 2] = (byte)((uint)(int.Parse(item.value) >> 8) & 0xFFu);
|
bytes[item.addr + 3] = (byte)((uint)(int.Parse(item.value) >> 0) & 0xFFu);
|
break;
|
case "String":
|
case "Byte":
|
var bytevalue = Encoding.UTF8.GetBytes(item.value);
|
if (bytevalue.Count() <= item.length)
|
{
|
for (int i = 0; i < bytevalue.Count(); i++)
|
{
|
|
bytes[item.addr + item.length - bytevalue.Count() + i] = bytevalue[i];
|
}
|
}
|
break;
|
}
|
}
|
|
}
|
return bytes;
|
}
|
|
public static Dictionary<string, List<S7Model>> Dictvalues = new Dictionary<string, List<S7Model>>();
|
|
/// <summary>
|
/// s7读取方法
|
/// </summary>
|
public static void Read()
|
{
|
var plcInfo = Settings.linePlcInfo.Where(a => (a.deviceNo == "1") && a.enable == 1).FirstOrDefault();
|
var bytes = new byte[0];
|
//获取配置文件读取的地址
|
List<S7Model> readmodels = Settings.plcValue.Find(a => a.address == plcInfo.address).read;
|
if (readmodels.Count > 0)
|
{
|
//获取读取长度
|
|
var model = readmodels.OrderByDescending(a => a.addr).First();
|
var readlen = model.addr + model.length;
|
PLCRead(plcInfo, 100, 0, readlen, out bytes);
|
|
//将配置文件配置的偏移量转换存放到字典里
|
// List<S7Value> s7Values = new List<S7Value>();
|
foreach (var item in readmodels)
|
{
|
//解析数据
|
string value = "";
|
switch (item.type)
|
{
|
case "Int":
|
value = GetInt(bytes, item.addr, VarType.Int).ToString();
|
break;
|
case "DInt":
|
value = GetInt(bytes, item.addr, VarType.DInt).ToString();
|
break;
|
case "Real":
|
value = GetInt(bytes, item.addr, VarType.Real).ToString();
|
break;
|
case "String":
|
value = GetString(bytes, item.addr, VarType.String, item.length).ToString();
|
break;
|
//case "Byte":
|
// value = S7Help.GetInt(bytes, item.addr, VarType.Byte,item.length).ToString();
|
// break;
|
}
|
item.value = value;
|
// s7Values.Add(new S7Value { addr = item.addr, type = item.type, value = value });
|
}
|
//数据存入字典
|
if (Dictvalues.Keys.Contains(plcInfo.address))
|
{
|
Dictvalues[plcInfo.address] = readmodels;
|
}
|
else
|
{
|
Dictvalues.Add(plcInfo.address, readmodels);
|
|
}
|
}
|
|
}
|
/// <summary>
|
/// S7写入byte数组
|
/// </summary>
|
/// <param name="plcInfo">配置文件</param>
|
/// <param name="db">DB块</param>
|
/// <param name="bytes"></param>
|
/// <returns></returns>
|
public static bool PLCWrite(Settings.LinePlcInfo plcInfo, int db, byte[] bytes)
|
{
|
bool result = false;
|
var plc = new Plc(CpuType.S71500, plcInfo.address, 0, 1);
|
Link(plc);
|
|
try
|
{
|
result = plc.WriteBytes(DataType.DataBlock, Convert.ToInt16(db), Convert.ToInt16(plcInfo.writeAddr), bytes) == ErrorCode.NoError;
|
return result;
|
}
|
catch (Exception ex)
|
{
|
|
throw ex;
|
}
|
}
|
|
/// <summary>
|
/// S7读取byte数组
|
/// </summary>
|
/// <param name="plcInfo">配置文件</param>
|
/// <param name="db">DB块</param>
|
/// <param name="addr">读取起始偏移量</param>
|
/// <param name="bytes">输出byte</param>
|
/// <returns></returns>
|
public static bool PLCRead(Settings.LinePlcInfo plcInfo, int db, int addr, int len, out byte[] bytes)
|
{
|
|
bool result = false;
|
var plc = new Plc(CpuType.S71500, plcInfo.address, 0, 1);
|
Link(plc);
|
|
string res = string.Empty;
|
bytes = new byte[len];
|
try
|
{
|
if (plcInfo != null)
|
{
|
|
bytes = plc.ReadBytes(DataType.DataBlock, Convert.ToInt16(db), Convert.ToInt16(plcInfo.readAddr + addr), len);
|
var r1 = "new byte[] {" + string.Join(",", bytes) + " }";
|
res = $" B{db} A{addr} L{len} A{r1}";
|
|
}
|
else
|
{
|
res = " 没设备。";
|
}
|
}
|
catch (Exception ex)
|
{
|
res = "Err" + ex.Message + ex.StackTrace;
|
}
|
|
// result = plc.WriteBytes(DataType.DataBlock, Convert.ToInt16(1000), Convert.ToInt16(linePlcInfo.writeAddr + 6), bytes) == ErrorCode.NoError;
|
return result;
|
}
|
|
/// <summary>
|
/// 解析堆垛机byte数组int类型
|
/// </summary>
|
/// <param name="adr">偏移量</param>
|
/// <param name="varType">类型</param>
|
/// <param name="action">解析失败调取方法单个读取</param>
|
/// <returns></returns>
|
public static int GetInt(byte[] bytes, int adr, VarType varType, Func<int> action = null)
|
{
|
var b = -1;
|
byte[] by = new byte[0];
|
|
by = bytes;
|
|
if (by.Count() > 0)
|
{
|
b = GetInt(adr, varType, by);
|
}
|
if (b == -1 && action != null)
|
{
|
b = action();
|
}
|
|
|
return b;
|
}
|
|
public static int GetInt(int adr, VarType varType, byte[] d, int length = 1)
|
{
|
//计算类型长度,string类型为可变长度用配置文件定义的长度
|
var vlen = VarTypeToByteLength(varType);
|
if (varType == VarType.String)
|
{
|
vlen = length;
|
}
|
|
if (adr + vlen > d.Length)
|
{
|
return -1;
|
}
|
|
var adrbyte = d.Skip(adr).Take(vlen).ToArray();
|
var obj = ParseBytes(varType, adrbyte, 1, 0);
|
return Convert.ToInt32(obj);
|
}
|
/// <summary>
|
/// 解析string类型
|
/// </summary>
|
/// <param name="bytes"></param>
|
/// <param name="adr"></param>
|
/// <param name="varType"></param>
|
/// <param name="count"></param>
|
/// <param name="action"></param>
|
/// <returns></returns>
|
public static string GetString(byte[] bytes, int adr, VarType varType, int count, Func<int> action = null)
|
{
|
string b = null;
|
byte[] by = new byte[0];
|
|
by = bytes;
|
|
if (by.Count() > 0)
|
{
|
b = GetString(adr, varType, by, count);
|
}
|
if (string.IsNullOrEmpty(b) && action != null)
|
{
|
b = action().ToString();
|
}
|
|
|
return b;
|
}
|
public static string GetString(int adr, VarType varType, byte[] d, int length = 1)
|
{
|
//计算类型长度,string类型为可变长度用配置文件定义的长度
|
var vlen = VarTypeToByteLength(varType);
|
if (varType == VarType.String)
|
{
|
vlen = length;
|
}
|
|
if (adr + vlen > d.Length)
|
{
|
return "";
|
}
|
|
var adrbyte = d.Skip(adr).Take(vlen).ToArray();
|
adrbyte = adrbyte.Skip(adrbyte.ToList().FindIndex(x => x > 0)).ToArray();
|
var obj = ParseBytes(varType, adrbyte, 1, 0);
|
return obj.ToString();
|
}
|
|
private static int VarTypeToByteLength(VarType varType, int varCount = 1)
|
{
|
switch (varType)
|
{
|
case VarType.Bit:
|
return varCount;
|
case VarType.Byte:
|
if (varCount >= 1)
|
{
|
return varCount;
|
}
|
|
return 1;
|
case VarType.String:
|
return varCount;
|
case VarType.StringEx:
|
return varCount + 2;
|
case VarType.Word:
|
case VarType.Int:
|
case VarType.Timer:
|
case VarType.Counter:
|
return varCount * 2;
|
case VarType.DWord:
|
case VarType.DInt:
|
case VarType.Real:
|
return varCount * 4;
|
default:
|
return 0;
|
}
|
}
|
private static object ParseBytes(VarType varType, byte[] bytes, int varCount, byte bitAdr = 0)
|
{
|
if (bytes == null)
|
{
|
return null;
|
}
|
|
switch (varType)
|
{
|
case VarType.Byte:
|
if (varCount == 1)
|
{
|
return bytes[0];
|
}
|
|
return bytes;
|
case VarType.Word:
|
if (varCount == 1)
|
{
|
return Word.FromByteArray(bytes);
|
}
|
|
return Word.ToArray(bytes);
|
case VarType.Int:
|
if (varCount == 1)
|
{
|
return Int.FromByteArray(bytes);
|
}
|
|
return Int.ToArray(bytes);
|
case VarType.DWord:
|
if (varCount == 1)
|
{
|
return DWord.FromByteArray(bytes);
|
}
|
|
return DWord.ToArray(bytes);
|
case VarType.DInt:
|
if (varCount == 1)
|
{
|
return DInt.FromByteArray(bytes);
|
}
|
|
return DInt.ToArray(bytes);
|
case VarType.Real:
|
if (varCount == 1)
|
{
|
return S7.Net.Types.Double.FromByteArray(bytes);
|
}
|
|
return S7.Net.Types.Double.ToArray(bytes);
|
case VarType.String:
|
return S7.Net.Types.String.FromByteArray(bytes);
|
case VarType.StringEx:
|
return StringEx.FromByteArray(bytes);
|
case VarType.Timer:
|
if (varCount == 1)
|
{
|
return S7.Net.Types.Timer.FromByteArray(bytes);
|
}
|
|
return S7.Net.Types.Timer.ToArray(bytes);
|
case VarType.Counter:
|
if (varCount == 1)
|
{
|
return Counter.FromByteArray(bytes);
|
}
|
|
return Counter.ToArray(bytes);
|
case VarType.Bit:
|
if (varCount == 1)
|
{
|
if (bitAdr > 7)
|
{
|
return null;
|
}
|
|
return Bit.FromByte(bytes[0], bitAdr);
|
}
|
|
return Bit.ToBitArray(bytes);
|
default:
|
return null;
|
}
|
}
|
#endregion
|
|
#region 用于模拟测试
|
/// <summary>
|
/// short类型,一个占2个byte
|
/// </summary>
|
public class DBWModel
|
{
|
public int db { get; set; }
|
public int byteAddr { get; set; }
|
/// <summary>
|
/// int类型需要用逗号分开,string不需要
|
/// </summary>
|
public string value { get; set; }
|
}
|
/// <summary>
|
/// 字符串类型,一个占1个byte
|
/// </summary>
|
public class DBBModel
|
{
|
public int db { get; set; }
|
public int byteAddr { get; set; }
|
public string value { get; set; }
|
}
|
public class DBXModel
|
{
|
public int db { get; set; }
|
public int byteAddr { get; set; }
|
public int bitAddr { get; set; }
|
/// <summary>
|
/// 1:true 0:false
|
/// </summary>
|
public int value { get; set; }
|
}
|
public static void s7SetInt(DBWModel model)
|
{
|
var data = model.value.Replace(",", "");
|
var s7Key = $"int_{model.db}_{model.byteAddr}_{data.Length}";
|
if (s7TestData.ContainsKey(s7Key))
|
{
|
s7TestData[s7Key] = model.value;
|
}
|
else
|
{
|
s7TestData.Add(s7Key, model.value);
|
}
|
}
|
|
internal static void s7SetBit(DBXModel model)
|
{
|
var s7Key = $"bit_{model.db}_{model.byteAddr}_{model.bitAddr}";
|
var value = model.value == 1 ? "1" : "0";
|
if (s7TestData.ContainsKey(s7Key))
|
{
|
s7TestData[s7Key] = value;
|
}
|
else
|
{
|
s7TestData.Add(s7Key, value);
|
}
|
}
|
|
internal static void s7SetStr(DBBModel model)
|
{
|
var s7Key = $"str_{model.db}_{model.byteAddr}_{model.value.Length}";
|
if (s7TestData.ContainsKey(s7Key))
|
{
|
s7TestData[s7Key] = model.value;
|
}
|
else
|
{
|
s7TestData.Add(s7Key, model.value);
|
}
|
}
|
}
|
#endregion
|
}
|