diff --git a/NewLife.Siemens/Drivers/SiemensS7Driver.cs b/NewLife.Siemens/Drivers/SiemensS7Driver.cs index 1e8c36a..7190260 100644 --- a/NewLife.Siemens/Drivers/SiemensS7Driver.cs +++ b/NewLife.Siemens/Drivers/SiemensS7Driver.cs @@ -1,11 +1,8 @@ using System.ComponentModel; -using System.Runtime.CompilerServices; using NewLife.IoT; using NewLife.IoT.Drivers; using NewLife.IoT.ThingModels; -using NewLife.Log; using NewLife.Omron.Drivers; -using NewLife.Serialization; using NewLife.Siemens.Models; using NewLife.Siemens.Protocols; @@ -28,7 +25,12 @@ public class SiemensS7Driver : DriverBase /// 销毁时,关闭连接 /// /// - protected override void Dispose(Boolean disposing) => base.Dispose(disposing); + protected override void Dispose(Boolean disposing) + { + base.Dispose(disposing); + + _plcConn.TryDispose(); + } #endregion #region 方法 @@ -39,6 +41,7 @@ public class SiemensS7Driver : DriverBase protected override IDriverParameter OnCreateParameter() => new SiemensParameter { Address = "127.0.0.1:102", + CpuType = CpuType.S7200Smart, Rack = 0, Slot = 0, }; @@ -57,7 +60,7 @@ public override INode Open(IDevice device, IDriverParameter parameter) if (address.IsNullOrEmpty()) throw new ArgumentException("参数中未指定地址address"); //var p = address.IndexOfAny(new[] { ':', '.' }); // p为3,最后截取不到正确ip - var p = address.IndexOfAny(new[] { ':' }); + var p = address.IndexOfAny([':']); if (p < 0) throw new ArgumentException($"参数中地址address格式错误:{address}"); var cpuType = pm.CpuType; @@ -130,6 +133,7 @@ public override IDictionary Read(INode node, IPoint[] points) if (points == null || points.Length == 0) return dic; + var spec = node.Device?.Specification; foreach (var point in points) { var addr = GetAddress(point); @@ -150,7 +154,12 @@ public override IDictionary Read(INode node, IPoint[] points) var data = _plcConn.ReadBytes(dataType, db, startByteAdr, (UInt16)point.Length); - dic[point.Name] = data; + // 借助物模型转换数据类型 + var v = spec?.DecodeByThingModel(data, point); + if (v != null) + dic[point.Name] = v; + else + dic[point.Name] = data; } return dic; @@ -181,20 +190,22 @@ public virtual String GetAddress(IPoint point) /// 数值 public override Object Write(INode node, IPoint point, Object value) { - using var span = Tracer?.NewSpan("write_value", new { point, value }); - var addr = GetAddress(point); if (addr.IsNullOrWhiteSpace()) return null; - span.AppendTag($"addr:{addr}"); + // 借助物模型转换数据类型 + var spec = node.Device?.Specification; + if (spec != null && value is not Byte[]) + { + // 普通数值转为字节数组 + value = spec.EncodeByThingModel(value, point); + } // 操作字节数组,不用设置bitNumber,但是解析需要带上 if (addr.IndexOf('.') == -1) addr += ".0"; var plc_adr = new PLCAddress(addr); - span.AppendTag($"plc_addr:{plc_adr}"); - var dataType = plc_adr.DataType; var db = plc_adr.DbNumber; var startByteAdr = plc_adr.StartByte; @@ -222,8 +233,6 @@ public override Object Write(INode node, IPoint point, Object value) }; } - span.AppendTag($"转换完成bytes:{bytes.ToHex()}"); - _plcConn.WriteBytes(dataType, db, startByteAdr, bytes); return null; diff --git a/NewLife.Siemens/Protocols/COTP.cs b/NewLife.Siemens/Protocols/COTP.cs index 8f15bd1..a43b114 100644 --- a/NewLife.Siemens/Protocols/COTP.cs +++ b/NewLife.Siemens/Protocols/COTP.cs @@ -9,6 +9,7 @@ public enum PduType : Byte Data = 0xf0, ConnectionConfirmed = 0xd0 } + /// /// Describes a COTP TPDU (Transport protocol data unit) /// @@ -48,28 +49,17 @@ public TPDU(TPKT tPKT) /// See: https://tools.ietf.org/html/rfc905 /// /// The socket to read from + /// /// COTP DPDU instance public static async Task ReadAsync(Stream stream, CancellationToken cancellationToken) { var tpkt = await TPKT.ReadAsync(stream, cancellationToken).ConfigureAwait(false); - if (tpkt.Length == 0) - { - throw new TPDUInvalidException("No protocol data received"); - } - return new TPDU(tpkt); - } + if (tpkt.Length == 0) throw new TPDUInvalidException("No protocol data received"); - public override String ToString() - { - return String.Format("Length: {0} PDUType: {1} TPDUNumber: {2} Last: {3} Segment Data: {4}", - HeaderLength, - PDUType, - TPDUNumber, - LastDataUnit, - BitConverter.ToString(Data) - ); + return new TPDU(tpkt); } + public override String ToString() => $"Length: {HeaderLength} PDUType: {PDUType} TPDUNumber: {TPDUNumber} Last: {LastDataUnit} Segment Data: {BitConverter.ToString(Data)}"; } /// @@ -82,15 +72,13 @@ public class TSDU /// See: https://tools.ietf.org/html/rfc905 /// /// The stream to read from + /// /// Data in TSDU public static async Task ReadAsync(Stream stream, CancellationToken cancellationToken) { var segment = await TPDU.ReadAsync(stream, cancellationToken).ConfigureAwait(false); - if (segment.LastDataUnit) - { - return segment.Data; - } + if (segment.LastDataUnit) return segment.Data; // More segments are expected, prepare a buffer to store all data var buffer = new Byte[segment.Data.Length]; diff --git a/NewLife.Siemens/Protocols/DataItemAddress.cs b/NewLife.Siemens/Protocols/DataItemAddress.cs index 27012de..c1910dc 100644 --- a/NewLife.Siemens/Protocols/DataItemAddress.cs +++ b/NewLife.Siemens/Protocols/DataItemAddress.cs @@ -5,34 +5,25 @@ namespace NewLife.Siemens.Protocols; /// /// Represents an area of memory in the PLC /// -internal class DataItemAddress +internal class DataItemAddress(DataType dataType, Int32 db, Int32 startByteAddress, Int32 byteLength) { - public DataItemAddress(DataType dataType, Int32 db, Int32 startByteAddress, Int32 byteLength) - { - DataType = dataType; - DB = db; - StartByteAddress = startByteAddress; - ByteLength = byteLength; - } - - /// /// Memory area to read /// - public DataType DataType { get; } + public DataType DataType { get; } = dataType; /// /// Address of memory area to read (example: for DB1 this value is 1, for T45 this value is 45) /// - public Int32 DB { get; } + public Int32 DB { get; } = db; /// /// Address of the first byte to read /// - public Int32 StartByteAddress { get; } + public Int32 StartByteAddress { get; } = startByteAddress; /// /// Length of data to read /// - public Int32 ByteLength { get; } + public Int32 ByteLength { get; } = byteLength; } diff --git a/NewLife.Siemens/Protocols/PLCAddress.cs b/NewLife.Siemens/Protocols/PLCAddress.cs index 87f82aa..ec832b0 100644 --- a/NewLife.Siemens/Protocols/PLCAddress.cs +++ b/NewLife.Siemens/Protocols/PLCAddress.cs @@ -34,7 +34,7 @@ public static void Parse(String input, out DataType dataType, out Int32 dbNumber switch (input[..2]) { case "DB": - var strings = input.Split(new Char[] { '.' }); + var strings = input.Split(['.']); if (strings.Length < 2) throw new InvalidAddressException("To few periods for DB address"); diff --git a/NewLife.Siemens/Protocols/S7PLC.cs b/NewLife.Siemens/Protocols/S7PLC.cs index 4bc1e8d..08c18c0 100644 --- a/NewLife.Siemens/Protocols/S7PLC.cs +++ b/NewLife.Siemens/Protocols/S7PLC.cs @@ -15,7 +15,7 @@ public partial class S7PLC : DisposeBase /// 端口 public Int32 Port { get; set; } = 102; - /// 超时时间。默认5秒 + /// 超时时间。默认5000毫秒 public Int32 Timeout { get; set; } = 5_000; /// TSAP地址 @@ -33,112 +33,10 @@ public partial class S7PLC : DisposeBase /// 最大PDU大小 public Int32 MaxPDUSize { get; private set; } = 240; - private readonly Byte[] plcHead1 = new Byte[22] -{ - 3, - 0, - 0, - 22, - 17, - 224, - 0, - 0, - 0, - 1, - 0, - 192, - 1, - 10, - 193, - 2, - 1, - 2, - 194, - 2, - 1, - 0 -}; - private readonly Byte[] plcHead2 = new Byte[25] - { - 3, - 0, - 0, - 25, - 2, - 240, - 128, - 50, - 1, - 0, - 0, - 4, - 0, - 0, - 8, - 0, - 0, - 240, - 0, - 0, - 1, - 0, - 1, - 1, - 224 - }; - private readonly Byte[] plcHead1_200smart = new Byte[22] - { - 3, - 0, - 0, - 22, - 17, - 224, - 0, - 0, - 0, - 1, - 0, - 193, - 2, - 16, - 0, - 194, - 2, - 3, - 0, - 192, - 1, - 10 - }; - private readonly Byte[] plcHead2_200smart = new Byte[25] -{ - 3, - 0, - 0, - 25, - 2, - 240, - 128, - 50, - 1, - 0, - 0, - 204, - 193, - 0, - 8, - 0, - 0, - 240, - 0, - 0, - 1, - 0, - 1, - 3, - 192 -}; + private readonly Byte[] plcHead1 = [3, 0, 0, 22, 17, 224, 0, 0, 0, 1, 0, 192, 1, 10, 193, 2, 1, 2, 194, 2, 1, 0]; + private readonly Byte[] plcHead2 = [3, 0, 0, 25, 2, 240, 128, 50, 1, 0, 0, 4, 0, 0, 8, 0, 0, 240, 0, 0, 1, 0, 1, 1, 224]; + private readonly Byte[] plcHead1_200smart = [3, 0, 0, 22, 17, 224, 0, 0, 0, 1, 0, 193, 2, 16, 0, 194, 2, 3, 0, 192, 1, 10]; + private readonly Byte[] plcHead2_200smart = [3, 0, 0, 25, 2, 240, 128, 50, 1, 0, 0, 204, 193, 0, 8, 0, 0, 240, 0, 0, 1, 0, 1, 3, 192]; private TcpClient _client; private NetworkStream _stream; #endregion @@ -226,23 +124,30 @@ private Byte[] GetCOTPConnectionRequest(TsapAddress tsap) if (CPU == CpuType.S7200Smart) return plcHead1_200smart; - Byte[] buf = { - 3, 0, 0, 22, //TPKT - 17, //COTP Header Length - 224, //Connect Request - 0, 0, //Destination Reference - 0, 46, //Source Reference - 0, //Flags - 193, //Parameter Code (src-tasp) - 2, //Parameter Length - (Byte)(tsap.Local>>8), (Byte)(tsap.Local&0xFF), //Source TASP - 194, //Parameter Code (dst-tasp) - 2, //Parameter Length - (Byte)(tsap.Remote>>8), (Byte)(tsap.Remote&0xFF), //Destination TASP - 192, //Parameter Code (tpdu-size) - 1, //Parameter Length - 10 //TPDU Size (2^10 = 1024) - }; + Byte[] buf = [ + 3, + 0, + 0, + 22, //TPKT + 17, //COTP Header Length + 224, //Connect Request + 0, + 0, //Destination Reference + 0, + 46, //Source Reference + 0, //Flags + 193, //Parameter Code (src-tasp) + 2, //Parameter Length + (Byte)(tsap.Local >> 8), + (Byte)(tsap.Local & 0xFF), //Source TASP + 194, //Parameter Code (dst-tasp) + 2, //Parameter Length + (Byte)(tsap.Remote >> 8), + (Byte)(tsap.Remote & 0xFF), //Destination TASP + 192, //Parameter Code (tpdu-size) + 1, //Parameter Length + 10 //TPDU Size (2^10 = 1024) + ]; return buf; } @@ -307,13 +212,13 @@ public void Close() private static void BuildHeaderPackage(MemoryStream stream, Int32 amount = 1) { //header size = 19 bytes - stream.WriteByteArray(new Byte[] { 0x03, 0x00 }); + stream.WriteByteArray([0x03, 0x00]); //complete package size stream.WriteByteArray(((Int16)(19 + (12 * amount))).GetBytes(false)); - stream.WriteByteArray(new Byte[] { 0x02, 0xf0, 0x80, 0x32, 0x01, 0x00, 0x00, 0x00, 0x00 }); + stream.WriteByteArray([0x02, 0xf0, 0x80, 0x32, 0x01, 0x00, 0x00, 0x00, 0x00]); //data part size stream.WriteByteArray(ToByteArray((UInt16)(2 + (amount * 12)))); - stream.WriteByteArray(new Byte[] { 0x00, 0x00, 0x04 }); + stream.WriteByteArray([0x00, 0x00, 0x04]); //amount of requests stream.WriteByte((Byte)amount); } @@ -387,7 +292,7 @@ private void ReadBytesWithSingleRequest(DataType dataType, Int32 db, Int32 start private static void BuildReadDataRequestPackage(MemoryStream stream, DataType dataType, Int32 db, Int32 startByteAdr, Int32 count = 1) { //single data req = 12 - stream.WriteByteArray(new Byte[] { 0x12, 0x0a, 0x10 }); + stream.WriteByteArray([0x12, 0x0a, 0x10]); switch (dataType) { case DataType.Timer: @@ -465,18 +370,18 @@ private Byte[] BuildWriteBytesPackage(DataType dataType, Int32 db, Int32 startBy package.WriteByte(0); //complete package size package.WriteByteArray(((Int16)packageSize).GetBytes(false)); - package.WriteByteArray(new Byte[] { 2, 0xf0, 0x80, 0x32, 1, 0, 0 }); + package.WriteByteArray([2, 0xf0, 0x80, 0x32, 1, 0, 0]); package.WriteByteArray(ToByteArray((UInt16)(varCount - 1))); - package.WriteByteArray(new Byte[] { 0, 0x0e }); + package.WriteByteArray([0, 0x0e]); package.WriteByteArray(ToByteArray((UInt16)(varCount + 4))); - package.WriteByteArray(new Byte[] { 0x05, 0x01, 0x12, 0x0a, 0x10, 0x02 }); + package.WriteByteArray([0x05, 0x01, 0x12, 0x0a, 0x10, 0x02]); package.WriteByteArray(ToByteArray((UInt16)varCount)); package.WriteByteArray(ToByteArray((UInt16)(db))); package.WriteByte((Byte)dataType); var overflow = (Int32)(startByteAdr * 8 / 0xffffU); // handles words with address bigger than 8191 package.WriteByte((Byte)overflow); package.WriteByteArray(ToByteArray((UInt16)(startByteAdr * 8))); - package.WriteByteArray(new Byte[] { 0, 4 }); + package.WriteByteArray([0, 4]); package.WriteByteArray(ToByteArray((UInt16)(varCount * 8))); // now join the header and the data @@ -554,9 +459,7 @@ private Byte[] GetS7ConnectionSetup() { if (CPU == CpuType.S7200Smart) return plcHead2_200smart; - return new Byte[] { 3, 0, 0, 25, 2, 240, 128, 50, 1, 0, 0, 255, 255, 0, 8, 0, 0, 240, 0, 0, 3, 0, 3, - 3, 192 // Use 960 PDU size - }; + return [3, 0, 0, 25, 2, 240, 128, 50, 1, 0, 0, 255, 255, 0, 8, 0, 0, 240, 0, 0, 3, 0, 3, 3, 192]; } private async Task NoLockRequestTsduAsync(Stream stream, Byte[] requestData, Int32 offset, Int32 length, diff --git a/NewLife.Siemens/Protocols/TsapAddress.cs b/NewLife.Siemens/Protocols/TsapAddress.cs index b910c12..3eb68d6 100644 --- a/NewLife.Siemens/Protocols/TsapAddress.cs +++ b/NewLife.Siemens/Protocols/TsapAddress.cs @@ -3,24 +3,18 @@ namespace NewLife.Siemens.Protocols; /// TSAP地址对 -public class TsapAddress +/// +/// 实例化 +/// +/// +/// +public class TsapAddress(UInt16 local, UInt16 remote) { /// 本地 - public UInt16 Local { get; set; } + public UInt16 Local { get; set; } = local; /// 远程 - public UInt16 Remote { get; set; } - - /// - /// 实例化 - /// - /// - /// - public TsapAddress(UInt16 local, UInt16 remote) - { - Local = local; - Remote = remote; - } + public UInt16 Remote { get; set; } = remote; /// 获取默认地址对 ///