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;
/// 获取默认地址对
///