mirror of
https://gitee.com/ThingsGateway/ThingsGateway.git
synced 2025-10-20 10:50:48 +08:00
build: 10.11.98
This commit is contained in:
@@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Riok.Mapperly" Version="4.2.1" ExcludeAssets="runtime" PrivateAssets="all" />
|
<PackageReference Include="Riok.Mapperly" Version="4.2.1" ExcludeAssets="runtime" PrivateAssets="all" />
|
||||||
<PackageReference Include="Rougamo.Fody" Version="5.0.1" />
|
<PackageReference Include="Rougamo.Fody" Version="5.0.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' ">
|
<ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' ">
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.1" />
|
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.1" />
|
||||||
|
@@ -5,30 +5,101 @@ namespace ThingsGateway.NewLife;
|
|||||||
|
|
||||||
public class ExpiringDictionary<TKey, TValue> : IDisposable
|
public class ExpiringDictionary<TKey, TValue> : IDisposable
|
||||||
{
|
{
|
||||||
private ConcurrentDictionary<TKey, TValue> _dict = new();
|
/// <summary>缓存项</summary>
|
||||||
private readonly TimerX _cleanupTimer;
|
public class CacheItem
|
||||||
|
|
||||||
public ExpiringDictionary(int cleanupInterval = 60000)
|
|
||||||
{
|
{
|
||||||
_cleanupTimer = new TimerX(Clear, null, cleanupInterval, cleanupInterval) { Async = true };
|
private TValue? _value;
|
||||||
|
/// <summary>数值</summary>
|
||||||
|
public TValue? Value { get => _value; }
|
||||||
|
|
||||||
|
/// <summary>过期时间。系统启动以来的毫秒数</summary>
|
||||||
|
public Int64 ExpiredTime { get; set; }
|
||||||
|
|
||||||
|
/// <summary>是否过期</summary>
|
||||||
|
public Boolean Expired => ExpiredTime <= Runtime.TickCount64;
|
||||||
|
|
||||||
|
/// <summary>访问时间</summary>
|
||||||
|
public Int64 VisitTime { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>构造缓存项</summary>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
/// <param name="expire"></param>
|
||||||
|
public CacheItem(TValue? value, Int32 expire) => Set(value, expire);
|
||||||
|
|
||||||
|
/// <summary>设置数值和过期时间</summary>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
/// <param name="expire">过期时间,秒</param>
|
||||||
|
public void Set(TValue value, Int32 expire)
|
||||||
|
{
|
||||||
|
_value = value;
|
||||||
|
|
||||||
|
var now = VisitTime = Runtime.TickCount64;
|
||||||
|
if (expire <= 0)
|
||||||
|
ExpiredTime = Int64.MaxValue;
|
||||||
|
else
|
||||||
|
ExpiredTime = now + expire * 1000L;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>更新访问时间并返回数值</summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public TValue? Visit()
|
||||||
|
{
|
||||||
|
VisitTime = Runtime.TickCount64;
|
||||||
|
var rs = _value;
|
||||||
|
if (rs == null) return default;
|
||||||
|
|
||||||
|
return rs;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void TryAdd(TKey key, TValue value)
|
private ConcurrentDictionary<TKey, CacheItem> _dict = new();
|
||||||
|
private readonly TimerX _cleanupTimer;
|
||||||
|
private int defaultExpire = 60;
|
||||||
|
public ExpiringDictionary(int expire = 60)
|
||||||
{
|
{
|
||||||
_dict.TryAdd(key, value);
|
defaultExpire = expire;
|
||||||
|
_cleanupTimer = new TimerX(TimerClear, null, 10000, 10000) { Async = true };
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public bool TryAdd(TKey key, TValue value)
|
||||||
|
{
|
||||||
|
if (_dict.TryGetValue(key, out var item))
|
||||||
|
{
|
||||||
|
if (!item.Expired) return false;
|
||||||
|
item.Set(value, defaultExpire);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return _dict.TryAdd(key, new CacheItem(value, defaultExpire));
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryGetValue(TKey key, out TValue value)
|
public bool TryGetValue(TKey key, out TValue value)
|
||||||
{
|
{
|
||||||
return _dict.TryGetValue(key, out value);
|
value = default;
|
||||||
|
|
||||||
|
// 没有值,直接结束
|
||||||
|
if (!_dict.TryGetValue(key, out var item) || item == null) return false;
|
||||||
|
|
||||||
|
// 得到已有值
|
||||||
|
value = item.Visit();
|
||||||
|
|
||||||
|
// 是否未过期的有效值
|
||||||
|
return !item.Expired;
|
||||||
}
|
}
|
||||||
public TValue GetOrAdd(TKey key, Func<TKey, TValue> func)
|
public TValue GetOrAdd(TKey key, Func<TKey, TValue> func)
|
||||||
{
|
{
|
||||||
return _dict.GetOrAdd(key, func);
|
CacheItem? item = null;
|
||||||
}
|
do
|
||||||
public TValue GetOrAdd(TKey key, TValue value)
|
{
|
||||||
{
|
if (_dict.TryGetValue(key, out item) && item != null) return item.Visit();
|
||||||
return _dict.GetOrAdd(key, value);
|
|
||||||
|
item ??= new CacheItem(func(key), defaultExpire);
|
||||||
|
}
|
||||||
|
while (!_dict.TryAdd(key, item));
|
||||||
|
|
||||||
|
return item.Visit();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryRemove(TKey key) => _dict.TryRemove(key, out _);
|
public bool TryRemove(TKey key) => _dict.TryRemove(key, out _);
|
||||||
@@ -41,7 +112,31 @@ public class ExpiringDictionary<TKey, TValue> : IDisposable
|
|||||||
_dict = new();
|
_dict = new();
|
||||||
data.Clear();
|
data.Clear();
|
||||||
}
|
}
|
||||||
|
private void TimerClear(object? state)
|
||||||
|
{
|
||||||
|
|
||||||
|
var dic = _dict;
|
||||||
|
if (dic.IsEmpty) return;
|
||||||
|
|
||||||
|
// 60分钟之内过期的数据,进入LRU淘汰
|
||||||
|
var now = Runtime.TickCount64;
|
||||||
|
|
||||||
|
// 这里先计算,性能很重要
|
||||||
|
var toDels = new List<TKey>();
|
||||||
|
foreach (var item in dic)
|
||||||
|
{
|
||||||
|
// 已过期,准备删除
|
||||||
|
var ci = item.Value;
|
||||||
|
if (ci.ExpiredTime <= now)
|
||||||
|
toDels.Add(item.Key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确认删除
|
||||||
|
foreach (var item in toDels)
|
||||||
|
{
|
||||||
|
_dict.Remove(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_dict.Clear();
|
_dict.Clear();
|
||||||
|
@@ -561,7 +561,7 @@ public static class Reflect
|
|||||||
/// <param name="method"></param>
|
/// <param name="method"></param>
|
||||||
/// <param name="target"></param>
|
/// <param name="target"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static TFunc? As<TFunc>(this MethodInfo method, object? target = null)
|
public static TFunc? As<TFunc>(this MethodInfo method, object? target = null) where TFunc : class
|
||||||
{
|
{
|
||||||
if (method == null) return default;
|
if (method == null) return default;
|
||||||
|
|
||||||
@@ -569,10 +569,14 @@ public static class Reflect
|
|||||||
|
|
||||||
var func = DelegateCache<TFunc>.Cache.GetOrAdd(
|
var func = DelegateCache<TFunc>.Cache.GetOrAdd(
|
||||||
key,
|
key,
|
||||||
_ => (TFunc)(object)(
|
_ =>
|
||||||
|
{
|
||||||
|
return (
|
||||||
target == null
|
target == null
|
||||||
? Delegate.CreateDelegate(typeof(TFunc), method, true)
|
? Delegate.CreateDelegate(typeof(TFunc), method, true) as TFunc
|
||||||
: Delegate.CreateDelegate(typeof(TFunc), target, method, true)));
|
: Delegate.CreateDelegate(typeof(TFunc), target, method, true) as TFunc
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
return func;
|
return func;
|
||||||
}
|
}
|
||||||
|
@@ -391,11 +391,7 @@ public class TimerX : ITimer, ITimerx, IDisposable
|
|||||||
// 释放非托管资源
|
// 释放非托管资源
|
||||||
Scheduler?.Remove(this, disposing ? "Dispose" : "GC");
|
Scheduler?.Remove(this, disposing ? "Dispose" : "GC");
|
||||||
|
|
||||||
DelegateCache<TimerCallback>.Cache.Clear();
|
|
||||||
#if NET6_0_OR_GREATER
|
|
||||||
DelegateCache<Func<Object?, ValueTask>>.Cache.Clear();
|
|
||||||
#endif
|
|
||||||
DelegateCache<Func<Object?, Task>>.Cache.Clear();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,9 +1,9 @@
|
|||||||
<Project>
|
<Project>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<PluginVersion>10.11.97</PluginVersion>
|
<PluginVersion>10.11.98</PluginVersion>
|
||||||
<ProPluginVersion>10.11.97</ProPluginVersion>
|
<ProPluginVersion>10.11.98</ProPluginVersion>
|
||||||
<DefaultVersion>10.11.97</DefaultVersion>
|
<DefaultVersion>10.11.98</DefaultVersion>
|
||||||
<AuthenticationVersion>10.11.6</AuthenticationVersion>
|
<AuthenticationVersion>10.11.6</AuthenticationVersion>
|
||||||
<SourceGeneratorVersion>10.11.6</SourceGeneratorVersion>
|
<SourceGeneratorVersion>10.11.6</SourceGeneratorVersion>
|
||||||
<NET8Version>8.0.20</NET8Version>
|
<NET8Version>8.0.20</NET8Version>
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
<IsTrimmable>false</IsTrimmable>
|
<IsTrimmable>false</IsTrimmable>
|
||||||
<ManagementProPluginVersion>10.11.87</ManagementProPluginVersion>
|
<ManagementProPluginVersion>10.11.87</ManagementProPluginVersion>
|
||||||
<ManagementPluginVersion>10.11.87</ManagementPluginVersion>
|
<ManagementPluginVersion>10.11.87</ManagementPluginVersion>
|
||||||
<TSVersion>4.0.0-beta.115</TSVersion>
|
<TSVersion>4.0.0-beta.120</TSVersion>
|
||||||
|
|
||||||
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
@@ -9,7 +9,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Portable.BouncyCastle" Version="1.9.0" />
|
<PackageReference Include="Portable.BouncyCastle" Version="1.9.0" />
|
||||||
<PackageReference Include="Riok.Mapperly" Version="4.2.1" ExcludeAssets="runtime" PrivateAssets="all" />
|
<PackageReference Include="Riok.Mapperly" Version="4.2.1" ExcludeAssets="runtime" PrivateAssets="all" />
|
||||||
<PackageReference Include="Rougamo.Fody" Version="5.0.1" />
|
<PackageReference Include="Rougamo.Fody" Version="5.0.2" />
|
||||||
<PackageReference Include="TouchSocket.Dmtp" Version="$(TSVersion)" />
|
<PackageReference Include="TouchSocket.Dmtp" Version="$(TSVersion)" />
|
||||||
<!--<PackageReference Include="TouchSocket.WebApi.Swagger" Version="$(TSVersion)" />-->
|
<!--<PackageReference Include="TouchSocket.WebApi.Swagger" Version="$(TSVersion)" />-->
|
||||||
<PackageReference Include="TouchSocket.WebApi" Version="$(TSVersion)" />
|
<PackageReference Include="TouchSocket.WebApi" Version="$(TSVersion)" />
|
||||||
|
@@ -21,7 +21,7 @@
|
|||||||
ShowToolbar="true"
|
ShowToolbar="true"
|
||||||
ShowExportButton
|
ShowExportButton
|
||||||
IsAutoRefresh
|
IsAutoRefresh
|
||||||
AutoRefreshInterval="2000"
|
AutoRefreshInterval="1000"
|
||||||
ShowDefaultButtons=true
|
ShowDefaultButtons=true
|
||||||
ShowSearch=false
|
ShowSearch=false
|
||||||
ExtendButtonColumnWidth=220
|
ExtendButtonColumnWidth=220
|
||||||
|
@@ -55,11 +55,11 @@ public partial class VariableRuntimeInfo : IDisposable
|
|||||||
scheduler = new SmartTriggerScheduler(Notify, TimeSpan.FromMilliseconds(1000));
|
scheduler = new SmartTriggerScheduler(Notify, TimeSpan.FromMilliseconds(1000));
|
||||||
|
|
||||||
#if !Management
|
#if !Management
|
||||||
_ = RunTimerAsync();
|
//timer = new TimerX(RunTimerAsync, null, 1000, 1000) { Async = true };
|
||||||
#endif
|
#endif
|
||||||
base.OnInitialized();
|
base.OnInitialized();
|
||||||
}
|
}
|
||||||
|
//private TimerX timer;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// IntFormatter
|
/// IntFormatter
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -92,27 +92,23 @@ public partial class VariableRuntimeInfo : IDisposable
|
|||||||
await InvokeAsync(table.QueryAsync);
|
await InvokeAsync(table.QueryAsync);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task RunTimerAsync()
|
//private async Task RunTimerAsync(object? state)
|
||||||
{
|
//{
|
||||||
while (!Disposed)
|
// try
|
||||||
{
|
// {
|
||||||
try
|
// //if (table != null)
|
||||||
{
|
// // await InvokeAsync(() => table.RowElementRefresh());
|
||||||
//if (table != null)
|
|
||||||
// await table.QueryAsync();
|
|
||||||
|
|
||||||
await InvokeAsync(StateHasChanged);
|
// await InvokeAsync(StateHasChanged);
|
||||||
}
|
// }
|
||||||
catch (Exception ex)
|
// catch (Exception ex)
|
||||||
{
|
// {
|
||||||
NewLife.Log.XTrace.WriteException(ex);
|
// NewLife.Log.XTrace.WriteException(ex);
|
||||||
}
|
// }
|
||||||
finally
|
// finally
|
||||||
{
|
// {
|
||||||
await Task.Delay(1000);
|
// }
|
||||||
}
|
//}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#region 查询
|
#region 查询
|
||||||
|
|
||||||
@@ -126,7 +122,7 @@ public partial class VariableRuntimeInfo : IDisposable
|
|||||||
return data;
|
return data;
|
||||||
#else
|
#else
|
||||||
var data = Items
|
var data = Items
|
||||||
.WhereIf(!options.SearchText.IsNullOrWhiteSpace(), a => a.Name.Contains(options.SearchText))
|
.WhereIf(!string.IsNullOrWhiteSpace(options.SearchText), a => a.Name.Contains(options.SearchText))
|
||||||
.GetQueryData(options);
|
.GetQueryData(options);
|
||||||
_option = options;
|
_option = options;
|
||||||
return Task.FromResult(data);
|
return Task.FromResult(data);
|
||||||
@@ -354,7 +350,7 @@ public partial class VariableRuntimeInfo : IDisposable
|
|||||||
#if !Management
|
#if !Management
|
||||||
|
|
||||||
var models = Items
|
var models = Items
|
||||||
.WhereIf(!_option.SearchText.IsNullOrWhiteSpace(), a => a.Name.Contains(_option.SearchText)).GetData(_option, out var total).Cast<Variable>().ToList();
|
.WhereIf(!string.IsNullOrWhiteSpace(_option.SearchText), a => a.Name.Contains(_option.SearchText)).GetData(_option, out var total).Cast<Variable>().ToList();
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
|
@@ -516,185 +516,169 @@ public class ModbusSlave : DeviceBase, IModbusAddress
|
|||||||
return new OperResult(ex);
|
return new OperResult(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
protected override Task ChannelReceived(IClientChannel client, ReceivedDataEventArgs e, bool last)
|
||||||
/// <inheritdoc/>
|
|
||||||
protected override async Task ChannelReceived(IClientChannel client, ReceivedDataEventArgs e, bool last)
|
|
||||||
{
|
{
|
||||||
var requestInfo = e.RequestInfo;
|
return HandleChannelReceivedAsync(client, e, last);
|
||||||
bool modbusRtu = false;
|
}
|
||||||
ModbusRequest modbusRequest = default;
|
|
||||||
ReadOnlySequence<byte> readOnlySequences = default;
|
private async Task HandleChannelReceivedAsync(IClientChannel client, ReceivedDataEventArgs e, bool last)
|
||||||
//接收外部报文
|
{
|
||||||
if (requestInfo is ModbusRtuSlaveMessage modbusRtuSlaveMessage)
|
if (!TryParseRequest(e.RequestInfo, out var modbusRequest, out var sequences, out var modbusRtu))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!MulStation && modbusRequest.Station != Station)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var function = NormalizeFunctionCode(modbusRequest.FunctionCode);
|
||||||
|
|
||||||
|
if (function <= 4)
|
||||||
|
await HandleReadRequestAsync(client, e, modbusRequest, sequences, modbusRtu).ConfigureAwait(false);
|
||||||
|
else
|
||||||
|
await HandleWriteRequestAsync(client, e, modbusRequest, sequences, modbusRtu, function).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TryParseRequest(object requestInfo, out ModbusRequest modbusRequest, out ReadOnlySequence<byte> sequences, out bool modbusRtu)
|
||||||
|
{
|
||||||
|
modbusRequest = default;
|
||||||
|
sequences = default;
|
||||||
|
modbusRtu = false;
|
||||||
|
|
||||||
|
switch (requestInfo)
|
||||||
{
|
{
|
||||||
if (!modbusRtuSlaveMessage.IsSuccess)
|
case ModbusRtuSlaveMessage rtuMsg when rtuMsg.IsSuccess:
|
||||||
{
|
modbusRequest = rtuMsg.Request;
|
||||||
return;
|
sequences = rtuMsg.Sequences;
|
||||||
}
|
modbusRtu = true;
|
||||||
modbusRequest = modbusRtuSlaveMessage.Request;
|
return true;
|
||||||
readOnlySequences = modbusRtuSlaveMessage.Sequences;
|
|
||||||
modbusRtu = true;
|
case ModbusTcpSlaveMessage tcpMsg when tcpMsg.IsSuccess:
|
||||||
|
modbusRequest = tcpMsg.Request;
|
||||||
|
sequences = tcpMsg.Sequences;
|
||||||
|
modbusRtu = false;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
else if (requestInfo is ModbusTcpSlaveMessage modbusTcpSlaveMessage)
|
}
|
||||||
|
|
||||||
|
private static byte NormalizeFunctionCode(byte funcCode)
|
||||||
|
=> funcCode > 0x30 ? (byte)(funcCode - 0x30) : funcCode;
|
||||||
|
|
||||||
|
private async Task HandleReadRequestAsync(
|
||||||
|
IClientChannel client,
|
||||||
|
ReceivedDataEventArgs e,
|
||||||
|
ModbusRequest modbusRequest,
|
||||||
|
ReadOnlySequence<byte> sequences,
|
||||||
|
bool modbusRtu)
|
||||||
|
{
|
||||||
|
var data = ModbusRequest(modbusRequest, true);
|
||||||
|
if (!data.IsSuccess)
|
||||||
{
|
{
|
||||||
if (!modbusTcpSlaveMessage.IsSuccess)
|
await WriteError(modbusRtu, client, sequences, e).ConfigureAwait(false);
|
||||||
{
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ValueByteBlock byteBlock = new(1024);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
WriteReadResponse(modbusRequest, sequences, data.Content, ref byteBlock, modbusRtu);
|
||||||
|
await ReturnData(client, byteBlock.Memory, e).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
await WriteError(modbusRtu, client, sequences, e).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
byteBlock.SafeDispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task HandleWriteRequestAsync(
|
||||||
|
IClientChannel client,
|
||||||
|
ReceivedDataEventArgs e,
|
||||||
|
ModbusRequest modbusRequest,
|
||||||
|
ReadOnlySequence<byte> sequences,
|
||||||
|
bool modbusRtu,
|
||||||
|
byte f)
|
||||||
|
{
|
||||||
|
var modbusAddress = new ModbusAddress(modbusRequest);
|
||||||
|
bool isSuccess;
|
||||||
|
|
||||||
|
switch (f)
|
||||||
|
{
|
||||||
|
case 5:
|
||||||
|
case 15:
|
||||||
|
modbusAddress.WriteFunctionCode = modbusRequest.FunctionCode;
|
||||||
|
modbusAddress.FunctionCode = 1;
|
||||||
|
isSuccess = await HandleWriteCoreAsync(modbusAddress, client, modbusRequest).ConfigureAwait(false);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 6:
|
||||||
|
case 16:
|
||||||
|
modbusAddress.WriteFunctionCode = modbusRequest.FunctionCode;
|
||||||
|
modbusAddress.FunctionCode = 3;
|
||||||
|
isSuccess = await HandleWriteCoreAsync(modbusAddress, client, modbusRequest).ConfigureAwait(false);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
modbusRequest = modbusTcpSlaveMessage.Request;
|
|
||||||
readOnlySequences = modbusTcpSlaveMessage.Sequences;
|
if (isSuccess)
|
||||||
modbusRtu = false;
|
await WriteSuccess(modbusRtu, client, sequences, e).ConfigureAwait(false);
|
||||||
|
else
|
||||||
|
await WriteError(modbusRtu, client, sequences, e).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<bool> HandleWriteCoreAsync(ModbusAddress address, IClientChannel client, ModbusRequest modbusRequest)
|
||||||
|
{
|
||||||
|
if (WriteData != null)
|
||||||
|
{
|
||||||
|
var result = await WriteData(address, ThingsGatewayBitConverter, client).ConfigureAwait(false);
|
||||||
|
if (!result.IsSuccess) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsWriteMemory)
|
||||||
|
{
|
||||||
|
var memResult = ModbusRequest(modbusRequest, false);
|
||||||
|
return memResult.IsSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void WriteReadResponse(
|
||||||
|
ModbusRequest modbusRequest,
|
||||||
|
ReadOnlySequence<byte> sequences,
|
||||||
|
ReadOnlyMemory<byte> content,
|
||||||
|
ref ValueByteBlock byteBlock,
|
||||||
|
bool modbusRtu)
|
||||||
|
{
|
||||||
|
if (modbusRtu)
|
||||||
|
ByteBlockExtension.Write(ref byteBlock, sequences.Slice(0, 2));
|
||||||
|
else
|
||||||
|
ByteBlockExtension.Write(ref byteBlock, sequences.Slice(0, 8));
|
||||||
|
|
||||||
|
if (modbusRequest.IsBitFunction)
|
||||||
|
{
|
||||||
|
var bitdata = content.Span.ByteToBool().AsSpan().BoolArrayToByte();
|
||||||
|
var len = (int)Math.Ceiling(modbusRequest.Length / 8.0);
|
||||||
|
var bitWriteData = bitdata.AsMemory().Slice(0, len);
|
||||||
|
WriterExtension.WriteValue(ref byteBlock, (byte)bitWriteData.Length);
|
||||||
|
byteBlock.Write(bitWriteData.Span);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return;
|
WriterExtension.WriteValue(ref byteBlock, (byte)content.Length);
|
||||||
|
byteBlock.Write(content.Span);
|
||||||
}
|
}
|
||||||
//忽略不同设备地址的报文
|
|
||||||
if (!MulStation && modbusRequest.Station != Station)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var f = modbusRequest.FunctionCode > 0x30 ? modbusRequest.FunctionCode - 0x30 : modbusRequest.FunctionCode;
|
|
||||||
|
|
||||||
if (f <= 4)
|
if (modbusRtu)
|
||||||
{
|
byteBlock.Write(CRC16Utils.Crc16Only(byteBlock.Memory.Span));
|
||||||
var data = ModbusRequest(modbusRequest, true);
|
else
|
||||||
if (data.IsSuccess)
|
ByteBlockExtension.WriteBackValue(ref byteBlock, (byte)(byteBlock.Length - 6), EndianType.Big, 5);
|
||||||
{
|
|
||||||
ValueByteBlock byteBlock = new(1024);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (modbusRtu)
|
|
||||||
{
|
|
||||||
ByteBlockExtension.Write(ref byteBlock, readOnlySequences.Slice(0, 2));
|
|
||||||
if (modbusRequest.IsBitFunction)
|
|
||||||
{
|
|
||||||
var bitdata = data.Content.Span.ByteToBool().AsSpan().BoolArrayToByte();
|
|
||||||
ReadOnlyMemory<byte> bitwritedata = bitdata.Length == (int)Math.Ceiling(modbusRequest.Length / 8.0) ? bitdata.AsMemory() : bitdata.AsMemory().Slice(0, (int)Math.Ceiling(modbusRequest.Length / 8.0));
|
|
||||||
WriterExtension.WriteValue(ref byteBlock, (byte)bitwritedata.Length);
|
|
||||||
byteBlock.Write(bitwritedata.Span);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
WriterExtension.WriteValue(ref byteBlock, (byte)data.Content.Length);
|
|
||||||
byteBlock.Write(data.Content.Span);
|
|
||||||
}
|
|
||||||
byteBlock.Write(CRC16Utils.Crc16Only(byteBlock.Memory.Span));
|
|
||||||
await ReturnData(client, byteBlock.Memory, e).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ByteBlockExtension.Write(ref byteBlock, readOnlySequences.Slice(0, 8));
|
|
||||||
if (modbusRequest.IsBitFunction)
|
|
||||||
{
|
|
||||||
var bitdata = data.Content.Span.ByteToBool().AsSpan().BoolArrayToByte();
|
|
||||||
ReadOnlyMemory<byte> bitwritedata = bitdata.Length == (int)Math.Ceiling(modbusRequest.Length / 8.0) ? bitdata.AsMemory() : bitdata.AsMemory().Slice(0, (int)Math.Ceiling(modbusRequest.Length / 8.0));
|
|
||||||
WriterExtension.WriteValue(ref byteBlock, (byte)bitwritedata.Length);
|
|
||||||
byteBlock.Write(bitwritedata.Span);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
WriterExtension.WriteValue(ref byteBlock, (byte)data.Content.Length);
|
|
||||||
byteBlock.Write(data.Content.Span);
|
|
||||||
}
|
|
||||||
ByteBlockExtension.WriteBackValue(ref byteBlock, (byte)(byteBlock.Length - 6), EndianType.Big, 5);
|
|
||||||
await ReturnData(client, byteBlock.Memory, e).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
await WriteError(modbusRtu, client, readOnlySequences, e).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
byteBlock.SafeDispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await WriteError(modbusRtu, client, readOnlySequences, e).ConfigureAwait(false);//返回错误码
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else//写入
|
|
||||||
{
|
|
||||||
if (f == 5 || f == 15)
|
|
||||||
{
|
|
||||||
//写入继电器
|
|
||||||
if (WriteData != null)
|
|
||||||
{
|
|
||||||
var modbusAddress = new ModbusAddress(modbusRequest) { WriteFunctionCode = modbusRequest.FunctionCode, FunctionCode = 1 };
|
|
||||||
// 接收外部写入时,传出变量地址/写入字节组/转换规则/客户端
|
|
||||||
if ((await WriteData(modbusAddress, ThingsGatewayBitConverter, client).ConfigureAwait(false)).IsSuccess)
|
|
||||||
{
|
|
||||||
await WriteSuccess(modbusRtu, client, readOnlySequences, e).ConfigureAwait(false);
|
|
||||||
if (IsWriteMemory)
|
|
||||||
{
|
|
||||||
var result = ModbusRequest(modbusRequest, false);
|
|
||||||
if (result.IsSuccess)
|
|
||||||
await WriteSuccess(modbusRtu, client, readOnlySequences, e).ConfigureAwait(false);
|
|
||||||
else
|
|
||||||
await WriteError(modbusRtu, client, readOnlySequences, e).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
await WriteSuccess(modbusRtu, client, readOnlySequences, e).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await WriteError(modbusRtu, client, readOnlySequences, e).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//写入内存区
|
|
||||||
var result = ModbusRequest(modbusRequest, false);
|
|
||||||
if (result.IsSuccess)
|
|
||||||
{
|
|
||||||
await WriteSuccess(modbusRtu, client, readOnlySequences, e).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await WriteError(modbusRtu, client, readOnlySequences, e).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (f == 6 || f == 16)
|
|
||||||
{
|
|
||||||
//写入寄存器
|
|
||||||
if (WriteData != null)
|
|
||||||
{
|
|
||||||
var modbusAddress = new ModbusAddress(modbusRequest) { WriteFunctionCode = modbusRequest.FunctionCode, FunctionCode = 3 };
|
|
||||||
if ((await WriteData(modbusAddress, ThingsGatewayBitConverter, client).ConfigureAwait(false)).IsSuccess)
|
|
||||||
{
|
|
||||||
if (IsWriteMemory)
|
|
||||||
{
|
|
||||||
var result = ModbusRequest(modbusRequest, false);
|
|
||||||
if (result.IsSuccess)
|
|
||||||
await WriteSuccess(modbusRtu, client, readOnlySequences, e).ConfigureAwait(false);
|
|
||||||
else
|
|
||||||
await WriteError(modbusRtu, client, readOnlySequences, e).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
await WriteSuccess(modbusRtu, client, readOnlySequences, e).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await WriteError(modbusRtu, client, readOnlySequences, e).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var result = ModbusRequest(modbusRequest, false);
|
|
||||||
if (result.IsSuccess)
|
|
||||||
{
|
|
||||||
await WriteSuccess(modbusRtu, client, readOnlySequences, e).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await WriteError(modbusRtu, client, readOnlySequences, e).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ReturnData(IClientChannel client, ReadOnlyMemory<byte> sendData, ReceivedDataEventArgs e)
|
private async Task ReturnData(IClientChannel client, ReadOnlyMemory<byte> sendData, ReceivedDataEventArgs e)
|
||||||
|
Reference in New Issue
Block a user