using System.Reflection; using System.Text; using HslCommunication; using HslCommunication.Profinet.Siemens; using PlcTool.Siemens.Entity; using PlcTool.Siemens.PLCAttribute; namespace PlcTool.Siemens; public class SiemensS7 { /* * 作者:菻蔃 * * 版本时间:2023年4月15日 * */ private const string tip = "致其他开发者,此组件为收费组件,若您有意使用,请自行联系作者购买授权,擅自使用本公司激活码将承担责任"; /// /// 存储PLC连接 /// readonly List siemensS7Connections = []; /// /// 存储DB块具体在哪个PLC中 /// readonly List plcDBNames = []; /// /// 操作PLC的类,传入PLC类型,地址,和组件激活码。 /// /// /// public SiemensS7(string? authorizationCode = null) { if (string.IsNullOrEmpty(authorizationCode)) { /*致本公司开发者,此为 10.6.1 版本激活码,若激活失败,尝试使用此版本*/ /*致其他开发者,此组件为收费组件,若您有意使用,请自行联系作者购买授权,擅自使用本公司激活码将承担责任*/ authorizationCode = "f562cc4c-4772-4b32-bdcd-f3e122c534e3"; } bool isAuthorization = Authorization.SetAuthorizationCode(authorizationCode); if (!isAuthorization) { /*这里抛出异常,一定记得提醒开发者注册*/ throw new Exception("组件未激活,上述激活码可能会因为版本更新而失效,请联系公司获取最新激活码,测试时可注释此行,未激活将只能使用24小时,切记上线前一定记得激活!!!"); } } /// /// 添加PLC信息 /// /// /// /// public SiemensS7 SetPlcs(List plcs) where T : class { if (plcs == null || plcs.Count == 0) { throw new Exception("传入数据为空。"); } Type type = typeof(T); var properties = type.GetProperties(); /*定义两个存储属性的变量*/ PropertyInfo? plcIdProperty = null; PropertyInfo? plcIPProperty = null; PropertyInfo? plcKindProperty = null; PropertyInfo? rackProperty = null; PropertyInfo? soltindProperty = null; /*轮询查找到的属性给变量赋值*/ foreach (var property in properties) { var plcIdattributes = property.GetCustomAttributes(typeof(PlcIdAttribute), false); if (plcIdattributes.Length != 0) { plcIdProperty = property; } var plcIPattributes = property.GetCustomAttributes(typeof(PlcIPAttribute), false); if (plcIPattributes.Length != 0) { plcIPProperty = property; } var plcKindattributes = property.GetCustomAttributes(typeof(PlcKindAttribute), false); if (plcKindattributes.Length != 0) { plcKindProperty = property; } var rackattributes = property.GetCustomAttributes(typeof(RackAttribute), false); if (rackattributes.Length != 0) { rackProperty = property; } var soltattributes = property.GetCustomAttributes(typeof(SlotAttribute), false); if (soltattributes.Length != 0) { soltindProperty = property; } } /*判断是否有对应的特性*/ if (plcIdProperty == null || plcIPProperty == null || plcKindProperty == null) { throw new Exception("未正确识别类中的属性特性,需要同时添加 PlcIdAttribution、PlcIPAttribute 和 PlcKindAttribute 三个特性。"); } /*添加对应的值*/ foreach (T plc in plcs) { var plckind = plcKindProperty.GetValue(plc)!.ToString().ToPlcKind(); var plcip = plcIPProperty.GetValue(plc)!.ToString(); SiemensS7Connection siemensS7Connection = new() { PlcId = Convert.ToInt32(plcIdProperty.GetValue(plc)), SiemensS7 = new SiemensS7Net(plckind, plcip) }; siemensS7Connection.SiemensS7.Rack = Convert.ToByte(rackProperty!.GetValue(plc)); siemensS7Connection.SiemensS7.Slot = Convert.ToByte(soltindProperty!.GetValue(plc)); siemensS7Connections.Add(siemensS7Connection); } return this; } /// /// 添加PLC的Db地址信息 /// /// /// /// public SiemensS7 SetPlcDB(List? dbNames) where T : class { if (dbNames == default || dbNames.Count == 0) { throw new Exception("传入的数据为空"); } Type type = typeof(T); var properties = type.GetProperties(); /*定义两个存储属性的变量*/ PropertyInfo? plcIdProperty = null; PropertyInfo? dbNameProperty = null; PropertyInfo? dbAddressProperty = null; /*轮询查找到的属性给变量赋值*/ foreach (var property in properties) { var plcIdattributes = property.GetCustomAttributes(typeof(PlcIdAttribute), false); if (plcIdattributes.Length != 0) { plcIdProperty = property; } var dbNameattributes = property.GetCustomAttributes(typeof(PlcDBNameAttribute), false); if (dbNameattributes.Length != 0) { dbNameProperty = property; } var dbAddressattributes = property.GetCustomAttributes(typeof(PlcDBAddressAttribute), false); if (dbAddressattributes.Length != 0) { dbAddressProperty = property; } } /*判断是否有对应的特性*/ if (plcIdProperty == null || dbNameProperty == null || dbAddressProperty == null) { throw new Exception("未正确识别类中的属性特性,需要同时添加 PlcIdAttribute、PlcDBNameAttribute、PlcDBAddressAttribute 三个特性。"); } /*添加对应的值*/ foreach (T dbName in dbNames) { PlcDBName plcDBName = new() { PlcId = Convert.ToUInt32(plcIdProperty.GetValue(dbName)), DBName = dbNameProperty.GetValue(dbName)!.ToString(), }; var dbaddress = dbAddressProperty.GetValue(dbName); plcDBName.DBAddress = dbaddress == null ? "" : dbaddress.ToString(); plcDBNames.Add(plcDBName); } return this; } /// /// 连接PLC /// /// /// 请使用异步重载方法 /// public SemS7Result ConnectPlcs() { if (siemensS7Connections.Count == 0) { return new SemS7Result() { Success = false, ErrCode = 999, Message = "没有设置PLC连接,请先调用 SetPlcs 方法。" }; } /*连接PLC*/ foreach (SiemensS7Connection siemensS7Connection in siemensS7Connections) { if (siemensS7Connection.SiemensS7 == default) { continue; } siemensS7Connection.SiemensS7.ConnectTimeOut = 2000; var connectResult = siemensS7Connection.SiemensS7.ConnectServer(); if (!connectResult.IsSuccess) { // 只要有一个连接失败,直接返回错误信息 connectResult.Message = $"PLC:{siemensS7Connection.SiemensS7.IpAddress} 连接失败,异常信息:{connectResult.Message}"; return connectResult.ToSemS7Result(); } } return new SemS7Result() { Success = true, ErrCode = 0, Message = "所有PLC连接成功" }; } #region 根据名称读写PLC数据 /// /// 根据名称写入PLC值,目前不支持 string /// 写入前请提前转换好对应的数据格式 /// /// /// /// public (SemS7Result result, byte[]? bytes) WritePlcWhithName(string plcDBName, params object[] values) { if (values.Length == 0) { return (new SemS7Result() { Success = false, ErrCode = 997, Message = $"写入失败,传入的值为空。" }, default); } /*根据名称获取地址*/ (SiemensS7Net? siemensS7, string? dbAddress, string? errText) = GetSiemensS7(plcDBName); if (siemensS7 == null) { return (new SemS7Result() { Success = false, ErrCode = 999, Message = errText }, default); } (Exception? Ex, byte[]? bytes) getByte = GetPlcBytes(values); if (getByte.Ex != null) { return (new SemS7Result() { Success = false, ErrCode = 996, Message = $"{getByte.Ex.Message}" }, default); } byte[]? bytes = getByte.bytes;//获取转化成功的数组 OperateResult operateResult = siemensS7.Write(dbAddress, bytes); return (operateResult.ToSemS7Result(), bytes); } /// /// 写入bool值 /// /// /// /// public SemS7Result WriteBoolWhithName(string plcDBName, params bool[] values) { if (values.Length == 0) { return new SemS7Result() { Success = false, ErrCode = 997, Message = $"写入失败,传入的值为空。" }; } /*根据名称获取地址*/ (SiemensS7Net? siemensS7, string? dbAddress, string? errText) = GetSiemensS7(plcDBName); if (siemensS7 == null) { return new SemS7Result() { Success = false, ErrCode = 999, Message = errText }; } OperateResult operateResult = siemensS7.Write(dbAddress, values); return operateResult.ToSemS7Result(); } /// /// 根据地址写入值 /// /// /// /// /// /// public SemS7Result WritePlcWhithAddress(int plcNo, string plcDBAddress, params object[] values) { if (values.Length == 0) { return new SemS7Result() { Success = false, ErrCode = 997, Message = $"写入失败,传入的值为空。" }; } /*根据名称获取地址*/ SiemensS7Net? siemensS7 = GetSiemensS7(plcNo); if (siemensS7 == default) { return new SemS7Result() { Success = false, ErrCode = 999, Message = "找不到该PLC连接" }; } (Exception? Ex, byte[]? bytes) getByte = GetPlcBytes(values); if (getByte.Ex != null) { return new SemS7Result() { Success = false, ErrCode = 996, Message = $"{getByte.Ex.Message}" }; } byte[]? bytes = getByte.bytes;//获取转化成功的数组 OperateResult operateResult = siemensS7.Write(plcDBAddress, bytes); return operateResult.ToSemS7Result(); } /// /// 根据名称写入PLC值,写入string /// 写入前请提前转换好对应的数据格式 /// /// /// /// 自定的编码信息,一般System.Text.Encoding.ASCII即可,中文需要 Encoding.GetEncoding("gb2312") /// public SemS7Result WriteStringWhithName(string plcDBName, string value, Encoding encoding) { (SiemensS7Net? siemensS7, string? dbAddress, string? errText) = GetSiemensS7(plcDBName); if (siemensS7 == null) { return new SemS7Result() { Success = false, ErrCode = 999, Message = errText }; } return siemensS7.Write(dbAddress, value, encoding).ToSemS7Result(); } /// /// 读取一个Byte数组 /// /// /// /// public SemS7Result ReadByteWithName(string plcDBName, ushort length) { (SiemensS7Net? siemensS7, string? dbAddress, string? errText) = GetSiemensS7(plcDBName); if (siemensS7 == null) { return new SemS7Result() { Success = false, ErrCode = 999, Message = errText }; } return siemensS7.Read(dbAddress, length).ToSemS7Result(); } /// /// 读取一个Byte /// /// /// public SemS7Result ReadByteWithName(string plcDBName) { (SiemensS7Net? siemensS7, string? dbAddress, string? errText) = GetSiemensS7(plcDBName); if (siemensS7 == null) { return new SemS7Result() { Success = false, ErrCode = 999, Message = errText }; } SemS7Result actionResult = ReadByteWithName(dbAddress!, 1); if (!actionResult.Success) { return new SemS7Result() { Success = false, ErrCode = actionResult.ErrCode, Message = actionResult.Message }; } var values = actionResult.Value; if (values == default || values.Length == 0) { return new SemS7Result() { Success = false, ErrCode = actionResult.ErrCode, Message = actionResult.Message }; } return new SemS7Result() { Success = true, ErrCode = actionResult.ErrCode, Message = actionResult.Message, Value = values[0] }; } /// /// 根据名称读取一定数量的Int16值 /// 对应PLC数据类型为 W,int, /// /// /// /// public SemS7Result ReadInt16WithName(string plcDBName, ushort length) { (SiemensS7Net? siemensS7, string? dbAddress, string? errText) = GetSiemensS7(plcDBName); if (siemensS7 == null) { return new SemS7Result() { Success = false, ErrCode = 999, Message = errText }; } return siemensS7.ReadInt16(dbAddress, length).ToSemS7Result(); } /// /// 根据名称读取一个Int16值 /// 对应PLC数据类型为 W,int, /// /// /// public SemS7Result ReadInt16WithName(string plcDBName) { (SiemensS7Net? siemensS7, string? dbAddress, string? errText) = GetSiemensS7(plcDBName); if (siemensS7 == null) { return new SemS7Result() { Success = false, ErrCode = 999, Message = errText }; } return siemensS7.ReadInt16(dbAddress).ToSemS7Result(); } /// /// 根据名称读取一定数量的Int32值 /// 对应PLC数据类型为 DW,Dint, /// /// /// /// public SemS7Result ReadInt32WithName(string plcDBName, ushort length) { (SiemensS7Net? siemensS7, string? dbAddress, string? errText) = GetSiemensS7(plcDBName); if (siemensS7 == null) { return new SemS7Result() { Success = false, ErrCode = 999, Message = errText }; } return siemensS7.ReadInt32(dbAddress, length).ToSemS7Result(); } /// /// 根据名称读取一个Int32值 /// 对应PLC数据类型为 DW,Dint, /// /// /// public SemS7Result ReadInt32WithName(string plcDBName) { (SiemensS7Net? siemensS7, string? dbAddress, string? errText) = GetSiemensS7(plcDBName); if (siemensS7 == null) { return new SemS7Result() { Success = false, ErrCode = 999, Message = errText }; } return siemensS7.ReadInt32(dbAddress).ToSemS7Result(); } public SemS7Result ReadUInt32WithName(string plcDBName) { (SiemensS7Net? siemensS7, string? dbAddress, string? errText) = GetSiemensS7(plcDBName); if (siemensS7 == null) { return new SemS7Result() { Success = false, ErrCode = 999, Message = errText }; } return siemensS7.ReadUInt32(dbAddress).ToSemS7Result(); } /// /// 根据名称读取一定数量的bool值 /// 对应PLC数据类型为 X /// /// /// /// /// public SemS7Result ReadBoolWithName(string plcDBName, ushort length) { (SiemensS7Net? siemensS7, string? dbAddress, string? errText) = GetSiemensS7(plcDBName); if (siemensS7 == null) { return new SemS7Result() { Success = false, ErrCode = 999, Message = errText }; } return siemensS7.ReadBool(dbAddress, length).ToSemS7Result(); } /// /// 根据名称读取一个Int32值 /// 对应PLC数据类型为 X /// /// /// public SemS7Result ReadBoolWithName(string plcDBName) { (SiemensS7Net? siemensS7, string? dbAddress, string? errText) = GetSiemensS7(plcDBName); if (siemensS7 == null) { return new SemS7Result() { Success = false, ErrCode = 999, Message = errText }; } return siemensS7.ReadBool(dbAddress).ToSemS7Result(); } /// /// 根据名称读取string值 /// 对应PLC数据类型为 string /// /// /// /// 自定的编码信息,一般System.Text.Encoding.ASCII即可,中文需要 Encoding.GetEncoding("gb2312") /// public SemS7Result ReadStringWithName(string plcDBName, ushort length, Encoding encoding) { (SiemensS7Net? siemensS7, string? dbAddress, string? errText) = GetSiemensS7(plcDBName); if (siemensS7 == null) { return new SemS7Result() { Success = false, ErrCode = 999, Message = errText }; } return siemensS7.ReadString(dbAddress, length, encoding).ToSemS7Result(); } /// /// 根据DB名称查找其对应的连接 /// /// /// private (SiemensS7Net? siemensS7, string? dbAddress, string? errText) GetSiemensS7(string dbName) { if (plcDBNames.Count == 0 || siemensS7Connections.Count == 0) { return (null, null, "未设置 plc 或者 未设置 DB地址,请调用 SetPlcs 方法设置 plc 或者 调用 SetPlcDB 方法设置 DB地址"); } /*找出该地址对应的ID*/ PlcDBName? plcDBName = plcDBNames.Find(f => f.DBName == dbName); if (plcDBName == default) { return (null, null, "该DB地址不存在,请核实wcs数据录入是否正确"); } /*找出该ID对应的连接*/ SiemensS7Connection? siemensS7Connection = siemensS7Connections.Find(f => f.PlcId == plcDBName.PlcId); if (siemensS7Connection == default) { return (null, null, "该PLC连接不存在,请核实wcs数据录入是否正确"); } return (siemensS7Connection.SiemensS7, plcDBName.DBAddress, null); } /// /// 根据plcNo 返回连接信息 /// /// /// private SiemensS7Net? GetSiemensS7(int plcNo) { /*找出该ID对应的连接*/ SiemensS7Connection? siemensS7Connection = siemensS7Connections.Find(f => f.PlcId == plcNo); if (siemensS7Connection == default) { return default; } return siemensS7Connection.SiemensS7; } #endregion //NetworkDoubleBase dataTransfrom = new NetworkDoubleBase(); private readonly SiemensS7Net dataTransfrom = new(SiemensPLCS.S1500); #region 将 byte 数组转化为值 /// /// 将数组转化为值 /// /// /// /// /// public object? Trans(byte[] bytes, int index) where T : struct { Type type = typeof(T); if (type == typeof(short)) { return dataTransfrom.ByteTransform.TransInt16(bytes, index); } else if (type == typeof(int)) { return dataTransfrom.ByteTransform.TransInt32(bytes, index); } else if (type == typeof(bool)) { return dataTransfrom.ByteTransform.TransBool(bytes, index); } else if (type == typeof(double)) { return dataTransfrom.ByteTransform.TransDouble(bytes, index); } else if (type == typeof(float)) { return dataTransfrom.ByteTransform.TransSingle(bytes, index); } else if (type == typeof(uint)) { return dataTransfrom.ByteTransform.TransUInt32(bytes, index); } else if (type == typeof(ushort)) { return dataTransfrom.ByteTransform.TransUInt16(bytes, index); } else { return null; // 不支持的数据类型 } } #endregion #region 将传入的值转化成 byte 数组 /// /// 将传入的值转化成byte数组 /// /// /// public (Exception? Ex, byte[]? bytes) GetPlcBytes(object[] values) { List bytes = []; foreach (object value in values) { Type type = value.GetType(); if (type == typeof(short)) { short va = Convert.ToInt16(value); byte[] data = dataTransfrom.ByteTransform.TransByte(va); bytes.AddRange(data); } else if (type == typeof(int)) { int va = Convert.ToInt32(value); byte[] data = dataTransfrom.ByteTransform.TransByte(va); bytes.AddRange(data); } else if (type == typeof(bool)) { bool va = Convert.ToBoolean(value); byte[] data = dataTransfrom.ByteTransform.TransByte(va); bytes.AddRange(data); } else if (type == typeof(double)) { double va = Convert.ToDouble(value); byte[] data = dataTransfrom.ByteTransform.TransByte(va); bytes.AddRange(data); } else if (type == typeof(float)) { float va = Convert.ToSingle(value); byte[] data = dataTransfrom.ByteTransform.TransByte(va); bytes.AddRange(data); } else if (type == typeof(uint)) { uint va = Convert.ToUInt32(value); byte[] data = dataTransfrom.ByteTransform.TransByte(va); bytes.AddRange(data); } else if (type == typeof(ushort)) { ushort va = Convert.ToUInt16(value); byte[] data = dataTransfrom.ByteTransform.TransByte(va); bytes.AddRange(data); } else { return (new Exception($"传入的数据中有不支持的数据类型 {type.Name} "), null); } } return (null, [.. bytes]); } #endregion #region 根据名称和偏移量返回地址 /// /// 根据名称和偏移量返回地址 /// /// /// /// public string GetAddressWithNameAndBit(string dbName, int moveBit) { /* * 1、判断有几个点 * 有一个点的不是bool值 * 有两个点的是bool值 * 必须写全,如DB100等价于DB100.0,但不允许写DB100,因为容易识别错误 */ if (plcDBNames.Count == 0) { return string.Empty; // 没有注册DB地址, } /*找出该地址对应的ID*/ PlcDBName? plcDBName = plcDBNames.Find(f => f.DBName == dbName); if (plcDBName == default) { return string.Empty; // 找不到DB地址 } string startDBAddress = plcDBName.DBAddress ?? string.Empty; string[] dbInfos = startDBAddress.Split('.'); if (dbInfos.Length == 2) { // 只有一个点,判断为非Bool值 int startPoint = Convert.ToInt32(dbInfos[1]); string newDBAddress = $"{dbInfos[0]}.{startPoint + moveBit}"; return newDBAddress; } else if (dbInfos.Length == 3) { // 有两个点,判断为bool值 int bigAddress = Convert.ToInt32(dbInfos[1]); int smallAddress = Convert.ToInt32(dbInfos[2]); while (moveBit > 0) { smallAddress++; if (smallAddress >= 8) { bigAddress++; smallAddress = 0; } moveBit--; } return $"{dbInfos[0]}.{bigAddress}.{smallAddress}"; } return string.Empty; } #endregion }