diff --git a/framework/ThingsGateway.ApiController/Controllers/RpcControler.cs b/framework/ThingsGateway.ApiController/Controllers/RpcControler.cs index 5bc984a14..fb5839e58 100644 --- a/framework/ThingsGateway.ApiController/Controllers/RpcControler.cs +++ b/framework/ThingsGateway.ApiController/Controllers/RpcControler.cs @@ -89,13 +89,8 @@ public class RpcControler : IDynamicApiController [Description("写入变量")] public async Task> WriteDeviceMethods(Dictionary objs) { - Dictionary operResultDict = new(); - foreach (KeyValuePair obj in objs) - { - var result = await RpcCore.InvokeDeviceMethodAsync($"WebApi-{UserManager.UserAccount}-{App.HttpContext.Connection.RemoteIpAddress.MapToIPv4()}", obj); - operResultDict.Add(obj.Key, result); - } - return operResultDict; + var result = await RpcCore.InvokeDeviceMethodAsync($"WebApi-{UserManager.UserAccount}-{App.HttpContext.Connection.RemoteIpAddress.MapToIPv4()}", objs); + return result; } } diff --git a/framework/ThingsGateway.Application/Plugin/CollectBase.cs b/framework/ThingsGateway.Application/Plugin/CollectBase.cs index 3e27fbec6..40631acc2 100644 --- a/framework/ThingsGateway.Application/Plugin/CollectBase.cs +++ b/framework/ThingsGateway.Application/Plugin/CollectBase.cs @@ -36,6 +36,10 @@ public abstract class CollectBase : DriverBase /// /// public abstract bool IsSupportRequest { get; } + /// + /// 一般底层驱动,也有可能为null + /// + protected abstract IReadWriteDevice PLC { get; } /// /// 数据转换器 @@ -177,10 +181,22 @@ public abstract class CollectBase : DriverBase } /// - /// 写入变量值 + /// 批量写入变量值,需返回变量名称/结果 /// /// - public abstract Task WriteValueAsync(DeviceVariableRunTime deviceVariable, JToken value, CancellationToken token); + public virtual async Task> WriteValuesAsync(Dictionary writeInfoLists, CancellationToken token) + { + if (PLC == null) + throw new("未初始化成功"); + Dictionary operResults = new(); + foreach (var writeInfo in writeInfoLists) + { + var result = await PLC.WriteAsync(writeInfo.Key.VariableAddress, writeInfo.Key.DataType, writeInfo.Value.ToString(), token); + await Task.Delay(10, token); //防止密集写入 + operResults.Add(writeInfo.Key.Name, result); + } + return operResults; + } /// /// 初始化 diff --git a/framework/ThingsGateway.Application/Rpc/RpcSingletonService.cs b/framework/ThingsGateway.Application/Rpc/RpcSingletonService.cs index f5a615f05..05bd061df 100644 --- a/framework/ThingsGateway.Application/Rpc/RpcSingletonService.cs +++ b/framework/ThingsGateway.Application/Rpc/RpcSingletonService.cs @@ -48,92 +48,172 @@ public class RpcSingletonService : ISingleton /// 反向RPC入口方法 /// /// 触发该方法的源说明 - /// 指定键为变量名称,值为附带方法参数或写入值 + /// 指定键为变量名称,值为附带方法参数或写入值 /// 如果是true,不检查字段 /// 取消源 /// - public async Task InvokeDeviceMethodAsync(string sourceDes, KeyValuePair item, bool isBlazor = false, CancellationToken token = default) + public async Task> InvokeDeviceMethodAsync(string sourceDes, Dictionary items, bool isBlazor = false, CancellationToken token = default) { - //避免并发过高,这里延时10ms - await Task.Delay(10, token); - OperResult data = new(); - var tag = _globalDeviceData.AllVariables.FirstOrDefault(it => it.Name == item.Key); - if (tag == null) return new OperResult("不存在变量:" + item.Key); - if (tag.ProtectTypeEnum == ProtectTypeEnum.ReadOnly) return new OperResult("只读变量"); - if (!tag.RpcWriteEnable && !isBlazor) return new OperResult("不允许远程写入"); - - if (tag.IsMemoryVariable == true) + Dictionary> WriteVariables = new(); + Dictionary> WriteMethods = new(); + Dictionary results = new(); + foreach (var item in items) { - return tag.SetValue(item.Value); - } - var dev = _collectDeviceHostService.CollectDeviceCores.FirstOrDefault(it => it.Device.Id == tag.DeviceId); - if (dev == null) return new OperResult("系统错误,不存在对应采集设备,请稍候重试"); - if (dev.Device.DeviceStatus == DeviceStatusEnum.OffLine) return new OperResult("设备已离线"); - if (dev.Device.DeviceStatus == DeviceStatusEnum.Pause) return new OperResult("设备已暂停"); - if (string.IsNullOrEmpty(tag.OtherMethod)) - { - //写入变量 - JToken tagValue; - try - { - tagValue = JToken.Parse(item.Value); - } - catch (Exception) - { - tagValue = JToken.Parse("\"" + item.Value + "\""); - } - data = await dev.InVokeWriteAsync(tag, tagValue, token); - _logQueues.Enqueue( - new RpcLog() + OperResult data = new(); + var tag = _globalDeviceData.AllVariables.FirstOrDefault(it => it.Name == item.Key); + if (tag == null) + results.Add(item.Key, new("不存在变量:" + item.Key)); + if (tag.ProtectTypeEnum == ProtectTypeEnum.ReadOnly) + results.Add(item.Key, new("只读变量:" + item.Key)); + if (!tag.RpcWriteEnable && !isBlazor) + results.Add(item.Key, new("不允许远程写入:" + item.Key)); + + if (tag.IsMemoryVariable == true) + { + results.Add(item.Key, tag.SetValue(item.Value)); + } + var dev = _collectDeviceHostService.CollectDeviceCores.FirstOrDefault(it => it.Device.Id == tag.DeviceId); + if (dev == null) + results.Add(item.Key, new OperResult("系统错误,不存在对应采集设备,请稍候重试")); + if (dev.Device.DeviceStatus == DeviceStatusEnum.OffLine) + results.Add(item.Key, new OperResult("设备已离线")); + if (dev.Device.DeviceStatus == DeviceStatusEnum.Pause) + results.Add(item.Key, new OperResult("设备已暂停")); + + if (!results.ContainsKey(item.Key)) + { + if (string.IsNullOrEmpty(tag.OtherMethod)) { - LogTime = SysDateTimeExtensions.CurrentDateTime, - OperateMessage = data.Exception, - IsSuccess = data.IsSuccess, - OperateMethod = WriteVariable, - OperateObject = tag.Name, - OperateSource = sourceDes, - ParamJson = item.Value, - ResultJson = data.Message + //写入变量 + JToken tagValue; + try + { + tagValue = JToken.Parse(item.Value); + } + catch (Exception) + { + tagValue = JToken.Parse("\"" + item.Value + "\""); + } + if (WriteVariables.ContainsKey(dev)) + { + + WriteVariables[dev].Add(tag, tagValue); + + } + else + { + WriteVariables.Add(dev, new()); + WriteVariables[dev].Add(tag, tagValue); + } + } + else + { + if (WriteMethods.ContainsKey(dev)) + { + + WriteMethods[dev].Add(tag, item.Value); + + } + else + { + WriteVariables.Add(dev, new()); + WriteVariables[dev].Add(tag, item.Value); + } } - ); - if (!data.IsSuccess) - { - _logger.LogWarning($"写入变量[{tag.Name}]失败:{data.Message}"); } } - else + + foreach (var item in WriteVariables) { - //执行变量附带的方法 - var method = dev.DeviceVariableMethodSources.FirstOrDefault(it => it.DeviceVariable == tag); try { - data = await dev.InvokeMethodAsync(method, false, item.Value, token); + var result = await item.Key.InVokeWriteAsync(item.Value, token); + foreach (var resultItem in result) + { + string operObj; + string parJson; + if (resultItem.Key.IsNullOrEmpty()) + { + operObj = items.Select(x => x.Key).ToJsonString(); + parJson = items.Select(x => x.Value).ToJsonString(); + } + else + { + operObj = resultItem.Key; + parJson = items[resultItem.Key]; + + } + _logQueues.Enqueue( + new RpcLog() + { + LogTime = SysDateTimeExtensions.CurrentDateTime, + OperateMessage = resultItem.Value.Exception, + IsSuccess = resultItem.Value.IsSuccess, + OperateMethod = WriteVariable, + OperateObject = operObj, + OperateSource = sourceDes, + ParamJson = parJson, + ResultJson = resultItem.Value.Message + } + ); + if (!resultItem.Value.IsSuccess) + { + _logger.LogWarning($"写入变量[{resultItem.Key}]失败:{resultItem.Value.Message}"); + } + } + + results.AddRange(result); } catch (Exception ex) { - data = new OperResult(ex); - } - _logQueues.Enqueue( -new RpcLog() -{ - LogTime = SysDateTimeExtensions.CurrentDateTime, - OperateMessage = data.Exception, - IsSuccess = data.IsSuccess, - OperateMethod = tag.OtherMethod, - OperateObject = tag.Name, - OperateSource = sourceDes, - ParamJson = item.Value?.ToString(), - ResultJson = data.Message -} -); - if (!data.IsSuccess) - { - _logger.LogWarning($"执行变量[{tag.Name}]方法[{tag.OtherMethod}]失败:{data.Message}"); + _logger.LogWarning($"写入变量异常:{ex.Message + Environment.NewLine + ex.StackTrace}"); } } - return data; + foreach (var item in WriteMethods) + { + foreach (var writeMethod in item.Value) + { + + //执行变量附带的方法 + var method = item.Key.DeviceVariableMethodSources.FirstOrDefault(it => it.DeviceVariable == writeMethod.Key); + OperResult result; + try + { + result = await item.Key.InvokeMethodAsync(method, false, writeMethod.Value, token); + results.Add(writeMethod.Key.Name, result); + } + catch (Exception ex) + { + result = new OperResult(ex); + results.Add(writeMethod.Key.Name, result); + } + _logQueues.Enqueue( + new RpcLog() + { + LogTime = SysDateTimeExtensions.CurrentDateTime, + OperateMessage = result.Exception, + IsSuccess = result.IsSuccess, + OperateMethod = writeMethod.Key.OtherMethod, + OperateObject = writeMethod.Key.Name, + OperateSource = sourceDes, + ParamJson = writeMethod.Value?.ToString(), + ResultJson = result.Message } + ); + if (!result.IsSuccess) + { + _logger.LogWarning($"执行变量[{writeMethod.Key.Name}]方法[{writeMethod.Key.OtherMethod}]失败:{result.Message}"); + } + + + } + + } + return results; + } + + private async Task RpcLogInsertAsync() { diff --git a/framework/ThingsGateway.Application/ThingsGateway.Application.xml b/framework/ThingsGateway.Application/ThingsGateway.Application.xml index 63630c2ff..b00cae8b7 100644 --- a/framework/ThingsGateway.Application/ThingsGateway.Application.xml +++ b/framework/ThingsGateway.Application/ThingsGateway.Application.xml @@ -1855,6 +1855,11 @@ + + + 一般底层驱动,也有可能为null + + 数据转换器 @@ -1906,9 +1911,9 @@ 采集驱动读取 - + - 写入变量值 + 批量写入变量值,需返回变量名称/结果 @@ -2242,12 +2247,12 @@ - + 反向RPC入口方法 触发该方法的源说明 - 指定键为变量名称,值为附带方法参数或写入值 + 指定键为变量名称,值为附带方法参数或写入值 如果是true,不检查字段 取消源 @@ -3505,7 +3510,7 @@ 执行特殊方法 - + 执行变量写入 diff --git a/framework/ThingsGateway.Application/Workers/Collect/CollectDeviceCore.cs b/framework/ThingsGateway.Application/Workers/Collect/CollectDeviceCore.cs index b0ffb181c..486647e3c 100644 --- a/framework/ThingsGateway.Application/Workers/Collect/CollectDeviceCore.cs +++ b/framework/ThingsGateway.Application/Workers/Collect/CollectDeviceCore.cs @@ -712,46 +712,46 @@ public class CollectDeviceCore /// 执行变量写入 /// /// - internal async Task InVokeWriteAsync(DeviceVariableRunTime deviceVariable, JToken value, CancellationToken token) + internal async Task> InVokeWriteAsync(Dictionary writeInfoLists, CancellationToken token) { try { await easyLock.WaitAsync(); if (IsShareChannel) _driver.InitDataAdapter(); + Dictionary results = new(); + foreach (var deviceVariable in writeInfoLists.Keys) + { + if (!string.IsNullOrEmpty(deviceVariable.WriteExpressions)) + { + var jToken = writeInfoLists[deviceVariable]; + object rawdata; + if (jToken is JValue jValue) + { + rawdata = jValue.Value; + } + else + { + rawdata = jToken.ToString(); + } + object data; + try + { + data = deviceVariable.WriteExpressions.GetExpressionsResult(rawdata); + writeInfoLists[deviceVariable] = JToken.FromObject(data); + } + catch (Exception ex) + { + results.Add(deviceVariable.Name, new OperResult(deviceVariable.Name + " 转换写入表达式失败:" + ex.Message)); + } + } - if (!string.IsNullOrEmpty(deviceVariable.WriteExpressions)) - { - var jToken = value; - object rawdata; - if (jToken is JValue jValue) - { - rawdata = jValue.Value; - } - else - { - rawdata = jToken.ToString(); - } - object data; - try - { - data = deviceVariable.WriteExpressions.GetExpressionsResult(rawdata); - var result = await _driver.WriteValueAsync(deviceVariable, JToken.FromObject(data), token); - return result; - } - catch (Exception ex) - { - return new OperResult(deviceVariable.Name + " 转换写入表达式失败:" + ex.Message); - } } - else - { - var result = await _driver.WriteValueAsync(deviceVariable, value, token); - return result; - } - } - catch (Exception ex) - { - return (new OperResult(ex)); + var result = await _driver.WriteValuesAsync(writeInfoLists. + Where(a => !results.Any(b => b.Key == a.Key.Name)). + ToDictionary(item => item.Key, item => item.Value), + token); + + return result; } finally { @@ -759,6 +759,8 @@ public class CollectDeviceCore } } + + /// /// 执行轮询特殊方法,并设置变量值 /// diff --git a/framework/ThingsGateway.Blazor/Page/DeviceVariableRunTimePage.razor.cs b/framework/ThingsGateway.Blazor/Page/DeviceVariableRunTimePage.razor.cs index 8ccb3d194..0419acad2 100644 --- a/framework/ThingsGateway.Blazor/Page/DeviceVariableRunTimePage.razor.cs +++ b/framework/ThingsGateway.Blazor/Page/DeviceVariableRunTimePage.razor.cs @@ -149,10 +149,18 @@ public partial class DeviceVariableRunTimePage private EventCallback WriteValueAsync; private async Task OnWriteValueAsync(DeviceVariableRunTime tag, string value) { - var data = await RpcCore?.InvokeDeviceMethodAsync($"BLAZOR-{UserResoures.CurrentUser.Account}-{App.HttpContext.Connection.RemoteIpAddress.MapToIPv4()}", new KeyValuePair(tag.Name, value), true); - if (!data.IsSuccess) + var data = await RpcCore?.InvokeDeviceMethodAsync($"BLAZOR-{UserResoures.CurrentUser.Account}-{App.HttpContext.Connection.RemoteIpAddress.MapToIPv4()}", + + new Dictionary() + { + + { tag.Name, value} + } + + , true); + if (data.Count > 0 && !data.FirstOrDefault().Value.IsSuccess) { - throw new(data.Message); + throw new(data.FirstOrDefault().Value.Message); } } private async Task WriteAsync(DeviceVariableRunTime collectVariableRunTime) diff --git a/framework/ThingsGateway.Plugin/Foundataion/ThingsGateway.Foundation.Adapter.Modbus/Modbus/ModbusAddress.cs b/framework/ThingsGateway.Plugin/Foundataion/ThingsGateway.Foundation.Adapter.Modbus/Modbus/ModbusAddress.cs index 27dbe826a..0746972d7 100644 --- a/framework/ThingsGateway.Plugin/Foundataion/ThingsGateway.Foundation.Adapter.Modbus/Modbus/ModbusAddress.cs +++ b/framework/ThingsGateway.Plugin/Foundataion/ThingsGateway.Foundation.Adapter.Modbus/Modbus/ModbusAddress.cs @@ -66,7 +66,8 @@ public class ModbusAddress : DeviceAddressBase Length = result.Content.Length; AddressStart = result.Content.AddressStart; ReadFunction = result.Content.ReadFunction; - Station = result.Content.Station; + if (result.Content.Station > 0) + Station = result.Content.Station; WriteFunction = result.Content.WriteFunction; } } @@ -81,9 +82,10 @@ public class ModbusAddress : DeviceAddressBase try { - ModbusAddress modbusAddress = new ModbusAddress(); - - modbusAddress.Length = length; + ModbusAddress modbusAddress = new() + { + Length = length + }; if (address.IndexOf(';') < 0) { Address(address); diff --git a/framework/ThingsGateway.Plugin/Foundataion/ThingsGateway.Foundation.Adapter.OPCDA/OPCDAClient.cs b/framework/ThingsGateway.Plugin/Foundataion/ThingsGateway.Foundation.Adapter.OPCDA/OPCDAClient.cs index 942680e67..1e2adadd9 100644 --- a/framework/ThingsGateway.Plugin/Foundataion/ThingsGateway.Foundation.Adapter.OPCDA/OPCDAClient.cs +++ b/framework/ThingsGateway.Plugin/Foundataion/ThingsGateway.Foundation.Adapter.OPCDA/OPCDAClient.cs @@ -301,50 +301,94 @@ public class OPCDAClient : DisposableObject return OPCNode?.ToString(); } + /// - /// 写入值 + /// 批量写入值 /// - /// 写入 - /// /// - public OperResult WriteItem(string valueName, JToken value) + public Dictionary WriteItem(Dictionary writeInfos) { + Dictionary results = new(); if (PrivateConnect()) { try { - var group = Groups.FirstOrDefault(it => it.OpcItems.Any(a => a.ItemID == valueName)); - if (group == null) - return new OperResult("不存在该变量" + valueName); - var item = group.OpcItems.Where(it => it.ItemID == valueName).FirstOrDefault(); - int[] serverHandle = new int[1] { item.ServerHandle }; - int[] PErrors = new int[1]; - var jtoken = value; - var rank = jtoken.CalculateActualValueRank(); - object rawWriteValue; - switch (rank) + var valueGroup = writeInfos.GroupBy(itemId => + { + var group = Groups.FirstOrDefault(it => it.OpcItems.Any(a => a.ItemID == itemId.Key)); + return group; + }).ToList(); + + + + + foreach (var item1 in valueGroup) { - case -1: - rawWriteValue = ((JValue)jtoken).Value; - break; - default: - var jarray = ((JArray)jtoken); - rawWriteValue = jarray.Select(j => (object)j).ToArray(); - break; + if (item1.Key == null) + { + foreach (var item2 in item1) + { + results.Add(item2.Key, new OperResult("不存在该变量" + item2.Key)); + } + } + else + { + List ServerHandles = new(); + List Values = new(); + foreach (var item2 in item1) + { + var opcItem = item1.Key.OpcItems.Where(it => it.ItemID == item2.Key).FirstOrDefault(); + ServerHandles.Add(opcItem.ServerHandle); + var jtoken = item2.Value; + var rank = jtoken.CalculateActualValueRank(); + object rawWriteValue; + switch (rank) + { + case -1: + rawWriteValue = ((JValue)jtoken).Value; + break; + default: + var jarray = ((JArray)jtoken); + rawWriteValue = jarray.Select(j => (object)j).ToArray(); + break; + } + + Values.Add(rawWriteValue); + + } + + var result = item1.Key.Write(Values.ToArray(), ServerHandles.ToArray(), out var PErrors); + var data = item1.ToList(); + for (int i = 0; i < data.Count; i++) + { + results.Add(data[i].Key, PErrors[i] == 0 ? OperResult.CreateSuccessResult() : new OperResult("错误代码:" + PErrors[i])); + } + } } - object[] Value = new object[1] { rawWriteValue }; - var result = group.Write(Value, serverHandle, out PErrors); - return result; + return results; } catch (Exception ex) { - return new OperResult(ex.Message); + var keys = writeInfos.Keys.ToList(); + foreach (var item in keys) + { + results.AddOrUpdate(item, new(ex)); + } + return results; + } } - return new OperResult(); + else + { + var keys = writeInfos.Keys.ToList(); + foreach (var item in keys) + { + results.AddOrUpdate(item, new("未初始化连接")); + } + return results; + } } - /// protected override void Dispose(bool disposing) { diff --git a/framework/ThingsGateway.Plugin/Foundataion/ThingsGateway.Foundation.Adapter.OPCDA/ThingsGateway.Foundation.Adapter.OPCDA.xml b/framework/ThingsGateway.Plugin/Foundataion/ThingsGateway.Foundation.Adapter.OPCDA/ThingsGateway.Foundation.Adapter.OPCDA.xml index af5fbc8de..d379fcf01 100644 --- a/framework/ThingsGateway.Plugin/Foundataion/ThingsGateway.Foundation.Adapter.OPCDA/ThingsGateway.Foundation.Adapter.OPCDA.xml +++ b/framework/ThingsGateway.Plugin/Foundataion/ThingsGateway.Foundation.Adapter.OPCDA/ThingsGateway.Foundation.Adapter.OPCDA.xml @@ -520,12 +520,10 @@ - + - 写入值 + 批量写入值 - 写入 - diff --git a/framework/ThingsGateway.Plugin/Foundataion/ThingsGateway.Foundation.Adapter.OPCUA/OPCUAClient/OPCUAClient.cs b/framework/ThingsGateway.Plugin/Foundataion/ThingsGateway.Foundation.Adapter.OPCUA/OPCUAClient/OPCUAClient.cs index 626ba1a4b..0465e2a37 100644 --- a/framework/ThingsGateway.Plugin/Foundataion/ThingsGateway.Foundation.Adapter.OPCUA/OPCUAClient/OPCUAClient.cs +++ b/framework/ThingsGateway.Plugin/Foundataion/ThingsGateway.Foundation.Adapter.OPCUA/OPCUAClient/OPCUAClient.cs @@ -521,29 +521,33 @@ public class OPCUAClient : DisposableObject /// /// 异步写opc标签 /// - public async Task WriteNodeAsync(string tag, JToken value, CancellationToken token = default) + public async Task> WriteNodeAsync(Dictionary writeInfoLists, CancellationToken token = default) { + Dictionary results = new(); try { - WriteValue valueToWrite = new() + WriteValueCollection valuesToWrite = new(); + foreach (var item in writeInfoLists) { - NodeId = new NodeId(tag), - AttributeId = Attributes.Value, - }; - var variableNode = (VariableNode)ReadNode(tag.ToString(), false); - await typeSystem.LoadType(variableNode.DataType, true, true); - var dataValue = JsonUtils.Decode( - m_session.MessageContext, - variableNode.DataType, - TypeInfo.GetBuiltInType(variableNode.DataType, m_session.SystemContext.TypeTable), - value.CalculateActualValueRank(), - value - ); - valueToWrite.Value = dataValue; - WriteValueCollection valuesToWrite = new() - { - valueToWrite - }; + WriteValue valueToWrite = new() + { + NodeId = new NodeId(item.Key), + AttributeId = Attributes.Value, + }; + var variableNode = (VariableNode)ReadNode(item.Key, false); + await typeSystem.LoadType(variableNode.DataType, true, true); + var dataValue = JsonUtils.Decode( + m_session.MessageContext, + variableNode.DataType, + TypeInfo.GetBuiltInType(variableNode.DataType, m_session.SystemContext.TypeTable), + item.Value.CalculateActualValueRank(), + item.Value + ); + valueToWrite.Value = dataValue; + + valuesToWrite.Add(valueToWrite); + } + var result = await m_session.WriteAsync( requestHeader: null, @@ -551,15 +555,29 @@ public class OPCUAClient : DisposableObject ClientBase.ValidateResponse(result.Results, valuesToWrite); ClientBase.ValidateDiagnosticInfos(result.DiagnosticInfos, valuesToWrite); - if (!StatusCode.IsGood(result.Results[0])) + + + var keys = writeInfoLists.Keys.ToList(); + for (int i = 0; i < keys.Count; i++) { - return new OperResult(result.Results[0].ToString()); + if (!StatusCode.IsGood(result.Results[i])) + results.Add(keys[i], new(result.Results[i].ToString())); + else + { + results.Add(keys[i], OperResult.CreateSuccessResult()); + } } - return OperResult.CreateSuccessResult(); + + return results; } catch (Exception ex) { - return new OperResult(ex); + var keys = writeInfoLists.Keys.ToList(); + foreach (var item in keys) + { + results.Add(item, new(ex)); + } + return results; } } diff --git a/framework/ThingsGateway.Plugin/Foundataion/ThingsGateway.Foundation.Adapter.OPCUA/ThingsGateway.Foundation.Adapter.OPCUA.xml b/framework/ThingsGateway.Plugin/Foundataion/ThingsGateway.Foundation.Adapter.OPCUA/ThingsGateway.Foundation.Adapter.OPCUA.xml index 599efd157..d31e31235 100644 --- a/framework/ThingsGateway.Plugin/Foundataion/ThingsGateway.Foundation.Adapter.OPCUA/ThingsGateway.Foundation.Adapter.OPCUA.xml +++ b/framework/ThingsGateway.Plugin/Foundataion/ThingsGateway.Foundation.Adapter.OPCUA/ThingsGateway.Foundation.Adapter.OPCUA.xml @@ -375,7 +375,7 @@ 从服务器读取值 - + 异步写opc标签 diff --git a/framework/ThingsGateway.Plugin/ThingsGateway.Modbus/ModbusRtu/ModbusRtu.cs b/framework/ThingsGateway.Plugin/ThingsGateway.Modbus/ModbusRtu/ModbusRtu.cs index cb59a41db..1339711e6 100644 --- a/framework/ThingsGateway.Plugin/ThingsGateway.Modbus/ModbusRtu/ModbusRtu.cs +++ b/framework/ThingsGateway.Plugin/ThingsGateway.Modbus/ModbusRtu/ModbusRtu.cs @@ -10,8 +10,6 @@ //------------------------------------------------------------------------------ #endregion -using Newtonsoft.Json.Linq; - using ThingsGateway.Application; using ThingsGateway.Foundation; using ThingsGateway.Foundation.Serial; @@ -22,6 +20,8 @@ namespace ThingsGateway.Modbus; public class ModbusRtu : CollectBase { private readonly ModbusRtuProperty driverPropertys = new(); + /// + protected override IReadWriteDevice PLC => _plc; private ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu _plc; /// public override Type DriverDebugUIType => typeof(ModbusRtuDebugDriverPage); @@ -74,12 +74,6 @@ public class ModbusRtu : CollectBase return deviceVariables.LoadSourceRead(_plc, driverPropertys.MaxPack); } - /// - public override async Task WriteValueAsync(DeviceVariableRunTime deviceVariable, JToken value, CancellationToken token) - { - return await _plc.WriteAsync(deviceVariable.VariableAddress, deviceVariable.DataType, value.ToString(), token); - } - /// protected override void Init(CollectDeviceRunTime device, object client = null) { diff --git a/framework/ThingsGateway.Plugin/ThingsGateway.Modbus/ModbusRtuOverTcp/ModbusRtuOverTcp.cs b/framework/ThingsGateway.Plugin/ThingsGateway.Modbus/ModbusRtuOverTcp/ModbusRtuOverTcp.cs index 89a47f9de..f00f6ce4a 100644 --- a/framework/ThingsGateway.Plugin/ThingsGateway.Modbus/ModbusRtuOverTcp/ModbusRtuOverTcp.cs +++ b/framework/ThingsGateway.Plugin/ThingsGateway.Modbus/ModbusRtuOverTcp/ModbusRtuOverTcp.cs @@ -10,8 +10,6 @@ //------------------------------------------------------------------------------ #endregion -using Newtonsoft.Json.Linq; - using ThingsGateway.Application; using ThingsGateway.Foundation; @@ -22,6 +20,8 @@ public class ModbusRtuOverTcp : CollectBase { private readonly ModbusRtuOverTcpProperty driverPropertys = new(); + /// + protected override IReadWriteDevice PLC => _plc; private ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp _plc; /// public override Type DriverDebugUIType => typeof(ModbusRtuOverTcpDebugDriverPage); @@ -61,11 +61,7 @@ public class ModbusRtuOverTcp : CollectBase return deviceVariables.LoadSourceRead(_plc, driverPropertys.MaxPack); } - /// - public override async Task WriteValueAsync(DeviceVariableRunTime deviceVariable, JToken value, CancellationToken token) - { - return await _plc.WriteAsync(deviceVariable.VariableAddress, deviceVariable.DataType, value.ToString(), token); - } + /// protected override void Dispose(bool disposing) diff --git a/framework/ThingsGateway.Plugin/ThingsGateway.Modbus/ModbusRtuOverUdp/ModbusRtuOverUdp.cs b/framework/ThingsGateway.Plugin/ThingsGateway.Modbus/ModbusRtuOverUdp/ModbusRtuOverUdp.cs index b6ba73d86..9e0d53222 100644 --- a/framework/ThingsGateway.Plugin/ThingsGateway.Modbus/ModbusRtuOverUdp/ModbusRtuOverUdp.cs +++ b/framework/ThingsGateway.Plugin/ThingsGateway.Modbus/ModbusRtuOverUdp/ModbusRtuOverUdp.cs @@ -10,8 +10,6 @@ //------------------------------------------------------------------------------ #endregion -using Newtonsoft.Json.Linq; - using ThingsGateway.Application; using ThingsGateway.Foundation; @@ -22,6 +20,8 @@ namespace ThingsGateway.Modbus; public class ModbusRtuOverUdp : CollectBase { private readonly ModbusRtuOverUdpProperty driverPropertys = new(); + /// + protected override IReadWriteDevice PLC => _plc; private ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp _plc; /// public override Type DriverDebugUIType => typeof(ModbusRtuOverUdpDebugDriverPage); @@ -65,11 +65,7 @@ public class ModbusRtuOverUdp : CollectBase return deviceVariables.LoadSourceRead(_plc, driverPropertys.MaxPack); } - /// - public override async Task WriteValueAsync(DeviceVariableRunTime deviceVariable, JToken value, CancellationToken token) - { - return await _plc.WriteAsync(deviceVariable.VariableAddress, deviceVariable.DataType, value.ToString(), token); - } + /// protected override void Dispose(bool disposing) diff --git a/framework/ThingsGateway.Plugin/ThingsGateway.Modbus/ModbusServer/ModbusServer.cs b/framework/ThingsGateway.Plugin/ThingsGateway.Modbus/ModbusServer/ModbusServer.cs index 3c91e047a..e0202ba7c 100644 --- a/framework/ThingsGateway.Plugin/ThingsGateway.Modbus/ModbusServer/ModbusServer.cs +++ b/framework/ThingsGateway.Plugin/ThingsGateway.Modbus/ModbusServer/ModbusServer.cs @@ -201,16 +201,25 @@ public class ModbusServer : UpLoadBase if (Enum.TryParse(type, out DataTypeEnum result)) { var resultTask1 = RpcCore.InvokeDeviceMethodAsync($"{nameof(ModbusServer)}-{CurDevice.Name}-{client.IP + ":" + client.Port}", - new(tag.Value.Name, thingsGatewayBitConverter.GetDynamicDataFormBytes(addressStr ?? string.Empty, result.GetSystemType(), bytes).ToString())); + new Dictionary +{ + {tag.Value.Name, thingsGatewayBitConverter.GetDynamicDataFormBytes(addressStr ?? string.Empty, result.GetSystemType(), bytes).ToString()}, +} + + ); var result1 = resultTask1.ConfigureAwait(true).GetAwaiter().GetResult(); - return result1; + return result1.FirstOrDefault().Value; } else { var resultTask1 = RpcCore.InvokeDeviceMethodAsync($"{nameof(ModbusServer)}-{CurDevice.Name}-{client.IP + ":" + client.Port}", -new(tag.Value.Name, thingsGatewayBitConverter.GetDynamicDataFormBytes(addressStr ?? string.Empty, tag.Value.DataType, bytes).ToString())); + new Dictionary +{ + {tag.Value.Name, thingsGatewayBitConverter.GetDynamicDataFormBytes(addressStr ?? string.Empty, result.GetSystemType(), bytes).ToString()}, +} + ); var result1 = resultTask1.ConfigureAwait(true).GetAwaiter().GetResult(); - return result1; + return result1.FirstOrDefault().Value; } } catch (Exception ex) diff --git a/framework/ThingsGateway.Plugin/ThingsGateway.Modbus/ModbusTcp/ModbusTcp.cs b/framework/ThingsGateway.Plugin/ThingsGateway.Modbus/ModbusTcp/ModbusTcp.cs index 2d64dff6b..ad855e59d 100644 --- a/framework/ThingsGateway.Plugin/ThingsGateway.Modbus/ModbusTcp/ModbusTcp.cs +++ b/framework/ThingsGateway.Plugin/ThingsGateway.Modbus/ModbusTcp/ModbusTcp.cs @@ -10,8 +10,6 @@ //------------------------------------------------------------------------------ #endregion -using Newtonsoft.Json.Linq; - using ThingsGateway.Application; using ThingsGateway.Foundation; @@ -22,6 +20,8 @@ namespace ThingsGateway.Modbus; /// public class ModbusTcp : CollectBase { + /// + protected override IReadWriteDevice PLC => _plc; private ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp _plc; private readonly ModbusTcpProperty driverPropertys = new(); @@ -67,11 +67,7 @@ public class ModbusTcp : CollectBase return deviceVariables.LoadSourceRead(_plc, driverPropertys.MaxPack); } - /// - public override async Task WriteValueAsync(DeviceVariableRunTime deviceVariable, JToken value, CancellationToken token) - { - return await _plc.WriteAsync(deviceVariable.VariableAddress, deviceVariable.DataType, value.ToString(), token); - } + /// protected override void Dispose(bool disposing) diff --git a/framework/ThingsGateway.Plugin/ThingsGateway.Modbus/ModbusUdp/ModbusUdp.cs b/framework/ThingsGateway.Plugin/ThingsGateway.Modbus/ModbusUdp/ModbusUdp.cs index bf4d5160e..3af0275dd 100644 --- a/framework/ThingsGateway.Plugin/ThingsGateway.Modbus/ModbusUdp/ModbusUdp.cs +++ b/framework/ThingsGateway.Plugin/ThingsGateway.Modbus/ModbusUdp/ModbusUdp.cs @@ -10,8 +10,6 @@ //------------------------------------------------------------------------------ #endregion -using Newtonsoft.Json.Linq; - using ThingsGateway.Application; using ThingsGateway.Foundation; @@ -21,6 +19,8 @@ namespace ThingsGateway.Modbus; /// public class ModbusUdp : CollectBase { + /// + protected override IReadWriteDevice PLC => _plc; private ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp _plc; private readonly ModbusUdpProperty driverPropertys = new(); @@ -66,11 +66,7 @@ public class ModbusUdp : CollectBase return deviceVariables.LoadSourceRead(_plc, driverPropertys.MaxPack); } - /// - public override async Task WriteValueAsync(DeviceVariableRunTime deviceVariable, JToken value, CancellationToken token) - { - return await _plc.WriteAsync(deviceVariable.VariableAddress, deviceVariable.DataType, value.ToString(), token); - } + /// protected override void Dispose(bool disposing) diff --git a/framework/ThingsGateway.Plugin/ThingsGateway.Modbus/ThingsGateway.Modbus.xml b/framework/ThingsGateway.Plugin/ThingsGateway.Modbus/ThingsGateway.Modbus.xml index f3e8cf9bf..5d51efdd6 100644 --- a/framework/ThingsGateway.Plugin/ThingsGateway.Modbus/ThingsGateway.Modbus.xml +++ b/framework/ThingsGateway.Plugin/ThingsGateway.Modbus/ThingsGateway.Modbus.xml @@ -7,6 +7,9 @@ + + + @@ -34,9 +37,6 @@ - - - @@ -116,6 +116,9 @@ + + + @@ -143,9 +146,6 @@ - - - @@ -168,6 +168,9 @@ + + + @@ -200,9 +203,6 @@ - - - @@ -400,6 +400,9 @@ + + + @@ -427,9 +430,6 @@ - - - @@ -507,6 +507,9 @@ + + + @@ -534,9 +537,6 @@ - - - diff --git a/framework/ThingsGateway.Plugin/ThingsGateway.Mqtt/IotSharp/IotSharpClient.cs b/framework/ThingsGateway.Plugin/ThingsGateway.Mqtt/IotSharp/IotSharpClient.cs index d4ef94a4b..27fa36520 100644 --- a/framework/ThingsGateway.Plugin/ThingsGateway.Mqtt/IotSharp/IotSharpClient.cs +++ b/framework/ThingsGateway.Plugin/ThingsGateway.Mqtt/IotSharp/IotSharpClient.cs @@ -340,9 +340,7 @@ GetPropertyValue(tag, nameof(variablePropertys.VariableRpcEnable)).ToBoolean() && driverPropertys.DeviceRpcEnable; if (rpcEnable == true) { - var result = await _rpcCore.InvokeDeviceMethodAsync(ToString() + "-" + rpcrequestid, item); - results.Add(item.Key, result); } else @@ -356,6 +354,12 @@ GetPropertyValue(tag, nameof(variablePropertys.VariableRpcEnable)).ToBoolean() results.Add(item.Key, new("不存在该变量")); } } + + var result = await _rpcCore.InvokeDeviceMethodAsync(ToString() + "-" + rpcrequestid, nameValue + .Where(a => !results.Any(b => b.Key == a.Key)) + .ToDictionary(a => a.Key, a => a.Value)); + + results.AddRange(result); rpcResponse = new() { DeviceId = rpcdevicename, diff --git a/framework/ThingsGateway.Plugin/ThingsGateway.Mqtt/MqttClient.cs b/framework/ThingsGateway.Plugin/ThingsGateway.Mqtt/MqttClient.cs index f7b11137a..2353fcbf3 100644 --- a/framework/ThingsGateway.Plugin/ThingsGateway.Mqtt/MqttClient.cs +++ b/framework/ThingsGateway.Plugin/ThingsGateway.Mqtt/MqttClient.cs @@ -405,40 +405,48 @@ public class MqttClient : UpLoadBase return; if (arg.ApplicationMessage.Topic != driverPropertys.RpcWriteTopic) return; - var rpcData = Encoding.UTF8.GetString(arg.ApplicationMessage.PayloadSegment).FromJson(); - if (rpcData == null) + var rpcDatas = Encoding.UTF8.GetString(arg.ApplicationMessage.PayloadSegment).FromJson(); + if (rpcDatas == null) return; - MqttRpcResult mqttRpcResult = new(); + MqttRpcResult mqttRpcResult = new() { RpcId = rpcDatas.RpcId, Success = true }; try { - var nv = rpcData.Adapt>(); - var tag = _uploadVariables.FirstOrDefault(a => a.Name == nv.Key); - if (tag != null) + foreach (var rpcData in rpcDatas.WriteInfos) { - var rpcEnable = GetPropertyValue(tag, nameof(variablePropertys.VariableRpcEnable)).ToBoolean(); - if (rpcEnable == true) + + var tag = _uploadVariables.FirstOrDefault(a => a.Name == rpcData.Key); + if (tag != null) { - var result = await _rpcCore.InvokeDeviceMethodAsync(ToString() + "-" + arg.ClientId, nv); - - mqttRpcResult = new() { Message = result.Message, RpcId = rpcData.RpcId, Success = result.IsSuccess }; + var rpcEnable = GetPropertyValue(tag, nameof(variablePropertys.VariableRpcEnable)).ToBoolean(); + if (rpcEnable == true) + { + } + else + { + mqttRpcResult.Success = false; + mqttRpcResult.Message.Add(rpcData.Key, new("权限不足,变量不支持写入")); + } } else { - mqttRpcResult = new() { Message = "权限不足,变量不支持写入", RpcId = rpcData.RpcId, Success = false }; + mqttRpcResult.Success = false; + mqttRpcResult.Message.Add(rpcData.Key, new("不存在该变量")); } + } + + var result = await _rpcCore.InvokeDeviceMethodAsync(ToString() + "-" + arg.ClientId, + rpcDatas.WriteInfos.Where( + a => !mqttRpcResult.Message.Any(b => b.Key == a.Key)).ToDictionary(a => a.Key, a => a.Value)); + + mqttRpcResult.Message.AddRange(result); + mqttRpcResult.Success = !mqttRpcResult.Message.Any(a => !a.Value.IsSuccess); - } - else - { - mqttRpcResult = new() { Message = "不存在该变量", RpcId = rpcData.RpcId, Success = false }; - } } catch (Exception ex) { LogMessage?.LogWarning(ex, ToString()); - mqttRpcResult = new() { Message = "Failed", RpcId = rpcData.RpcId, Success = false }; } try { diff --git a/framework/ThingsGateway.Plugin/ThingsGateway.Mqtt/MqttRpcNameVaueWithId.cs b/framework/ThingsGateway.Plugin/ThingsGateway.Mqtt/MqttRpcNameVaueWithId.cs index 9a0f75f9b..e4bb98706 100644 --- a/framework/ThingsGateway.Plugin/ThingsGateway.Mqtt/MqttRpcNameVaueWithId.cs +++ b/framework/ThingsGateway.Plugin/ThingsGateway.Mqtt/MqttRpcNameVaueWithId.cs @@ -10,6 +10,10 @@ //------------------------------------------------------------------------------ #endregion +using System.Collections.Generic; + +using ThingsGateway.Foundation; + namespace ThingsGateway.Mqtt; /// @@ -22,13 +26,9 @@ public class MqttRpcNameVaueWithId /// public string RpcId { get; set; } /// - /// 通常是变量名称 + /// "WriteInfos":{"test":"1"} /// - public string Key { get; set; } - /// - /// 值 - /// - public string Value { get; set; } + public Dictionary WriteInfos { get; set; } = new(); } /// /// MqttRpc输出 @@ -42,7 +42,7 @@ public class MqttRpcResult /// /// 消息 /// - public string Message { get; set; } + public Dictionary Message { get; set; } = new(); /// /// 是否成功 /// diff --git a/framework/ThingsGateway.Plugin/ThingsGateway.Mqtt/MqttServer.cs b/framework/ThingsGateway.Plugin/ThingsGateway.Mqtt/MqttServer.cs index 168c2acf0..4a35c3ec4 100644 --- a/framework/ThingsGateway.Plugin/ThingsGateway.Mqtt/MqttServer.cs +++ b/framework/ThingsGateway.Plugin/ThingsGateway.Mqtt/MqttServer.cs @@ -260,41 +260,46 @@ public class MqttServer : UpLoadBase return; if (arg.ApplicationMessage.Topic != driverPropertys.RpcWriteTopic) return; - var rpcData = Encoding.UTF8.GetString(arg.ApplicationMessage.PayloadSegment).FromJson(); - if (rpcData == null) + var rpcDatas = Encoding.UTF8.GetString(arg.ApplicationMessage.PayloadSegment).FromJson(); + if (rpcDatas == null) return; - MqttRpcResult mqttRpcResult = new(); + MqttRpcResult mqttRpcResult = new() { RpcId = rpcDatas.RpcId, Success = true }; try { - var nv = rpcData.Adapt>(); - var tag = _uploadVariables.FirstOrDefault(a => a.Name == nv.Key); - if (tag != null) + foreach (var rpcData in rpcDatas.WriteInfos) { - var rpcEnable = GetPropertyValue(tag, nameof(variablePropertys.VariableRpcEnable)).ToBoolean(); - if (rpcEnable == true) + + var tag = _uploadVariables.FirstOrDefault(a => a.Name == rpcData.Key); + if (tag != null) { - var result = await _rpcCore.InvokeDeviceMethodAsync(ToString() + "-" + IdWithName[arg.ClientId], nv); - - mqttRpcResult = new() { Message = result.Message, RpcId = rpcData.RpcId, Success = result.IsSuccess }; + var rpcEnable = GetPropertyValue(tag, nameof(variablePropertys.VariableRpcEnable)).ToBoolean(); + if (rpcEnable == true) + { + } + else + { + mqttRpcResult.Success = false; + mqttRpcResult.Message.Add(rpcData.Key, new("权限不足,变量不支持写入")); + } } else { - mqttRpcResult = new() { Message = "权限不足,变量不支持写入", RpcId = rpcData.RpcId, Success = false }; + mqttRpcResult.Success = false; + mqttRpcResult.Message.Add(rpcData.Key, new("不存在该变量")); } + } - } - else - { - mqttRpcResult = new() { Message = "不存在该变量", RpcId = rpcData.RpcId, Success = false }; - } + var result = await _rpcCore.InvokeDeviceMethodAsync(ToString() + "-" + arg.ClientId, + rpcDatas.WriteInfos.Where( + a => !mqttRpcResult.Message.Any(b => b.Key == a.Key)).ToDictionary(a => a.Key, a => a.Value)); + + mqttRpcResult.Message.AddRange(result); + mqttRpcResult.Success = !mqttRpcResult.Message.Any(a => !a.Value.IsSuccess); } - catch (Exception ex) { LogMessage?.LogWarning(ex, ToString()); - mqttRpcResult = new() { Message = "Failed", RpcId = rpcData.RpcId, Success = false }; - } try { @@ -302,7 +307,7 @@ public class MqttServer : UpLoadBase .WithTopic($"{driverPropertys.RpcSubTopic}") .WithPayload(mqttRpcResult.ToJson()).Build(); await _mqttServer.InjectApplicationMessage( - new InjectedMqttApplicationMessage(variableMessage)); + new InjectedMqttApplicationMessage(variableMessage)); } catch { diff --git a/framework/ThingsGateway.Plugin/ThingsGateway.Mqtt/ThingsGateway.Mqtt.xml b/framework/ThingsGateway.Plugin/ThingsGateway.Mqtt/ThingsGateway.Mqtt.xml index 7d0607653..f256f00f0 100644 --- a/framework/ThingsGateway.Plugin/ThingsGateway.Mqtt/ThingsGateway.Mqtt.xml +++ b/framework/ThingsGateway.Plugin/ThingsGateway.Mqtt/ThingsGateway.Mqtt.xml @@ -294,14 +294,9 @@ 标识 - + - 通常是变量名称 - - - - - 值 + "WriteInfos":{"test":"1"} diff --git a/framework/ThingsGateway.Plugin/ThingsGateway.OPCDA/OPCDAClient.cs b/framework/ThingsGateway.Plugin/ThingsGateway.OPCDA/OPCDAClient.cs index ad523d279..4dc852f48 100644 --- a/framework/ThingsGateway.Plugin/ThingsGateway.OPCDA/OPCDAClient.cs +++ b/framework/ThingsGateway.Plugin/ThingsGateway.OPCDA/OPCDAClient.cs @@ -35,7 +35,7 @@ public class OPCDAClient : CollectBase { internal CollectDeviceRunTime Device; - internal ThingsGateway.Foundation.Adapter.OPCDA.OPCDAClient PLC = null; + internal ThingsGateway.Foundation.Adapter.OPCDA.OPCDAClient _plc = null; private readonly OPCDAClientProperty driverPropertys = new(); private ConcurrentList _deviceVariables = new(); @@ -50,17 +50,20 @@ public class OPCDAClient : CollectBase /// public override ThingsGatewayBitConverter ThingsGatewayBitConverter { get; } = new(EndianType.Little); + /// + protected override IReadWriteDevice PLC => null; + /// public override Task AfterStopAsync() { - PLC?.Disconnect(); + _plc?.Disconnect(); return Task.CompletedTask; } /// public override async Task BeforStartAsync(CancellationToken token) { - PLC.Connect(); + _plc.Connect(); await Task.CompletedTask; } @@ -69,14 +72,14 @@ public class OPCDAClient : CollectBase { } /// - public override bool IsConnected() => PLC?.IsConnected == true; + public override bool IsConnected() => _plc?.IsConnected == true; /// public override List LoadSourceRead(List deviceVariables) { _deviceVariables = new(deviceVariables); if (deviceVariables.Count > 0) { - var result = PLC.AddItemsWithSave(deviceVariables.Select(a => a.VariableAddress).ToList()); + var result = _plc.AddItemsWithSave(deviceVariables.Select(a => a.VariableAddress).ToList()); var sourVars = result?.Select( it => { @@ -98,26 +101,31 @@ public class OPCDAClient : CollectBase public override async Task> ReadSourceAsync(DeviceVariableSourceRead deviceVariableSourceRead, CancellationToken token) { await Task.CompletedTask; - var result = PLC.ReadItemsWithGroup(deviceVariableSourceRead.Address); + var result = _plc.ReadItemsWithGroup(deviceVariableSourceRead.Address); return result.Copy(); } + /// - public override Task WriteValueAsync(DeviceVariableRunTime deviceVariable, JToken value, CancellationToken token) + public override Task> WriteValuesAsync(Dictionary writeInfoLists, CancellationToken token) { - var result = PLC.WriteItem(deviceVariable.VariableAddress, value); - return Task.FromResult(result); + var result = _plc.WriteItem(writeInfoLists.ToDictionary(a => a.Key.VariableAddress, a => a.Value)); + return Task.FromResult(result.ToDictionary(a => + { + return writeInfoLists.Keys.FirstOrDefault(b => b.VariableAddress == a.Key).Name; + }, a => a.Value)); } + /// /// protected override void Dispose(bool disposing) { - if (PLC != null) - PLC.DataChangedHandler -= DataChangedHandler; - PLC?.Disconnect(); - PLC?.SafeDispose(); - PLC = null; + if (_plc != null) + _plc.DataChangedHandler -= DataChangedHandler; + _plc?.Disconnect(); + _plc?.SafeDispose(); + _plc = null; base.Dispose(disposing); } /// @@ -134,12 +142,12 @@ public class OPCDAClient : CollectBase ActiveSubscribe = driverPropertys.ActiveSubscribe, CheckRate = driverPropertys.CheckRate }; - if (PLC == null) + if (_plc == null) { - PLC = new(LogMessage); - PLC.DataChangedHandler += DataChangedHandler; + _plc = new(LogMessage); + _plc.DataChangedHandler += DataChangedHandler; } - PLC.Init(opcNode); + _plc.Init(opcNode); } /// diff --git a/framework/ThingsGateway.Plugin/ThingsGateway.OPCDA/OPCDAClientDebugDriverPage.razor.cs b/framework/ThingsGateway.Plugin/ThingsGateway.OPCDA/OPCDAClientDebugDriverPage.razor.cs index 6d1ac7a41..f91f0588d 100644 --- a/framework/ThingsGateway.Plugin/ThingsGateway.OPCDA/OPCDAClientDebugDriverPage.razor.cs +++ b/framework/ThingsGateway.Plugin/ThingsGateway.OPCDA/OPCDAClientDebugDriverPage.razor.cs @@ -18,6 +18,7 @@ using Microsoft.AspNetCore.Components; using System; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using ThingsGateway.Admin.Blazor.Core; @@ -147,14 +148,19 @@ public partial class OPCDAClientDebugDriverPage : IDisposable { try { - var data = _plc.WriteItem(defalutDebugDriverPage.Address, defalutDebugDriverPage.WriteValue); - if (data.IsSuccess) + var data = _plc.WriteItem( + new() + { + {defalutDebugDriverPage.Address, defalutDebugDriverPage.WriteValue } + } + ); + if (data.Values.FirstOrDefault().IsSuccess) { - defalutDebugDriverPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Information, SysDateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset) + " - 写入" + data.Message)); + defalutDebugDriverPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Information, SysDateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset) + " - 写入" + data.Values.FirstOrDefault().Message)); } else { - defalutDebugDriverPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Warning, SysDateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset) + " - " + data.Message)); + defalutDebugDriverPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Warning, SysDateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset) + " - " + data.Values.FirstOrDefault().Message)); } } catch (Exception ex) diff --git a/framework/ThingsGateway.Plugin/ThingsGateway.OPCDA/ThingsGateway.OPCDA.xml b/framework/ThingsGateway.Plugin/ThingsGateway.OPCDA/ThingsGateway.OPCDA.xml index f6c254fb2..d80c89f33 100644 --- a/framework/ThingsGateway.Plugin/ThingsGateway.OPCDA/ThingsGateway.OPCDA.xml +++ b/framework/ThingsGateway.Plugin/ThingsGateway.OPCDA/ThingsGateway.OPCDA.xml @@ -40,6 +40,9 @@ + + + @@ -58,7 +61,7 @@ - + diff --git a/framework/ThingsGateway.Plugin/ThingsGateway.OPCUA/OPCUAClient/OPCUAClient.cs b/framework/ThingsGateway.Plugin/ThingsGateway.OPCUA/OPCUAClient/OPCUAClient.cs index b8e575846..1942f2b6e 100644 --- a/framework/ThingsGateway.Plugin/ThingsGateway.OPCUA/OPCUAClient/OPCUAClient.cs +++ b/framework/ThingsGateway.Plugin/ThingsGateway.OPCUA/OPCUAClient/OPCUAClient.cs @@ -36,7 +36,13 @@ namespace ThingsGateway.OPCUA; /// public class OPCUAClient : CollectBase { + internal Foundation.Adapter.OPCUA.OPCUAClient _plc = null; + internal CollectDeviceRunTime Device; readonly PeriodicTimer _periodicTimer = new(TimeSpan.FromSeconds(60)); + private readonly OPCUAClientProperty driverPropertys = new(); + + private List _deviceVariables = new(); + /// /// OPCUA客户端 /// @@ -45,33 +51,6 @@ public class OPCUAClient : CollectBase _ = RunTimerAsync(); } - private async Task RunTimerAsync() - { - while (await _periodicTimer.WaitForNextTickAsync()) - { - if (PLC != null && PLC.Session == null) - { - try - { - await PLC.ConnectAsync(); - } - catch (Exception ex) - { - LogMessage.Exception(ex); - } - } - } - - } - - internal CollectDeviceRunTime Device; - - internal Foundation.Adapter.OPCUA.OPCUAClient PLC = null; - - private List _deviceVariables = new(); - - private readonly OPCUAClientProperty driverPropertys = new(); - /// public override Type DriverDebugUIType => typeof(OPCUAClientDebugDriverPage); @@ -83,18 +62,23 @@ public class OPCUAClient : CollectBase /// public override ThingsGatewayBitConverter ThingsGatewayBitConverter { get; } = new(EndianType.Little); + + /// + protected override IReadWriteDevice PLC => null; + /// public override Task AfterStopAsync() { - PLC?.Disconnect(); + _plc?.Disconnect(); return Task.CompletedTask; } /// public override async Task BeforStartAsync(CancellationToken token) { - await PLC?.ConnectAsync(); + await _plc?.ConnectAsync(); } + /// public override void InitDataAdapter() { @@ -104,14 +88,14 @@ public class OPCUAClient : CollectBase public override bool IsConnected() { - return PLC.Connected; + return _plc.Connected; } /// public override List LoadSourceRead(List deviceVariables) { _deviceVariables = deviceVariables; - PLC.Variables.AddRange(deviceVariables.Select(a => a.VariableAddress).ToList()); + _plc.Variables.AddRange(deviceVariables.Select(a => a.VariableAddress).ToList()); var dataLists = deviceVariables.ChunkTrivialBetter(driverPropertys.GroupSize); var dataResult = new List(); foreach (var variable in dataLists) @@ -131,7 +115,7 @@ public class OPCUAClient : CollectBase /// public override async Task> ReadSourceAsync(DeviceVariableSourceRead deviceVariableSourceRead, CancellationToken token) { - var result = await PLC.ReadJTokenValueAsync(deviceVariableSourceRead.DeviceVariables.Select(a => a.VariableAddress).ToArray(), token); + var result = await _plc.ReadJTokenValueAsync(deviceVariableSourceRead.DeviceVariables.Select(a => a.VariableAddress).ToArray(), token); foreach (var data in result) { if (!token.IsCancellationRequested) @@ -184,26 +168,33 @@ public class OPCUAClient : CollectBase } } + /// - public override async Task WriteValueAsync(DeviceVariableRunTime deviceVariable, JToken value, CancellationToken token) + public override async Task> WriteValuesAsync(Dictionary writeInfoLists, CancellationToken token) { - var result = await PLC.WriteNodeAsync(deviceVariable.VariableAddress, value, token); - return result; + var result = await _plc.WriteNodeAsync(writeInfoLists.ToDictionary(a => a.Key.VariableAddress, a => a.Value), token); + return result.ToDictionary(a => + { + return writeInfoLists.Keys.FirstOrDefault(b => b.VariableAddress == a.Key)?.Name; + } + , a => a.Value); + } /// protected override void Dispose(bool disposing) { _periodicTimer?.Dispose(); - if (PLC != null) + if (_plc != null) { - PLC.DataChangedHandler -= DataChangedHandler; - PLC.Disconnect(); - PLC.SafeDispose(); - PLC = null; + _plc.DataChangedHandler -= DataChangedHandler; + _plc.Disconnect(); + _plc.SafeDispose(); + _plc = null; } base.Dispose(disposing); } + /// protected override void Init(CollectDeviceRunTime device, object client = null) { @@ -220,13 +211,13 @@ public class OPCUAClient : CollectBase UserName = driverPropertys.UserName, Password = driverPropertys.Password }; - if (PLC == null) + if (_plc == null) { - PLC = new(LogMessage); - PLC.DataChangedHandler += DataChangedHandler; + _plc = new(LogMessage); + _plc.DataChangedHandler += DataChangedHandler; } - PLC.OPCNode = opcNode; + _plc.OPCNode = opcNode; } /// @@ -252,7 +243,7 @@ public class OPCUAClient : CollectBase return; } //尝试固定点位的数据类型 - var type = TypeInfo.GetSystemType(TypeInfo.GetBuiltInType(data.variableNode.DataType, PLC.Session.SystemContext.TypeTable), data.variableNode.ValueRank); + var type = TypeInfo.GetSystemType(TypeInfo.GetBuiltInType(data.variableNode.DataType, _plc.Session.SystemContext.TypeTable), data.variableNode.ValueRank); var itemReads = _deviceVariables.Where(it => it.VariableAddress == data.variableNode.NodeId).ToList(); @@ -296,4 +287,23 @@ public class OPCUAClient : CollectBase LogMessage?.LogWarning(ex, ToString()); } } + + private async Task RunTimerAsync() + { + while (await _periodicTimer.WaitForNextTickAsync()) + { + if (_plc != null && _plc.Session == null) + { + try + { + await _plc.ConnectAsync(); + } + catch (Exception ex) + { + LogMessage.Exception(ex); + } + } + } + + } } diff --git a/framework/ThingsGateway.Plugin/ThingsGateway.OPCUA/OPCUAClient/OPCUAClientDebugDriverPage.razor.cs b/framework/ThingsGateway.Plugin/ThingsGateway.OPCUA/OPCUAClient/OPCUAClientDebugDriverPage.razor.cs index 7a63a2149..4bb97b95e 100644 --- a/framework/ThingsGateway.Plugin/ThingsGateway.OPCUA/OPCUAClient/OPCUAClientDebugDriverPage.razor.cs +++ b/framework/ThingsGateway.Plugin/ThingsGateway.OPCUA/OPCUAClient/OPCUAClientDebugDriverPage.razor.cs @@ -21,6 +21,7 @@ using Newtonsoft.Json.Linq; using Opc.Ua; using System; +using System.Linq; using System.Threading.Tasks; using ThingsGateway.Admin.Blazor.Core; @@ -157,14 +158,19 @@ public partial class OPCUAClientDebugDriverPage { if (_plc.Connected) { - var data = await _plc.WriteNodeAsync(defalutDebugDriverPage.Address, JToken.Parse(defalutDebugDriverPage.WriteValue)); - if (data.IsSuccess) + var data = await _plc.WriteNodeAsync( + new() + { + { defalutDebugDriverPage.Address, JToken.Parse(defalutDebugDriverPage.WriteValue)} + } + ); + if (data.Values.FirstOrDefault().IsSuccess) { defalutDebugDriverPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Information, SysDateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset) + " - " + " - 写入成功")); } else { - defalutDebugDriverPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Warning, SysDateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset) + " - " + " - 写入失败 " + data.Message)); + defalutDebugDriverPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Warning, SysDateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset) + " - " + " - 写入失败 " + data.Values.FirstOrDefault().Message)); } } else diff --git a/framework/ThingsGateway.Plugin/ThingsGateway.OPCUA/OPCUAServer/ThingsGatewayNodeManager.cs b/framework/ThingsGateway.Plugin/ThingsGateway.OPCUA/OPCUAServer/ThingsGatewayNodeManager.cs index 17665980a..55020a9b4 100644 --- a/framework/ThingsGateway.Plugin/ThingsGateway.OPCUA/OPCUAServer/ThingsGatewayNodeManager.cs +++ b/framework/ThingsGateway.Plugin/ThingsGateway.OPCUA/OPCUAServer/ThingsGatewayNodeManager.cs @@ -490,16 +490,22 @@ src.IsOnline ? StatusCodes.Good : StatusCodes.Bad); //仅当指定了值时才将值写入 if (variable.Value != null) { - var nv = new KeyValuePair(variable.SymbolicName, value?.ToString()); - var result = _rpcCore.InvokeDeviceMethodAsync("OPCUASERVER-" + context1?.OperationContext?.Session?.Identity?.DisplayName, nv).ConfigureAwait(true).GetAwaiter().GetResult(); - if (result.IsSuccess) + var result = _rpcCore.InvokeDeviceMethodAsync("OPCUASERVER-" + context1?.OperationContext?.Session?.Identity?.DisplayName, + new() + { + { variable.SymbolicName, value?.ToString() } + } + + + ).ConfigureAwait(true).GetAwaiter().GetResult(); + if (result.Values.FirstOrDefault().IsSuccess) { return StatusCodes.Good; } else { - return new(StatusCodes.BadWaitingForResponse, result.Message); + return new(StatusCodes.BadWaitingForResponse, result.Values.FirstOrDefault().Message); } } } diff --git a/framework/ThingsGateway.Plugin/ThingsGateway.OPCUA/ThingsGateway.OPCUA.xml b/framework/ThingsGateway.Plugin/ThingsGateway.OPCUA/ThingsGateway.OPCUA.xml index 30abcdfcf..e89df54d9 100644 --- a/framework/ThingsGateway.Plugin/ThingsGateway.OPCUA/ThingsGateway.OPCUA.xml +++ b/framework/ThingsGateway.Plugin/ThingsGateway.OPCUA/ThingsGateway.OPCUA.xml @@ -50,6 +50,9 @@ + + + @@ -68,7 +71,7 @@ - + diff --git a/framework/ThingsGateway.Plugin/ThingsGateway.Siemens/Siemens.cs b/framework/ThingsGateway.Plugin/ThingsGateway.Siemens/Siemens.cs index e337d57ec..8987fbea3 100644 --- a/framework/ThingsGateway.Plugin/ThingsGateway.Siemens/Siemens.cs +++ b/framework/ThingsGateway.Plugin/ThingsGateway.Siemens/Siemens.cs @@ -10,8 +10,6 @@ //------------------------------------------------------------------------------ #endregion -using Newtonsoft.Json.Linq; - using System.Collections.Generic; using System.Text; using System.Threading; @@ -31,6 +29,8 @@ public abstract class Siemens : CollectBase /// PLC /// protected SiemensS7PLC _plc; + /// + protected override IReadWriteDevice PLC => _plc; /// /// SiemensProperty /// @@ -124,11 +124,7 @@ public abstract class Siemens : CollectBase - /// - public override async Task WriteValueAsync(DeviceVariableRunTime deviceVariable, JToken value, CancellationToken token) - { - return await _plc.WriteAsync(deviceVariable.VariableAddress, deviceVariable.DataType, value.ToString(), token); - } + /// protected override void Dispose(bool disposing) diff --git a/framework/ThingsGateway.Plugin/ThingsGateway.Siemens/ThingsGateway.Siemens.xml b/framework/ThingsGateway.Plugin/ThingsGateway.Siemens/ThingsGateway.Siemens.xml index 63d828acc..f288e8779 100644 --- a/framework/ThingsGateway.Plugin/ThingsGateway.Siemens/ThingsGateway.Siemens.xml +++ b/framework/ThingsGateway.Plugin/ThingsGateway.Siemens/ThingsGateway.Siemens.xml @@ -88,6 +88,9 @@ PLC + + + SiemensProperty @@ -138,9 +141,6 @@ - - -