diff --git a/src/ThingsGateway.Foundation/Protocol/ProtocolBase.cs b/src/ThingsGateway.Foundation/Protocol/ProtocolBase.cs
index 7b7e23e42..a89b6a849 100644
--- a/src/ThingsGateway.Foundation/Protocol/ProtocolBase.cs
+++ b/src/ThingsGateway.Foundation/Protocol/ProtocolBase.cs
@@ -24,14 +24,17 @@ public abstract class ProtocolBase : DisposableObject, IProtocol
public ProtocolBase(IChannel channel)
{
if (channel == null) throw new ArgumentNullException(nameof(channel));
- channel.Collects.Add(this);
- Channel = channel;
- Logger = channel.Logger;
- Channel.Starting += ChannelStarting;
- Channel.Stoped += ChannelStoped;
- Channel.Started += ChannelStarted;
- Channel.ChannelReceived += ChannelReceived;
- Channel.Config.ConfigurePlugins(ConfigurePlugins());
+ lock (channel)
+ {
+ channel.Collects.Add(this);
+ Channel = channel;
+ Logger = channel.Logger;
+ Channel.Starting += ChannelStarting;
+ Channel.Stoped += ChannelStoped;
+ Channel.Started += ChannelStarted;
+ Channel.ChannelReceived += ChannelReceived;
+ Channel.Config.ConfigurePlugins(ConfigurePlugins());
+ }
}
///
diff --git a/src/Version.props b/src/Version.props
index 8ae2a0e40..50c180586 100644
--- a/src/Version.props
+++ b/src/Version.props
@@ -1,6 +1,6 @@
- 6.0.4.38
+ 6.0.4.41
diff --git a/src/adapter/ThingsGateway.Foundation.Demo/S7MasterTest.cs b/src/adapter/ThingsGateway.Foundation.Demo/S7MasterTest.cs
index 99b9ee0c2..18cda9caa 100644
--- a/src/adapter/ThingsGateway.Foundation.Demo/S7MasterTest.cs
+++ b/src/adapter/ThingsGateway.Foundation.Demo/S7MasterTest.cs
@@ -8,6 +8,7 @@
// QQ群:605534569
//------------------------------------------------------------------------------
+using ThingsGateway.Foundation.Json.Extension;
using ThingsGateway.Foundation.SiemensS7;
using TouchSocket.Core;
@@ -35,7 +36,8 @@ internal class S7MasterTest
addresss[1].Length = addresss[0].Data.Length;
addresss[2].Data = new byte[] { 0x01, 0x02, 0x03, 0x04 };
addresss[2].Length = addresss[0].Data.Length;
- var result = await siemensS7Master.S7RequestAsync(addresss, false, false);
+ var result = await siemensS7Master.S7WriteAsync(addresss);
+ Console.WriteLine(result.ToJsonNetString());
S7Variable s7Variable = new S7Variable(siemensS7Master, 200);
diff --git a/src/adapter/ThingsGateway.Foundation.Demo/ThingsGateway.Foundation.Demo.csproj b/src/adapter/ThingsGateway.Foundation.Demo/ThingsGateway.Foundation.Demo.csproj
index 35e5acd08..ecab4cbba 100644
--- a/src/adapter/ThingsGateway.Foundation.Demo/ThingsGateway.Foundation.Demo.csproj
+++ b/src/adapter/ThingsGateway.Foundation.Demo/ThingsGateway.Foundation.Demo.csproj
@@ -12,6 +12,4 @@
-
-
diff --git a/src/adapter/ThingsGateway.Foundation.SiemensS7/S7/Core/S7Send.cs b/src/adapter/ThingsGateway.Foundation.SiemensS7/S7/Core/S7Send.cs
index 629415574..af52d86bc 100644
--- a/src/adapter/ThingsGateway.Foundation.SiemensS7/S7/Core/S7Send.cs
+++ b/src/adapter/ThingsGateway.Foundation.SiemensS7/S7/Core/S7Send.cs
@@ -8,6 +8,8 @@
// QQ群:605534569
//------------------------------------------------------------------------------
+using ThingsGateway.Foundation.Extension.Generic;
+
using TouchSocket.Core;
namespace ThingsGateway.Foundation.SiemensS7;
@@ -46,6 +48,9 @@ public class S7Request
///
public int Length { get; set; }
+
+ public int BitLength { get; set; }
+ public bool IsBit { get; set; }
#endregion Request
}
@@ -56,7 +61,6 @@ internal class S7Send : ISendMessage
{
internal bool Handshake;
internal byte[] HandshakeBytes;
- internal bool IsBit;
internal bool Read;
internal SiemensAddress[] SiemensAddress;
@@ -66,11 +70,10 @@ internal class S7Send : ISendMessage
Handshake = true;
}
- public S7Send(SiemensAddress[] siemensAddress, bool read, bool isBit)
+ public S7Send(SiemensAddress[] siemensAddress, bool read)
{
SiemensAddress = siemensAddress;
Read = read;
- IsBit = isBit;
}
public int MaxLength => 2048;
@@ -110,7 +113,7 @@ internal class S7Send : ISendMessage
valueByteBlock.WriteByte(0x32);//协议id
valueByteBlock.WriteByte(0x01);//请求
valueByteBlock.WriteUInt16(0x00, EndianType.Big);//冗余识别
- valueByteBlock.WriteUInt16(0x01, EndianType.Big);//数据引用
+ valueByteBlock.WriteUInt16((ushort)this.Sign, EndianType.Big);//数据ID标识
valueByteBlock.WriteUInt16(parameterLen, EndianType.Big);//参数长度,item.len*12+2
valueByteBlock.WriteUInt16(0x00, EndianType.Big);//数据长度,data.len+4 ,写入时填写,读取时为0
//par
@@ -157,7 +160,7 @@ internal class S7Send : ISendMessage
valueByteBlock.WriteByte(0x32);//协议id
valueByteBlock.WriteByte(0x01);//请求
valueByteBlock.WriteUInt16(0x00, EndianType.Big);//冗余识别
- valueByteBlock.WriteUInt16(0x01, EndianType.Big);//数据引用
+ valueByteBlock.WriteUInt16((ushort)this.Sign, EndianType.Big);//数据ID标识
valueByteBlock.WriteUInt16(parameterLen, EndianType.Big);//参数长度,item.len*12+2
valueByteBlock.WriteUInt16(0, EndianType.Big);//数据长度,data.len+4 ,写入时填写,读取时为0
@@ -172,7 +175,7 @@ internal class S7Send : ISendMessage
{
var data = address.Data;
byte len = (byte)address.Length;
- bool isBit = (IsBit && len == 1);
+ bool isBit = (address.IsBit && len == 1);
valueByteBlock.WriteByte(0x12);//Var 规范
valueByteBlock.WriteByte(0x0a);//剩余的字节长度
valueByteBlock.WriteByte(0x10);//Syntax ID
@@ -190,14 +193,15 @@ internal class S7Send : ISendMessage
{
var data = address.Data;
byte len = (byte)address.Length;
- bool isBit = (IsBit && len == 1);
+ bool isBit = (address.IsBit && len == 1);
+ data = data.ArrayExpandToLengthEven();
//后面跟的是写入的数据信息
valueByteBlock.WriteByte(0);
- valueByteBlock.WriteByte((byte)(isBit ? 3 : 4));//Bit:3;Byte:4;Counter或者Timer:9
- valueByteBlock.WriteUInt16((ushort)(isBit ? len : len * 8), EndianType.Big);
+ valueByteBlock.WriteByte((byte)(isBit ? address.DataCode == (byte)S7WordLength.Counter ? 9 : 3 : 4));//Bit:3;Byte:4;Counter或者Timer:9
+ valueByteBlock.WriteUInt16((ushort)(isBit ? (byte)address.BitLength : len * 8), EndianType.Big);
valueByteBlock.Write(data);
- dataLen = (ushort)(dataLen +data.Length+4);
+ dataLen = (ushort)(dataLen + data.Length + 4);
}
ushort telegramLen = (ushort)(itemLen * 12 + 19 + dataLen);
valueByteBlock.Position = 2;
diff --git a/src/adapter/ThingsGateway.Foundation.SiemensS7/S7/Core/SiemensAddress.cs b/src/adapter/ThingsGateway.Foundation.SiemensS7/S7/Core/SiemensAddress.cs
index 9df0c2e4d..6d47b5405 100644
--- a/src/adapter/ThingsGateway.Foundation.SiemensS7/S7/Core/SiemensAddress.cs
+++ b/src/adapter/ThingsGateway.Foundation.SiemensS7/S7/Core/SiemensAddress.cs
@@ -31,6 +31,9 @@ public class SiemensAddress : S7Request
this.DbBlock = siemensAddress.DbBlock;
this.Data = siemensAddress.Data;
this.Length = siemensAddress.Length;
+
+ BitLength=siemensAddress.BitLength;
+ IsBit= siemensAddress.IsBit;
}
///
diff --git a/src/adapter/ThingsGateway.Foundation.SiemensS7/S7/SiemensS7Master.cs b/src/adapter/ThingsGateway.Foundation.SiemensS7/S7/SiemensS7Master.cs
index ce7c8ac59..d2e1a5a68 100644
--- a/src/adapter/ThingsGateway.Foundation.SiemensS7/S7/SiemensS7Master.cs
+++ b/src/adapter/ThingsGateway.Foundation.SiemensS7/S7/SiemensS7Master.cs
@@ -9,6 +9,7 @@
//------------------------------------------------------------------------------
using System.Data;
+using System.Net;
using ThingsGateway.Foundation.Extension.String;
@@ -23,8 +24,18 @@ public partial class SiemensS7Master : ProtocolBase
///
public SiemensS7Master(IChannel channel) : base(channel)
{
- RegisterByteLength = 1;
- ThingsGatewayBitConverter = new S7BitConverter(EndianType.Big);
+ lock (channel)
+ {
+ RegisterByteLength = 1;
+ ThingsGatewayBitConverter = new S7BitConverter(EndianType.Big);
+
+ if (channel.Collects.Count(a => a.GetType() == typeof(SiemensS7Master)) > 1)
+ {
+ Channel.Starting -= ChannelStarting;
+ Channel.Stoped -= ChannelStoped;
+ Channel.Started -= ChannelStarted;
+ }
+ }
}
///
@@ -117,9 +128,8 @@ public partial class SiemensS7Master : ProtocolBase
///
/// 此方法并不会智能分组以最大化效率,减少传输次数,因为返回值是byte[],所以一切都按地址数组的顺序执行,最后合并数组
///
- public async ValueTask> S7RequestAsync(SiemensAddress[] sAddresss, bool read, bool isBit, int bitLength = 0, CancellationToken cancellationToken = default)
+ public async ValueTask> S7ReadAsync(SiemensAddress[] sAddresss, CancellationToken cancellationToken = default)
{
- if (read)
{
var byteBlock = new ValueByteBlock(2048);
try
@@ -135,7 +145,7 @@ public partial class SiemensS7Master : ProtocolBase
sAddress.Length = len;
var result = await this.SendThenReturnAsync(
- new S7Send([sAddress], read, isBit), cancellationToken: cancellationToken).ConfigureAwait(false);
+ new S7Send([sAddress], true), cancellationToken: cancellationToken).ConfigureAwait(false);
if (!result.IsSuccess) return result;
byteBlock.Write(result.Content);
@@ -163,14 +173,27 @@ public partial class SiemensS7Master : ProtocolBase
byteBlock.SafeDispose();
}
}
- else
+ }
+ ///
+ /// 此方法并不会智能分组以最大化效率,减少传输次数,因为返回值是byte[],所以一切都按地址数组的顺序执行,最后合并数组
+ ///
+ public async ValueTask> S7WriteAsync(SiemensAddress[] sAddresss, CancellationToken cancellationToken = default)
+ {
+
+
+ var dictOperResult = new Dictionary();
+
+ void SetFailOperResult(OperResult operResult)
+ {
+ foreach (var item in sAddresss)
+ {
+ dictOperResult.TryAdd(item, operResult);
+ }
+ }
+
{
var sAddress = sAddresss[0];
- if (sAddresss.Length > 1 && isBit)
- {
- return new OperResult("Only supports single write");
- }
- else if (sAddresss.Length <= 1 && isBit)
+ if (sAddresss.Length <= 1 && sAddress.IsBit)
{
//读取,再写入
var byteBlock = new ValueByteBlock(2048);
@@ -179,24 +202,36 @@ public partial class SiemensS7Master : ProtocolBase
var addressLen = sAddress.Length == 0 ? 1 : sAddress.Length;
if (addressLen > PduLength)
- return new OperResult("Write length exceeds limit");
+ {
+ SetFailOperResult(new OperResult("Write length exceeds limit"));
+ return dictOperResult;
+ }
var result = await this.SendThenReturnAsync(
- new S7Send([sAddress], true, isBit), cancellationToken: cancellationToken).ConfigureAwait(false);
+ new S7Send([sAddress], true), cancellationToken: cancellationToken).ConfigureAwait(false);
- if (!result.IsSuccess) return result;
- var vaue = sAddress.Data.ByteToBoolArray(bitLength);
- for (int i = sAddress.BitCode; i < vaue.Length + sAddress.BitCode; i++)
+ if (!result.IsSuccess)
{
- result.Content[i / 8] = result.Content[i / 8].SetBit((i % 8), vaue[i - sAddress.BitCode]);
+ SetFailOperResult(result);
+ return dictOperResult;
+ }
+
+ var value = sAddress.Data.ByteToBoolArray(sAddress.BitLength);
+ for (int i = sAddress.BitCode; i < value.Length + sAddress.BitCode; i++)
+ {
+ result.Content[i / 8] = result.Content[i / 8].SetBit((i % 8), value[i - sAddress.BitCode]);
}
sAddress.Data = result.Content;
- return await this.SendThenReturnAsync(
-new S7Send([sAddress], false, isBit), cancellationToken: cancellationToken).ConfigureAwait(false);
+ var wresult = await this.SendThenReturnAsync(
+new S7Send([sAddress], false), cancellationToken: cancellationToken).ConfigureAwait(false);
+ dictOperResult.TryAdd(sAddress, wresult);
+ return dictOperResult;
+
}
catch (Exception ex)
{
- return new OperResult(ex);
+ SetFailOperResult(new OperResult(ex));
+ return dictOperResult;
}
finally
{
@@ -212,7 +247,7 @@ new S7Send([sAddress], false, isBit), cancellationToken: cancellationToken).Conf
List addresses = new();
foreach (var item in sAddresss)
{
- siemensAddresses.Add(addresses);
+ siemensAddresses.Add(addresses);
dataLen = (ushort)(dataLen + item.Data.Length + 4);
ushort telegramLen = (ushort)(itemLen * 12 + 19 + dataLen);
if (telegramLen < PduLength)
@@ -234,7 +269,8 @@ new S7Send([sAddress], false, isBit), cancellationToken: cancellationToken).Conf
}
else
{
- return new OperResult("Write length exceeds limit");
+ SetFailOperResult(new OperResult("Write length exceeds limit"));
+ return dictOperResult;
}
}
@@ -246,24 +282,25 @@ new S7Send([sAddress], false, isBit), cancellationToken: cancellationToken).Conf
try
{
var result = await this.SendThenReturnAsync(
- new S7Send(item.ToArray(), read, isBit), cancellationToken: cancellationToken).ConfigureAwait(false);
- if(!result.IsSuccess)
+ new S7Send(item.ToArray(), false), cancellationToken: cancellationToken).ConfigureAwait(false);
+ foreach (var i1 in item)
{
- return result;
+ dictOperResult.TryAdd(i1, result);
}
}
catch (Exception ex)
{
- return new OperResult(ex);
+
+ SetFailOperResult(new OperResult(ex));
+ return dictOperResult;
}
}
- return new();
+ return dictOperResult;
}
}
}
-
#region 读写
///
@@ -272,7 +309,7 @@ new S7Send([sAddress], false, isBit), cancellationToken: cancellationToken).Conf
try
{
var sAddress = SiemensAddress.ParseFrom(address, length);
- return S7RequestAsync([sAddress], true, false, 0, cancellationToken);
+ return S7ReadAsync([sAddress], cancellationToken);
}
catch (Exception ex)
{
@@ -288,7 +325,7 @@ new S7Send([sAddress], false, isBit), cancellationToken: cancellationToken).Conf
var sAddress = SiemensAddress.ParseFrom(address);
sAddress.Data = value;
sAddress.Length = value.Length;
- return await S7RequestAsync([sAddress], false, false, 0, cancellationToken);
+ return (await S7WriteAsync([sAddress], cancellationToken)).FirstOrDefault().Value;
}
catch (Exception ex)
{
@@ -299,16 +336,14 @@ new S7Send([sAddress], false, isBit), cancellationToken: cancellationToken).Conf
///
public override async ValueTask WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default)
{
- //if (value.Length > 1)
- //{
- // return new OperResult(SiemensS7Resource.Localizer["MulWriteError"]);
- //}
try
{
var sAddress = SiemensAddress.ParseFrom(address);
sAddress.Data = value.BoolArrayToByte();
sAddress.Length = sAddress.Data.Length;
- return await S7RequestAsync([sAddress], false, true, value.Length, cancellationToken);
+ sAddress.BitLength = value.Length;
+ sAddress.IsBit = true;
+ return (await S7WriteAsync([sAddress], cancellationToken)).FirstOrDefault().Value;
}
catch (Exception ex)
{
diff --git a/src/plugin/ThingsGateway.Plugin.SiemensS7/SiemensS7Master/SiemensS7Master.cs b/src/plugin/ThingsGateway.Plugin.SiemensS7/SiemensS7Master/SiemensS7Master.cs
index 9535456d2..e71f6ed69 100644
--- a/src/plugin/ThingsGateway.Plugin.SiemensS7/SiemensS7Master/SiemensS7Master.cs
+++ b/src/plugin/ThingsGateway.Plugin.SiemensS7/SiemensS7Master/SiemensS7Master.cs
@@ -8,6 +8,13 @@
// QQ群:605534569
//------------------------------------------------------------------------------
+using Newtonsoft.Json.Linq;
+
+using System.Collections.Concurrent;
+using System.ComponentModel.DataAnnotations;
+using System.IO;
+
+using ThingsGateway.Foundation.SiemensS7;
using ThingsGateway.Gateway.Application;
using TouchSocket.Core;
@@ -54,6 +61,127 @@ public class SiemensS7Master : CollectBase
};
}
+ ///
+ /// 获取jtoken值代表的字节数组,不包含字符串
+ ///
+ ///
+ ///
+ ///
+ public byte[] GetBytes(DataTypeEnum dataType, JToken value)
+ {
+ //排除字符串
+ if (value is JArray jArray)
+ {
+ return dataType switch
+ {
+ DataTypeEnum.Boolean => jArray.ToObject().BoolArrayToByte(),
+ DataTypeEnum.Byte => jArray.ToObject(),
+ DataTypeEnum.Int16 => _plc.ThingsGatewayBitConverter.GetBytes(jArray.ToObject()),
+ DataTypeEnum.UInt16 => _plc.ThingsGatewayBitConverter.GetBytes(jArray.ToObject()),
+ DataTypeEnum.Int32 => _plc.ThingsGatewayBitConverter.GetBytes(jArray.ToObject()),
+ DataTypeEnum.UInt32 => _plc.ThingsGatewayBitConverter.GetBytes(jArray.ToObject()),
+ DataTypeEnum.Int64 => _plc.ThingsGatewayBitConverter.GetBytes(jArray.ToObject()),
+ DataTypeEnum.UInt64 => _plc.ThingsGatewayBitConverter.GetBytes(jArray.ToObject()),
+ DataTypeEnum.Single => _plc.ThingsGatewayBitConverter.GetBytes(jArray.ToObject()),
+ DataTypeEnum.Double => _plc.ThingsGatewayBitConverter.GetBytes(jArray.ToObject()),
+ _ => throw new(DefaultResource.Localizer["DataTypeNotSupported", dataType]),
+ };
+ }
+ else
+ {
+ return dataType switch
+ {
+ DataTypeEnum.Boolean => _plc.ThingsGatewayBitConverter.GetBytes(value.ToObject()),
+ DataTypeEnum.Byte => [value.ToObject()],
+ DataTypeEnum.Int16 => _plc.ThingsGatewayBitConverter.GetBytes(value.ToObject()),
+ DataTypeEnum.UInt16 => _plc.ThingsGatewayBitConverter.GetBytes(value.ToObject()),
+ DataTypeEnum.Int32 => _plc.ThingsGatewayBitConverter.GetBytes(value.ToObject()),
+ DataTypeEnum.UInt32 => _plc.ThingsGatewayBitConverter.GetBytes(value.ToObject()),
+ DataTypeEnum.Int64 => _plc.ThingsGatewayBitConverter.GetBytes(value.ToObject()),
+ DataTypeEnum.UInt64 => _plc.ThingsGatewayBitConverter.GetBytes(value.ToObject()),
+ DataTypeEnum.Single => _plc.ThingsGatewayBitConverter.GetBytes(value.ToObject()),
+ DataTypeEnum.Double => _plc.ThingsGatewayBitConverter.GetBytes(value.ToObject()),
+ _ => throw new(DefaultResource.Localizer["DataTypeNotSupported", dataType]),
+ };
+ }
+ }
+
+
+ protected override async ValueTask> WriteValuesAsync(Dictionary writeInfoLists, CancellationToken cancellationToken)
+ {
+ try
+ {
+ // 如果是单线程模式,则等待写入锁
+ if (IsSingleThread)
+ await WriteLock.WaitAsync(cancellationToken).ConfigureAwait(false);
+
+ // 检查协议是否为空,如果为空则抛出异常
+ if (Protocol == null)
+ throw new NotSupportedException();
+
+ // 创建用于存储操作结果的并发字典
+ ConcurrentDictionary operResults = new();
+
+ //转换
+ Dictionary addresses = new();
+ var w1 = writeInfoLists.Where(a => a.Key.DataType != DataTypeEnum.String);
+ var w2 = writeInfoLists.Where(a => a.Key.DataType == DataTypeEnum.String);
+ foreach (var item in w1)
+ {
+ SiemensAddress siemensAddress = SiemensAddress.ParseFrom(item.Key.RegisterAddress);
+ siemensAddress.Data = GetBytes(item.Key.DataType, item.Value);
+ siemensAddress.Length = siemensAddress.Data.Length;
+ siemensAddress.BitLength = 1;
+ siemensAddress.IsBit = item.Key.DataType == DataTypeEnum.Boolean;
+ if (item.Key.DataType == DataTypeEnum.Boolean)
+ {
+ if (item.Value is JArray jArray)
+ {
+ siemensAddress.BitLength = jArray.ToObject().Length;
+ }
+ }
+ addresses.Add(item.Key, siemensAddress);
+ }
+
+ var result = await _plc.S7WriteAsync(addresses.Select(a => a.Value).ToArray(), cancellationToken).ConfigureAwait(false);
+ foreach (var writeInfo in addresses)
+ {
+ if (result.TryGetValue(writeInfo.Value, out var r1))
+ {
+ operResults.TryAdd(writeInfo.Key.Name, r1);
+ }
+ }
+
+ // 使用并发方式遍历写入信息列表,并进行异步写入操作
+ await w2.ParallelForEachAsync(async (writeInfo, cancellationToken) =>
+ {
+ try
+ {
+ // 调用协议的写入方法,将写入信息中的数据写入到对应的寄存器地址,并获取操作结果
+ var result = await Protocol.WriteAsync(writeInfo.Key.RegisterAddress, writeInfo.Value, writeInfo.Key.DataType, cancellationToken).ConfigureAwait(false);
+
+ // 将操作结果添加到结果字典中,使用变量名称作为键
+ operResults.TryAdd(writeInfo.Key.Name, result);
+ }
+ catch (Exception ex)
+ {
+ operResults.TryAdd(writeInfo.Key.Name, new(ex));
+ }
+ }, CollectProperties.ConcurrentCount, cancellationToken).ConfigureAwait(false);
+
+
+ // 返回包含操作结果的字典
+ return new Dictionary(operResults);
+ }
+ finally
+ {
+ // 如果是单线程模式,则释放写入锁
+ if (IsSingleThread)
+ WriteLock.Release();
+ }
+
+ }
+
///
///
///
diff --git a/src/tools/ThingsGateway.Startup/ThingsGateway.Startup.csproj b/src/tools/ThingsGateway.Startup/ThingsGateway.Startup.csproj
index 48a37711e..73ffcaf8c 100644
--- a/src/tools/ThingsGateway.Startup/ThingsGateway.Startup.csproj
+++ b/src/tools/ThingsGateway.Startup/ThingsGateway.Startup.csproj
@@ -9,7 +9,7 @@
-
+