release:6.0.4.41

refactor: 支持blazor hybrid;
feat(s7): 支持多写;
This commit is contained in:
Diego
2024-07-30 19:45:06 +08:00
parent e49507cd14
commit 150435f24e
9 changed files with 231 additions and 58 deletions

View File

@@ -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());
}
}
/// <inheritdoc/>

View File

@@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<Version>6.0.4.38</Version>
<Version>6.0.4.41</Version>
</PropertyGroup>
<ItemGroup>

View File

@@ -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);

View File

@@ -12,6 +12,4 @@
</ItemGroup>
</Project>

View File

@@ -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
/// </summary>
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;

View File

@@ -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;
}
/// <inheritdoc />

View File

@@ -9,6 +9,7 @@
//------------------------------------------------------------------------------
using System.Data;
using System.Net;
using ThingsGateway.Foundation.Extension.String;
@@ -23,8 +24,18 @@ public partial class SiemensS7Master : ProtocolBase
/// <inheritdoc/>
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;
}
}
}
/// <summary>
@@ -117,9 +128,8 @@ public partial class SiemensS7Master : ProtocolBase
/// <summary>
/// 此方法并不会智能分组以最大化效率减少传输次数因为返回值是byte[],所以一切都按地址数组的顺序执行,最后合并数组
/// </summary>
public async ValueTask<OperResult<byte[]>> S7RequestAsync(SiemensAddress[] sAddresss, bool read, bool isBit, int bitLength = 0, CancellationToken cancellationToken = default)
public async ValueTask<OperResult<byte[]>> 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
}
/// <summary>
/// 此方法并不会智能分组以最大化效率减少传输次数因为返回值是byte[],所以一切都按地址数组的顺序执行,最后合并数组
/// </summary>
public async ValueTask<Dictionary<SiemensAddress, OperResult>> S7WriteAsync(SiemensAddress[] sAddresss, CancellationToken cancellationToken = default)
{
var dictOperResult = new Dictionary<SiemensAddress, OperResult>();
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<byte[]>("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<byte[]>("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<byte[]>(ex);
SetFailOperResult(new OperResult(ex));
return dictOperResult;
}
finally
{
@@ -212,7 +247,7 @@ new S7Send([sAddress], false, isBit), cancellationToken: cancellationToken).Conf
List<SiemensAddress> 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<byte[]>("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<byte[]>(ex);
SetFailOperResult(new OperResult(ex));
return dictOperResult;
}
}
return new();
return dictOperResult;
}
}
}
#region
/// <inheritdoc/>
@@ -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
/// <inheritdoc/>
public override async ValueTask<OperResult> 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)
{

View File

@@ -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
};
}
/// <summary>
/// 获取jtoken值代表的字节数组不包含字符串
/// </summary>
/// <param name="dataType"></param>
/// <param name="value"></param>
/// <returns></returns>
public byte[] GetBytes(DataTypeEnum dataType, JToken value)
{
//排除字符串
if (value is JArray jArray)
{
return dataType switch
{
DataTypeEnum.Boolean => jArray.ToObject<Boolean[]>().BoolArrayToByte(),
DataTypeEnum.Byte => jArray.ToObject<Byte[]>(),
DataTypeEnum.Int16 => _plc.ThingsGatewayBitConverter.GetBytes(jArray.ToObject<Int16[]>()),
DataTypeEnum.UInt16 => _plc.ThingsGatewayBitConverter.GetBytes(jArray.ToObject<UInt16[]>()),
DataTypeEnum.Int32 => _plc.ThingsGatewayBitConverter.GetBytes(jArray.ToObject<Int32[]>()),
DataTypeEnum.UInt32 => _plc.ThingsGatewayBitConverter.GetBytes(jArray.ToObject<UInt32[]>()),
DataTypeEnum.Int64 => _plc.ThingsGatewayBitConverter.GetBytes(jArray.ToObject<Int64[]>()),
DataTypeEnum.UInt64 => _plc.ThingsGatewayBitConverter.GetBytes(jArray.ToObject<UInt64[]>()),
DataTypeEnum.Single => _plc.ThingsGatewayBitConverter.GetBytes(jArray.ToObject<Single[]>()),
DataTypeEnum.Double => _plc.ThingsGatewayBitConverter.GetBytes(jArray.ToObject<Double[]>()),
_ => throw new(DefaultResource.Localizer["DataTypeNotSupported", dataType]),
};
}
else
{
return dataType switch
{
DataTypeEnum.Boolean => _plc.ThingsGatewayBitConverter.GetBytes(value.ToObject<Boolean>()),
DataTypeEnum.Byte => [value.ToObject<Byte>()],
DataTypeEnum.Int16 => _plc.ThingsGatewayBitConverter.GetBytes(value.ToObject<Int16>()),
DataTypeEnum.UInt16 => _plc.ThingsGatewayBitConverter.GetBytes(value.ToObject<UInt16>()),
DataTypeEnum.Int32 => _plc.ThingsGatewayBitConverter.GetBytes(value.ToObject<Int32>()),
DataTypeEnum.UInt32 => _plc.ThingsGatewayBitConverter.GetBytes(value.ToObject<UInt32>()),
DataTypeEnum.Int64 => _plc.ThingsGatewayBitConverter.GetBytes(value.ToObject<Int64>()),
DataTypeEnum.UInt64 => _plc.ThingsGatewayBitConverter.GetBytes(value.ToObject<UInt64>()),
DataTypeEnum.Single => _plc.ThingsGatewayBitConverter.GetBytes(value.ToObject<Single>()),
DataTypeEnum.Double => _plc.ThingsGatewayBitConverter.GetBytes(value.ToObject<Double>()),
_ => throw new(DefaultResource.Localizer["DataTypeNotSupported", dataType]),
};
}
}
protected override async ValueTask<Dictionary<string, OperResult>> WriteValuesAsync(Dictionary<VariableRunTime, JToken> writeInfoLists, CancellationToken cancellationToken)
{
try
{
// 如果是单线程模式,则等待写入锁
if (IsSingleThread)
await WriteLock.WaitAsync(cancellationToken).ConfigureAwait(false);
// 检查协议是否为空,如果为空则抛出异常
if (Protocol == null)
throw new NotSupportedException();
// 创建用于存储操作结果的并发字典
ConcurrentDictionary<string, OperResult> operResults = new();
//转换
Dictionary<VariableRunTime, SiemensAddress> 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<Boolean[]>().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<string, OperResult>(operResults);
}
finally
{
// 如果是单线程模式,则释放写入锁
if (IsSingleThread)
WriteLock.Release();
}
}
/// <summary>
/// <see cref="SiemensS7PLC.ReadDateAsync(string,CancellationToken)"/>
/// </summary>

View File

@@ -9,7 +9,7 @@
<PackageReference Include="Yitter.IdGenerator" Version="1.0.14" />
<PackageReference Include="MiniExcel" Version="1.34.0" />
<PackageReference Include="Mapster" Version="7.4.0" />
<PackageReference Include="BootstrapBlazor" Version="8.7.4" />
<PackageReference Include="BootstrapBlazor" Version="8.7.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Localization.Abstractions" Version="8.0.7" />