diff --git a/Modbus.Net/Modbus.Net.Core/Modbus.Net.Core.csproj b/Modbus.Net/Modbus.Net.Core/Modbus.Net.Core.csproj index eec5ef8..c473162 100644 --- a/Modbus.Net/Modbus.Net.Core/Modbus.Net.Core.csproj +++ b/Modbus.Net/Modbus.Net.Core/Modbus.Net.Core.csproj @@ -18,6 +18,8 @@ git False True + True + True @@ -59,6 +61,7 @@ + \ No newline at end of file diff --git a/Modbus.Net/Modbus.Net.Modbus/AddressTranslatorModbus.cs b/Modbus.Net/Modbus.Net.Modbus/AddressTranslatorModbus.cs index b4b6ec4..05ca20d 100644 --- a/Modbus.Net/Modbus.Net.Modbus/AddressTranslatorModbus.cs +++ b/Modbus.Net/Modbus.Net.Modbus/AddressTranslatorModbus.cs @@ -3,10 +3,33 @@ using System.Linq; namespace Modbus.Net.Modbus { + public abstract class ModbusTranslatorBase : AddressTranslator + { + /// + /// 地址转换 + /// + /// 格式化的地址 + /// 是否为读取,是为读取,否为写入 + /// 是否只写入一个数据 + /// 翻译后的地址 + public abstract AddressDef AddressTranslate(string address, bool isRead, bool isSingle); + + /// + /// 地址转换 + /// + /// 格式化的地址 + /// 是否为读取,是为读取,否为写入 + /// 翻译后的地址 + public override AddressDef AddressTranslate(string address, bool isRead) + { + return AddressTranslate(address, isRead, false); + } + } + /// /// 南大奥拓NA200H数据单元翻译器 /// - public class AddressTranslatorNA200H : AddressTranslator + public class AddressTranslatorNA200H : ModbusTranslatorBase { /// /// 读功能码 @@ -21,7 +44,7 @@ namespace Modbus.Net.Modbus /// /// 写功能码 /// - protected Dictionary WriteFunctionCodeDictionary; + protected Dictionary<(string, bool), AreaOutputDef> WriteFunctionCodeDictionary; /// /// 构造器 @@ -104,10 +127,10 @@ namespace Modbus.Net.Modbus new AreaOutputDef {Code = (int) ModbusProtocalReadDataFunctionCode.ReadHoldRegister, AreaWidth = 2} } }; - WriteFunctionCodeDictionary = new Dictionary + WriteFunctionCodeDictionary = new Dictionary<(string, bool), AreaOutputDef> { { - "Q", + ("Q", false), new AreaOutputDef { Code = (int) ModbusProtocalWriteDataFunctionCode.WriteMultiCoil, @@ -115,7 +138,7 @@ namespace Modbus.Net.Modbus } }, { - "M", + ("M", false), new AreaOutputDef { Code = (int) ModbusProtocalWriteDataFunctionCode.WriteMultiCoil, @@ -123,7 +146,7 @@ namespace Modbus.Net.Modbus } }, { - "N", + ("N", false), new AreaOutputDef { Code = (int) ModbusProtocalWriteDataFunctionCode.WriteMultiCoil, @@ -131,7 +154,7 @@ namespace Modbus.Net.Modbus } }, { - "MW", + ("MW", false), new AreaOutputDef { Code = (int) ModbusProtocalWriteDataFunctionCode.WriteMultiRegister, @@ -139,7 +162,7 @@ namespace Modbus.Net.Modbus } }, { - "NW", + ("NW", false), new AreaOutputDef { Code = (int) ModbusProtocalWriteDataFunctionCode.WriteMultiRegister, @@ -147,12 +170,60 @@ namespace Modbus.Net.Modbus } }, { - "QW", + ("QW", false), new AreaOutputDef { Code = (int) ModbusProtocalWriteDataFunctionCode.WriteMultiRegister, AreaWidth = 2 } + }, + { + ("Q", true), + new AreaOutputDef + { + Code = (int) ModbusProtocalWriteDataFunctionCode.WriteSingleCoil, + AreaWidth = 0.125 + } + }, + { + ("M", true), + new AreaOutputDef + { + Code = (int) ModbusProtocalWriteDataFunctionCode.WriteSingleCoil, + AreaWidth = 0.125 + } + }, + { + ("N", true), + new AreaOutputDef + { + Code = (int) ModbusProtocalWriteDataFunctionCode.WriteSingleCoil, + AreaWidth = 0.125 + } + }, + { + ("MW", true), + new AreaOutputDef + { + Code = (int) ModbusProtocalWriteDataFunctionCode.WriteSingleRegister, + AreaWidth = 2 + } + }, + { + ("NW", true), + new AreaOutputDef + { + Code = (int) ModbusProtocalWriteDataFunctionCode.WriteSingleRegister, + AreaWidth = 2 + } + }, + { + ("QW", true), + new AreaOutputDef + { + Code = (int) ModbusProtocalWriteDataFunctionCode.WriteSingleRegister, + AreaWidth = 2 + } } }; } @@ -162,8 +233,9 @@ namespace Modbus.Net.Modbus /// /// 格式化的地址 /// 是否为读取,是为读取,否为写入 + /// 是否只写入一个数据 /// 翻译后的地址 - public override AddressDef AddressTranslate(string address, bool isRead) + public override AddressDef AddressTranslate(string address, bool isRead, bool isSingle) { address = address.ToUpper(); var splitString = address.Split(' '); @@ -191,7 +263,7 @@ namespace Modbus.Net.Modbus : new AddressDef { AreaString = head, - Area = WriteFunctionCodeDictionary[head].Code, + Area = WriteFunctionCodeDictionary[(head, isSingle)].Code, Address = TransDictionary[head] + int.Parse(tail) - 1, SubAddress = int.Parse(sub) }; @@ -211,7 +283,7 @@ namespace Modbus.Net.Modbus /// /// Modbus数据单元翻译器 /// - public class AddressTranslatorModbus : AddressTranslator + public class AddressTranslatorModbus : ModbusTranslatorBase { /// /// 读功能码 @@ -221,7 +293,7 @@ namespace Modbus.Net.Modbus /// /// 写功能码 /// - protected Dictionary WriteFunctionCodeDictionary; + protected Dictionary<(string, bool), AreaOutputDef> WriteFunctionCodeDictionary; /// /// 构造器 @@ -255,10 +327,10 @@ namespace Modbus.Net.Modbus new AreaOutputDef {Code = (int) ModbusProtocalReadDataFunctionCode.ReadHoldRegister, AreaWidth = 2} } }; - WriteFunctionCodeDictionary = new Dictionary + WriteFunctionCodeDictionary = new Dictionary<(string, bool), AreaOutputDef> { { - "0X", + ("0X", false), new AreaOutputDef { Code = (int) ModbusProtocalWriteDataFunctionCode.WriteMultiCoil, @@ -266,12 +338,28 @@ namespace Modbus.Net.Modbus } }, { - "4X", + ("4X", false), new AreaOutputDef { Code = (int) ModbusProtocalWriteDataFunctionCode.WriteMultiRegister, AreaWidth = 2 } + }, + { + ("0X", true), + new AreaOutputDef + { + Code = (int) ModbusProtocalWriteDataFunctionCode.WriteSingleCoil, + AreaWidth = 0.125 + } + }, + { + ("4X", true), + new AreaOutputDef + { + Code = (int) ModbusProtocalWriteDataFunctionCode.WriteSingleRegister, + AreaWidth = 2 + } } }; } @@ -281,8 +369,9 @@ namespace Modbus.Net.Modbus /// /// 格式化的地址 /// 是否为读取,是为读取,否为写入 + /// 是否只写入一个数据 /// 翻译后的地址 - public override AddressDef AddressTranslate(string address, bool isRead) + public override AddressDef AddressTranslate(string address, bool isRead, bool isSingle) { address = address.ToUpper(); var splitString = address.Split(' '); @@ -310,7 +399,7 @@ namespace Modbus.Net.Modbus : new AddressDef { AreaString = head, - Area = WriteFunctionCodeDictionary[head].Code, + Area = WriteFunctionCodeDictionary[(head, isSingle)].Code, Address = int.Parse(tail) - 1, SubAddress = int.Parse(sub) }; diff --git a/Modbus.Net/Modbus.Net.Modbus/Modbus.Net.Modbus.csproj b/Modbus.Net/Modbus.Net.Modbus/Modbus.Net.Modbus.csproj index 93e0701..8e9385a 100644 --- a/Modbus.Net/Modbus.Net.Modbus/Modbus.Net.Modbus.csproj +++ b/Modbus.Net/Modbus.Net.Modbus/Modbus.Net.Modbus.csproj @@ -16,7 +16,10 @@ https://github.com/parallelbgls/Modbus.Net git hardware communicate protocal modbus Delian + False True + True + True diff --git a/Modbus.Net/Modbus.Net.Modbus/ModbusProtocal.cs b/Modbus.Net/Modbus.Net.Modbus/ModbusProtocal.cs index 9f2a763..ee2e3e4 100644 --- a/Modbus.Net/Modbus.Net.Modbus/ModbusProtocal.cs +++ b/Modbus.Net/Modbus.Net.Modbus/ModbusProtocal.cs @@ -385,6 +385,138 @@ namespace Modbus.Net.Modbus } } + /// + /// 写数据输入 + /// + public class WriteSingleDataModbusInputStruct : IInputStruct + { + /// + /// 构造函数 + /// + /// 从站号 + /// 开始地址 + /// 写入的数据 + /// 地址翻译器 + /// 端格式 + public WriteSingleDataModbusInputStruct(byte slaveAddress, string startAddress, object writeValue, + ModbusTranslatorBase addressTranslator, Endian endian) + { + SlaveAddress = slaveAddress; + var translateAddress = addressTranslator.AddressTranslate(startAddress, false, true); + FunctionCode = (byte) translateAddress.Area; + StartAddress = (ushort) translateAddress.Address; + int a = 0, b = 0; + var writeByteValue = + FunctionCode == (byte) ModbusProtocalWriteDataFunctionCode.WriteSingleCoil + ? ((bool) writeValue + ? new byte[] {0xFF, 0x00} + : new byte[] {0x00, 0xFF}) + : ValueHelper.GetInstance(endian).GetBytes(ushort.Parse(writeValue.ToString())); + WriteValue = writeByteValue; + } + + + /// + /// 从站号 + /// + public byte SlaveAddress { get; } + + /// + /// 功能码 + /// + public byte FunctionCode { get; } + + /// + /// 开始地址 + /// + public ushort StartAddress { get; } + + /// + /// 写入的数据 + /// + public byte[] WriteValue { get; } + } + + /// + /// 写数据输出 + /// + public class WriteSingleDataModbusOutputStruct : IOutputStruct + { + /// + /// 构造函数 + /// + /// 从站号 + /// 功能码 + /// 开始地址 + /// 写入的数据 + public WriteSingleDataModbusOutputStruct(byte slaveAddress, byte functionCode, + ushort startAddress, object writeValue) + { + SlaveAddress = slaveAddress; + FunctionCode = functionCode; + StartAddress = startAddress; + WriteValue = writeValue; + } + + /// + /// 从站号 + /// + public byte SlaveAddress { get; private set; } + + /// + /// 功能码 + /// + public byte FunctionCode { get; private set; } + + /// + /// 开始地址 + /// + public ushort StartAddress { get; private set; } + + /// + /// 写入的数据 + /// + public object WriteValue { get; private set; } + } + + /// + /// 写多个寄存器协议 + /// + public class WriteSingleDataModbusProtocal : ProtocalUnit + { + /// + /// 格式化 + /// + /// 写寄存器参数 + /// 写寄存器协议核心 + public override byte[] Format(IInputStruct message) + { + var r_message = (WriteSingleDataModbusInputStruct) message; + var dataValue = Format(r_message.WriteValue); + var formattingBytes = Format(r_message.SlaveAddress, r_message.FunctionCode, + r_message.StartAddress, dataValue); + return formattingBytes; + } + + /// + /// 反格式化 + /// + /// 设备返回的信息 + /// 当前反格式化的位置 + /// 反格式化的信息 + public override IOutputStruct Unformat(byte[] messageBytes, ref int flag) + { + var slaveAddress = ValueHelper.GetInstance(Endian).GetByte(messageBytes, ref flag); + var functionCode = ValueHelper.GetInstance(Endian).GetByte(messageBytes, ref flag); + var startAddress = ValueHelper.GetInstance(Endian).GetUShort(messageBytes, ref flag); + var writeValue = ValueHelper.GetInstance(Endian).GetUShort(messageBytes, ref flag); + var returnValue = functionCode == (byte)ModbusProtocalWriteDataFunctionCode.WriteSingleCoil + ? (object)(writeValue == 0xFF00) : writeValue; + return new WriteSingleDataModbusOutputStruct(slaveAddress, functionCode, startAddress, + returnValue); + } + } + #endregion #region 读PLC时间 diff --git a/Modbus.Net/Modbus.Net.Modbus/ModbusUtility.cs b/Modbus.Net/Modbus.Net.Modbus/ModbusUtility.cs index 8aba0ad..7180119 100644 --- a/Modbus.Net/Modbus.Net.Modbus/ModbusUtility.cs +++ b/Modbus.Net/Modbus.Net.Modbus/ModbusUtility.cs @@ -25,10 +25,21 @@ namespace Modbus.Net.Modbus Ascii = 2 } + public interface IUtilityMethodWriteSingle : IUtilityMethod + { + /// + /// 写数据 + /// + /// 起始地址 + /// 需要设置的数据 + /// 设置是否成功 + Task SetSingleDataAsync(string startAddress, object setContent); + } + /// /// Modbus基础Api入口 /// - public class ModbusUtility : BaseUtility, IUtilityMethodTime + public class ModbusUtility : BaseUtility, IUtilityMethodTime, IUtilityMethodWriteSingle { /// /// Modbus协议类型 @@ -250,5 +261,29 @@ namespace Modbus.Net.Modbus return false; } } + + /// + /// 写数据 + /// + /// 起始地址 + /// 需要设置的数据 + /// 设置是否成功 + public async Task SetSingleDataAsync(string startAddress, object setContent) + { + try + { + var inputStruct = new WriteSingleDataModbusInputStruct(SlaveAddress, startAddress, setContent, + (ModbusTranslatorBase)AddressTranslator, Endian); + var outputStruct = await + Wrapper.SendReceiveAsync(Wrapper[typeof(WriteSingleDataModbusProtocal)], + inputStruct); + return outputStruct?.WriteValue.ToString() == setContent.ToString(); + } + catch (Exception e) + { + Log.Error(e, $"ModbusUtility -> SetSingleDatas: {ConnectionString} error"); + return false; + } + } } } \ No newline at end of file diff --git a/Modbus.Net/Modbus.Net.OPC/Modbus.Net.OPC.csproj b/Modbus.Net/Modbus.Net.OPC/Modbus.Net.OPC.csproj index 02d839e..df1c440 100644 --- a/Modbus.Net/Modbus.Net.OPC/Modbus.Net.OPC.csproj +++ b/Modbus.Net/Modbus.Net.OPC/Modbus.Net.OPC.csproj @@ -16,7 +16,10 @@ git hardware communicate protocal OPC DA Delian False + False True + True + True diff --git a/Modbus.Net/Modbus.Net.Siemens/Modbus.Net.Siemens.csproj b/Modbus.Net/Modbus.Net.Siemens/Modbus.Net.Siemens.csproj index 68e6b31..fc381ae 100644 --- a/Modbus.Net/Modbus.Net.Siemens/Modbus.Net.Siemens.csproj +++ b/Modbus.Net/Modbus.Net.Siemens/Modbus.Net.Siemens.csproj @@ -15,7 +15,10 @@ https://github.com/parallelbgls/Modbus.Net git hardware communicate protocal Siemens profinet Delian + False True + True + True diff --git a/Modbus.Net/Modbus.Net/Modbus.Net.csproj b/Modbus.Net/Modbus.Net/Modbus.Net.csproj index e24f0ac..5acf3d2 100644 --- a/Modbus.Net/Modbus.Net/Modbus.Net.csproj +++ b/Modbus.Net/Modbus.Net/Modbus.Net.csproj @@ -16,7 +16,10 @@ Copyright 2017 Hangzhou Delian IoT Science Technology Co.,Ltd. hardware communicate protocal Delian git + False True + True + True @@ -54,6 +57,7 @@ + diff --git a/Modbus.Net/src/Base.Common/BaseMachine.cs b/Modbus.Net/src/Base.Common/BaseMachine.cs index e247760..ac83417 100644 --- a/Modbus.Net/src/Base.Common/BaseMachine.cs +++ b/Modbus.Net/src/Base.Common/BaseMachine.cs @@ -243,7 +243,7 @@ namespace Modbus.Net //获取数据 var datas = await - BaseUtility.InvokeUtilityMethod>("GetDatasAsync", + BaseUtility.GetUtilityMethods().GetDatasAsync( AddressFormater.FormatAddress(communicateAddress.Area, communicateAddress.Address, communicateAddress.SubAddress), (int) @@ -446,7 +446,7 @@ namespace Modbus.Net communicateAddress.Address); var datasReturn = - await BaseUtility.InvokeUtilityMethod>("GetDatasAsync", + await BaseUtility.GetUtilityMethods().GetDatasAsync( AddressFormater.FormatAddress(communicateAddress.Area, communicateAddress.Address, 0), (int) Math.Ceiling(communicateAddress.GetCount * @@ -536,7 +536,7 @@ namespace Modbus.Net } //写入数据 await - BaseUtility.InvokeUtilityMethod>("SetDatasAsync", addressStart, + BaseUtility.GetUtilityMethods().SetDatasAsync(addressStart, valueHelper.ByteArrayToObjectArray(datas, new KeyValuePair(communicateAddress.DataType, communicateAddress.GetCount))); } @@ -591,25 +591,13 @@ namespace Modbus.Net /// public string ConnectionToken => BaseUtility.ConnectionToken; - /// - /// 调用Machine中的方法 - /// - /// Machine实现的接口名称 - /// 返回值的类型 - /// 方法的名称 - /// 方法的参数 - /// - public TReturnType InvokeMachineMethod(string methodName, - params object[] parameters) where TMachineMethod : IMachineMethod + public TMachineMethod GetMachineMethods() where TMachineMethod : class, IMachineMethod { if (this is TMachineMethod) { - var t = typeof(TMachineMethod); - var returnValue = t.GetRuntimeMethod(methodName, parameters.Select(p => p.GetType()).ToArray()) - .Invoke(this, parameters); - return (TReturnType) returnValue; + return this as TMachineMethod; } - throw new InvalidCastException($"Machine interface {nameof(TMachineMethod)} not implemented"); + return null; } /// @@ -876,15 +864,11 @@ namespace Modbus.Net IUtilityProperty BaseUtility { get; } /// - /// 调用Machine中的方法 + /// 获取设备的方法集合 /// - /// Machine实现的接口名称 - /// 返回值的类型 - /// 方法的名称 - /// 方法的参数 - /// - TReturnType InvokeMachineMethod(string methodName, - params object[] parameters) where TMachineMethod : IMachineMethod; + /// 方法集合的类型 + /// 设备的方法集合 + TMachineMethod GetMachineMethods() where TMachineMethod : class, IMachineMethod; /// /// 连接设备 diff --git a/Modbus.Net/src/Base.Common/BaseUtility.cs b/Modbus.Net/src/Base.Common/BaseUtility.cs index fb1cd9b..f6a7797 100644 --- a/Modbus.Net/src/Base.Common/BaseUtility.cs +++ b/Modbus.Net/src/Base.Common/BaseUtility.cs @@ -277,24 +277,17 @@ namespace Modbus.Net } /// - /// 调用Utility中的方法 + /// 返回Utility的方法集合 /// - /// Utility实现的接口名称 - /// 返回值的类型 - /// 方法的名称 - /// 方法的参数 - /// - public TReturnType InvokeUtilityMethod(string methodName, - params object[] parameters) where TUtilityMethod : IUtilityMethod + /// Utility方法集合类型 + /// Utility方法集合 + public TUtilityMethod GetUtilityMethods() where TUtilityMethod : class, IUtilityMethod { if (this is TUtilityMethod) { - var t = typeof(TUtilityMethod); - var returnValue = t.GetRuntimeMethod(methodName, parameters.Select(p => p.GetType()).ToArray(), false) - .Invoke(this, parameters); - return (TReturnType) returnValue; + return this as TUtilityMethod; } - throw new InvalidCastException($"Utility interface {nameof(TUtilityMethod)} not implemented"); + return null; } /// @@ -348,14 +341,10 @@ namespace Modbus.Net bool Disconnect(); /// - /// 调用Utility中的方法 + /// 返回Utility的方法集合 /// - /// Utility实现的接口名称 - /// 返回值的类型 - /// 方法的名称 - /// 方法的参数 - /// - TReturnType InvokeUtilityMethod(string methodName, - params object[] parameters) where TUtilityMethod : IUtilityMethod; + /// Utility方法集合类型 + /// Utility方法集合 + TUtilityMethod GetUtilityMethods() where TUtilityMethod : class, IUtilityMethod; } } \ No newline at end of file diff --git a/Modbus.Net/src/Base.Common/TaskManager.cs b/Modbus.Net/src/Base.Common/TaskManager.cs index 49a7b5d..3ee6be2 100644 --- a/Modbus.Net/src/Base.Common/TaskManager.cs +++ b/Modbus.Net/src/Base.Common/TaskManager.cs @@ -416,8 +416,8 @@ namespace Modbus.Net cts.CancelAfter(TimeSpan.FromMilliseconds(timeoutTime)); var ans = await tasks.StartNew( - async () => await machine.InvokeMachineMethod>>("GetDatasAsync", + async () => await machine.GetMachineMethods() + .GetDatasAsync( getDataType).WithCancellation(cts.Token)).Unwrap(); return new DataReturnDef { @@ -467,8 +467,8 @@ namespace Modbus.Net cts.CancelAfter(TimeSpan.FromMilliseconds(timeoutTime)); var ans = await tasks.StartNew( - async () => await machine.InvokeMachineMethod>("SetDatasAsync", setDataType, parameters[0] + async () => await machine.GetMachineMethods(). + SetDatasAsync(setDataType, (Dictionary)parameters[0] ).WithCancellation(cts.Token)).Unwrap(); return ans; }; diff --git a/Tests/Modbus.Net.Tests/MachineMethodTest.cs b/Tests/Modbus.Net.Tests/MachineMethodTest.cs index 9ff7dc4..8d99598 100644 --- a/Tests/Modbus.Net.Tests/MachineMethodTest.cs +++ b/Tests/Modbus.Net.Tests/MachineMethodTest.cs @@ -26,9 +26,9 @@ namespace Modbus.Net.Tests public async Task InvokeUtility() { BaseMachine baseMachine = new ModbusMachine(ModbusType.Tcp, "192.168.3.12", null, true, 2, 0); - var success = await baseMachine.BaseUtility.InvokeUtilityMethod>("SetTimeAsync", DateTime.Now); + var success = await baseMachine.BaseUtility.GetUtilityMethods().SetTimeAsync(DateTime.Now); Assert.AreEqual(success, true); - var time = await baseMachine.BaseUtility.InvokeUtilityMethod>("GetTimeAsync"); + var time = await baseMachine.BaseUtility.GetUtilityMethods().GetTimeAsync(); Assert.AreEqual((time.ToUniversalTime() - DateTime.Now.ToUniversalTime()).Seconds < 10, true); baseMachine.Disconnect(); } @@ -48,7 +48,7 @@ namespace Modbus.Net.Tests DataType = typeof(bool) } }, true, 2, 0); - var success = await baseMachine.InvokeMachineMethod>("SetDatasAsync", + var success = await baseMachine.GetMachineMethods().SetDatasAsync( MachineSetDataType.Address, new Dictionary { @@ -57,9 +57,9 @@ namespace Modbus.Net.Tests } }); Assert.AreEqual(success, true); - var datas = await baseMachine.InvokeMachineMethod>>("GetDatasAsync", MachineGetDataType.Address); + var datas = await baseMachine.GetMachineMethods().GetDatasAsync(MachineGetDataType.Address); Assert.AreEqual(datas["0X 1.0"].PlcValue, 1); - success = await baseMachine.InvokeMachineMethod>("SetDatasAsync", + success = await baseMachine.GetMachineMethods().SetDatasAsync( MachineSetDataType.Address, new Dictionary {