├── .gitignore ├── .travis.yml ├── LICENSE.txt ├── NModbus.chm ├── NModbus4.IntegrationTests ├── CustomMessages │ ├── CustomReadHoldingRegistersRequest.cs │ ├── CustomReadHoldingRegistersResponse.cs │ ├── CustomWriteMultipleRegistersRequest.cs │ └── CustomWriteMultipleRegistersResponse.cs ├── EnronFixture.cs ├── ModbusIpMasterFixture.cs ├── ModbusMasterFixture.cs ├── ModbusSerialMasterFixture.cs ├── NModbus4.IntegrationTests.csproj ├── NModbus4.IntegrationTests.ruleset ├── NModbusSerialAsciiMasterFixture.cs ├── NModbusSerialAsciiMasterJamodSerialAsciiSlaveFixture.cs ├── NModbusSerialAsciiMasterNModbusSerialAsciiSlaveFixture.cs ├── NModbusSerialRtuMasterDl06SlaveFixture.cs ├── NModbusSerialRtuMasterFixture.cs ├── NModbusSerialRtuMasterNModbusSerialRtuSlaveFixture.cs ├── NModbusSerialRtuSlaveFixture.cs ├── NModbusTcpMasterJamodTcpSlaveFixture.cs ├── NModbusTcpMasterNModbusTcpSlaveFixture.cs ├── NModbusTcpSlaveFixture.cs ├── NModbusUdpMasterNModbusUdpSlaveFixture.cs ├── NModbusUdpSlaveFixture.cs ├── Properties │ └── AssemblyInfo.cs ├── TestCases.cs └── packages.config ├── NModbus4.Serial ├── NModbus4.Serial.xproj ├── Properties │ └── AssemblyInfo.cs ├── SerialPortAdapter.cs └── project.json ├── NModbus4.TestDriver ├── App.config ├── NModbus4.TestDriver.csproj ├── Program.cs ├── Properties │ └── AssemblyInfo.cs └── packages.config ├── NModbus4.UnitTests ├── Data │ ├── BoolModbusDataCollectionFixture.cs │ ├── DataStoreEventArgsFixture.cs │ ├── DataStoreFixture.cs │ ├── DiscreteCollectionFixture.cs │ ├── ModbusDataCollectionFixture.cs │ ├── RegisterCollectionFixture.cs │ └── UshortModbusDataCollectionFixture.cs ├── Device │ ├── ModbusMasterFixture.cs │ ├── ModbusSlaveFixture.cs │ └── TcpConnectionEventArgsFixture.cs ├── IO │ ├── EmptyTransportFixture.cs │ ├── ModbusAsciiTransportFixture.cs │ ├── ModbusRtuTransportFixture.cs │ ├── ModbusSerialTransportFixture.cs │ ├── ModbusTcpTransportFixture.cs │ ├── ModbusTransportFixture.cs │ └── UdpClientAdapterFixture.cs ├── InvalidModbusRequestExceptionFixture.cs ├── Message │ ├── DiagnosticsRequestResponseFixture.cs │ ├── MessageUtility.cs │ ├── ModbusMessageFactoryFixture.cs │ ├── ModbusMessageFixture.cs │ ├── ModbusMessageImplFixture.cs │ ├── ModbusMessageWithDataFixture.cs │ ├── ReadCoilsInputsRequestFixture.cs │ ├── ReadCoilsInputsResponseFixture.cs │ ├── ReadHoldingInputRegistersRequestFixture.cs │ ├── ReadHoldingInputRegistersResponseFixture.cs │ ├── ReadWriteMultipleRegistersRequestFixture.cs │ ├── ReturnQueryDataRequestResponseFixture.cs │ ├── SlaveExceptionResponseFixture.cs │ ├── WriteMultipleCoilsRequestFixture.cs │ ├── WriteMultipleCoilsResponseFixture.cs │ ├── WriteMultipleRegistersRequestFixture.cs │ ├── WriteMultipleRegistersResponseFixture.cs │ ├── WriteSingleCoilRequestResponseFixture.cs │ └── WriteSingleRegisterRequestResponseFixture.cs ├── NModbus4.UnitTests.xproj ├── Properties │ └── AssemblyInfo.cs ├── SlaveExceptionFixture.cs ├── Utility │ ├── CollectionUtilityFixture.cs │ ├── DiscriminatedUnionFixture.cs │ └── ModbusUtilityFixture.cs └── project.json ├── NModbus4.sln ├── NModbus4 ├── Data │ ├── DataStore.cs │ ├── DataStoreEventArgs.cs │ ├── DataStoreFactory.cs │ ├── DiscreteCollection.cs │ ├── IModbusMessageDataCollection.cs │ ├── ModbusDataCollection.cs │ ├── ModbusDataType.cs │ └── RegisterCollection.cs ├── Device │ ├── IModbusMaster.cs │ ├── IModbusSerialMaster.cs │ ├── ModbusDevice.cs │ ├── ModbusIpMaster.cs │ ├── ModbusMaster.cs │ ├── ModbusMasterTcpConnection.cs │ ├── ModbusSerialMaster.cs │ ├── ModbusSerialSlave.cs │ ├── ModbusSlave.cs │ ├── ModbusSlaveRequestEventArgs.cs │ ├── ModbusTcpSlave.cs │ ├── ModbusUdpSlave.cs │ └── TcpConnectionEventArgs.cs ├── Extensions │ └── Enron │ │ └── EnronModbus.cs ├── GlobalSuppressions.cs ├── IO │ ├── EmptyTransport.cs │ ├── IStreamResource.cs │ ├── ModbusAsciiTransport.cs │ ├── ModbusIpTransport.cs │ ├── ModbusRtuTransport.cs │ ├── ModbusSerialTransport.cs │ ├── ModbusTransport.cs │ ├── StreamResourceUtility.cs │ ├── TcpClientAdapter.cs │ └── UdpClientAdapter.cs ├── InvalidModbusRequestException.cs ├── Message │ ├── AbstractModbusMessage.cs │ ├── AbstractModbusMessageWithData.cs │ ├── DiagnosticsRequestResponse.cs │ ├── IModbusMessage.cs │ ├── IModbusRequest.cs │ ├── ModbusMessageFactory.cs │ ├── ModbusMessageImpl.cs │ ├── ReadCoilsInputsRequest.cs │ ├── ReadCoilsInputsResponse.cs │ ├── ReadHoldingInputRegistersRequest.cs │ ├── ReadHoldingInputRegistersResponse.cs │ ├── ReadWriteMultipleRegistersRequest.cs │ ├── SlaveExceptionResponse.cs │ ├── WriteMultipleCoilsRequest.cs │ ├── WriteMultipleCoilsResponse.cs │ ├── WriteMultipleRegistersRequest.cs │ ├── WriteMultipleRegistersResponse.cs │ ├── WriteSingleCoilRequestResponse.cs │ └── WriteSingleRegisterRequestResponse.cs ├── Modbus.cs ├── NModbus4.xproj ├── Properties │ └── AssemblyInfo.cs ├── Resources.cs ├── SlaveException.cs ├── Unme.Common │ ├── DisposableUtility.cs │ └── SequenceUtility.cs ├── Utility │ ├── DiscriminatedUnion.cs │ └── ModbusUtility.cs └── project.json ├── NuGet.config ├── README.md ├── Samples ├── Program.cs ├── Properties │ └── AssemblyInfo.cs ├── Samples.xproj └── project.json ├── appveyor.yml └── global.json /.travis.yml: -------------------------------------------------------------------------------- 1 | language: csharp 2 | sudo: required 3 | os: linux 4 | dist: trusty 5 | addons: 6 | apt: 7 | packages: 8 | - libunwind8 9 | - gettext 10 | - libssl-dev 11 | - libcurl4-openssl-dev 12 | - zlib1g 13 | - libicu-dev 14 | install: 15 | - sudo sh -c 'echo "deb [arch=amd64] https://apt-mo.trafficmanager.net/repos/dotnet/ trusty main" > /etc/apt/sources.list.d/dotnetdev.list' 16 | - sudo apt-key adv --keyserver apt-mo.trafficmanager.net --recv-keys 417A0893 17 | - sudo apt-get update 18 | - sudo apt-get --assume-yes install dotnet-dev-1.0.0-preview2-003121 19 | solution: NModbus4.sln 20 | script: 21 | - dotnet restore 22 | - cd NModbus4.UnitTests 23 | - dotnet test 24 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2006 Scott Alexander, 2015 Dmitry Turin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /NModbus.chm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Maxwe11/NModbus4/7328fc27ae1ca636b979bbe34056d81e2bde2948/NModbus.chm -------------------------------------------------------------------------------- /NModbus4.IntegrationTests/CustomMessages/CustomReadHoldingRegistersRequest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net; 4 | using Modbus.Message; 5 | 6 | namespace Modbus.IntegrationTests.CustomMessages 7 | { 8 | public class CustomReadHoldingRegistersRequest : IModbusMessage 9 | { 10 | private byte _functionCode; 11 | private byte _slaveAddress; 12 | private ushort _startAddress; 13 | private ushort _numberOfPoints; 14 | private ushort _transactionId; 15 | 16 | public CustomReadHoldingRegistersRequest(byte functionCode, byte slaveAddress, ushort startAddress, ushort numberOfPoints) 17 | { 18 | _functionCode = functionCode; 19 | _slaveAddress = slaveAddress; 20 | _startAddress = startAddress; 21 | _numberOfPoints = numberOfPoints; 22 | } 23 | 24 | public byte[] MessageFrame 25 | { 26 | get 27 | { 28 | List frame = new List(); 29 | frame.Add(SlaveAddress); 30 | frame.AddRange(ProtocolDataUnit); 31 | 32 | return frame.ToArray(); 33 | } 34 | } 35 | 36 | public byte[] ProtocolDataUnit 37 | { 38 | get 39 | { 40 | List pdu = new List(); 41 | 42 | pdu.Add(FunctionCode); 43 | pdu.AddRange(BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)StartAddress))); 44 | pdu.AddRange(BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)NumberOfPoints))); 45 | 46 | return pdu.ToArray(); 47 | } 48 | } 49 | 50 | public ushort TransactionId 51 | { 52 | get { return _transactionId; } 53 | set { _transactionId = value; } 54 | } 55 | 56 | public byte FunctionCode 57 | { 58 | get { return _functionCode; } 59 | set { _functionCode = value; } 60 | } 61 | 62 | public byte SlaveAddress 63 | { 64 | get { return _slaveAddress; } 65 | set { _slaveAddress = value; } 66 | } 67 | 68 | public ushort StartAddress 69 | { 70 | get { return _startAddress; } 71 | set { _startAddress = value; } 72 | } 73 | 74 | public ushort NumberOfPoints 75 | { 76 | get { return _numberOfPoints; } 77 | set { _numberOfPoints = value; } 78 | } 79 | 80 | public void Initialize(byte[] frame) 81 | { 82 | if (frame == null) 83 | { 84 | throw new ArgumentNullException(nameof(frame)); 85 | } 86 | 87 | if (frame.Length != 6) 88 | { 89 | throw new ArgumentException("Invalid frame.", nameof(frame)); 90 | } 91 | 92 | SlaveAddress = frame[0]; 93 | FunctionCode = frame[1]; 94 | StartAddress = (ushort)IPAddress.NetworkToHostOrder(BitConverter.ToInt16(frame, 2)); 95 | NumberOfPoints = (ushort)IPAddress.NetworkToHostOrder(BitConverter.ToInt16(frame, 4)); 96 | } 97 | } 98 | } -------------------------------------------------------------------------------- /NModbus4.IntegrationTests/CustomMessages/CustomReadHoldingRegistersResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Modbus.Data; 5 | using Modbus.Message; 6 | 7 | namespace Modbus.IntegrationTests.CustomMessages 8 | { 9 | public class CustomReadHoldingRegistersResponse : IModbusMessage 10 | { 11 | private byte _functionCode; 12 | private byte _slaveAddress; 13 | private byte _byteCount; 14 | private ushort _transactionId; 15 | private RegisterCollection _data; 16 | 17 | public ushort[] Data => _data.ToArray(); 18 | 19 | public byte[] MessageFrame 20 | { 21 | get 22 | { 23 | List frame = new List(); 24 | frame.Add(SlaveAddress); 25 | frame.AddRange(ProtocolDataUnit); 26 | 27 | return frame.ToArray(); 28 | } 29 | } 30 | 31 | public byte[] ProtocolDataUnit 32 | { 33 | get 34 | { 35 | List pdu = new List(); 36 | 37 | pdu.Add(_functionCode); 38 | pdu.Add(ByteCount); 39 | pdu.AddRange(_data.NetworkBytes); 40 | 41 | return pdu.ToArray(); 42 | } 43 | } 44 | 45 | public ushort TransactionId 46 | { 47 | get { return _transactionId; } 48 | set { _transactionId = value; } 49 | } 50 | 51 | public byte FunctionCode 52 | { 53 | get { return _functionCode; } 54 | set { _functionCode = value; } 55 | } 56 | 57 | public byte SlaveAddress 58 | { 59 | get { return _slaveAddress; } 60 | set { _slaveAddress = value; } 61 | } 62 | 63 | public byte ByteCount 64 | { 65 | get { return _byteCount; } 66 | set { _byteCount = value; } 67 | } 68 | 69 | public void Initialize(byte[] frame) 70 | { 71 | if (frame == null) 72 | { 73 | throw new ArgumentNullException(nameof(frame)); 74 | } 75 | 76 | if (frame.Length < 3 || frame.Length < 3 + frame[2]) 77 | { 78 | throw new ArgumentException("Message frame does not contain enough bytes.", nameof(frame)); 79 | } 80 | 81 | SlaveAddress = frame[0]; 82 | FunctionCode = frame[1]; 83 | ByteCount = frame[2]; 84 | _data = new RegisterCollection(frame.Skip(3).Take(ByteCount).ToArray()); 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /NModbus4.IntegrationTests/CustomMessages/CustomWriteMultipleRegistersRequest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net; 5 | using Modbus.Data; 6 | using Modbus.Message; 7 | 8 | namespace Modbus.IntegrationTests.CustomMessages 9 | { 10 | public class CustomWriteMultipleRegistersRequest : IModbusMessage 11 | { 12 | private byte _functionCode; 13 | private byte _slaveAddress; 14 | private byte _byteCount; 15 | private ushort _startAddress; 16 | private ushort _numberOfPoints; 17 | private ushort _transactionId; 18 | private RegisterCollection _data; 19 | 20 | public CustomWriteMultipleRegistersRequest(byte functionCode, byte slaveAddress, ushort startAddress, RegisterCollection data) 21 | { 22 | _functionCode = functionCode; 23 | _slaveAddress = slaveAddress; 24 | _startAddress = startAddress; 25 | _numberOfPoints = (ushort)data.Count; 26 | _byteCount = data.ByteCount; 27 | _data = data; 28 | } 29 | 30 | public byte[] MessageFrame 31 | { 32 | get 33 | { 34 | List frame = new List(); 35 | frame.Add(SlaveAddress); 36 | frame.AddRange(ProtocolDataUnit); 37 | 38 | return frame.ToArray(); 39 | } 40 | } 41 | 42 | public byte[] ProtocolDataUnit 43 | { 44 | get 45 | { 46 | List pdu = new List(); 47 | 48 | pdu.Add(FunctionCode); 49 | pdu.AddRange(BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)StartAddress))); 50 | pdu.AddRange(BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)NumberOfPoints))); 51 | pdu.Add(ByteCount); 52 | pdu.AddRange(Data.NetworkBytes); 53 | 54 | return pdu.ToArray(); 55 | } 56 | } 57 | 58 | public ushort TransactionId 59 | { 60 | get { return _transactionId; } 61 | set { _transactionId = value; } 62 | } 63 | 64 | public byte FunctionCode 65 | { 66 | get { return _functionCode; } 67 | set { _functionCode = value; } 68 | } 69 | 70 | public byte SlaveAddress 71 | { 72 | get { return _slaveAddress; } 73 | set { _slaveAddress = value; } 74 | } 75 | 76 | public ushort StartAddress 77 | { 78 | get { return _startAddress; } 79 | set { _startAddress = value; } 80 | } 81 | 82 | public ushort NumberOfPoints 83 | { 84 | get { return _numberOfPoints; } 85 | set { _numberOfPoints = value; } 86 | } 87 | 88 | public byte ByteCount 89 | { 90 | get { return _byteCount; } 91 | set { _byteCount = value; } 92 | } 93 | 94 | public RegisterCollection Data 95 | { 96 | get { return _data; } 97 | set { _data = value; } 98 | } 99 | 100 | public void Initialize(byte[] frame) 101 | { 102 | if (frame == null) 103 | { 104 | throw new ArgumentNullException(nameof(frame)); 105 | } 106 | 107 | if (frame.Length < 7 || frame.Length < 7 + frame[6]) 108 | { 109 | throw new FormatException("Message frame does not contain enough bytes."); 110 | } 111 | 112 | SlaveAddress = frame[0]; 113 | FunctionCode = frame[1]; 114 | StartAddress = (ushort)IPAddress.NetworkToHostOrder(BitConverter.ToInt16(frame, 2)); 115 | NumberOfPoints = (ushort)IPAddress.NetworkToHostOrder(BitConverter.ToInt16(frame, 4)); 116 | ByteCount = frame[6]; 117 | Data = new RegisterCollection(frame.Skip(7).Take(ByteCount).ToArray()); 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /NModbus4.IntegrationTests/CustomMessages/CustomWriteMultipleRegistersResponse.cs: -------------------------------------------------------------------------------- 1 | namespace Modbus.IntegrationTests.CustomMessages 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Net; 6 | using Message; 7 | 8 | public class CustomWriteMultipleRegistersResponse : IModbusMessage 9 | { 10 | private byte _functionCode; 11 | private byte _slaveAddress; 12 | private ushort _startAddress; 13 | private ushort _numberOfPoints; 14 | private ushort _transactionId; 15 | 16 | public byte[] MessageFrame 17 | { 18 | get 19 | { 20 | List frame = new List(); 21 | frame.Add(SlaveAddress); 22 | frame.AddRange(ProtocolDataUnit); 23 | 24 | return frame.ToArray(); 25 | } 26 | } 27 | 28 | public byte[] ProtocolDataUnit 29 | { 30 | get 31 | { 32 | List pdu = new List(); 33 | 34 | pdu.Add(FunctionCode); 35 | pdu.AddRange(BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)StartAddress))); 36 | pdu.AddRange(BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)NumberOfPoints))); 37 | 38 | return pdu.ToArray(); 39 | } 40 | } 41 | 42 | public ushort TransactionId 43 | { 44 | get { return _transactionId; } 45 | set { _transactionId = value; } 46 | } 47 | 48 | public byte FunctionCode 49 | { 50 | get { return _functionCode; } 51 | set { _functionCode = value; } 52 | } 53 | 54 | public byte SlaveAddress 55 | { 56 | get { return _slaveAddress; } 57 | set { _slaveAddress = value; } 58 | } 59 | 60 | public ushort StartAddress 61 | { 62 | get { return _startAddress; } 63 | set { _startAddress = value; } 64 | } 65 | 66 | public ushort NumberOfPoints 67 | { 68 | get { return _numberOfPoints; } 69 | set { _numberOfPoints = value; } 70 | } 71 | 72 | public void Initialize(byte[] frame) 73 | { 74 | if (frame == null) 75 | { 76 | throw new ArgumentNullException(nameof(frame)); 77 | } 78 | 79 | if (frame.Length < 6) 80 | { 81 | throw new FormatException("Message frame does not contain enough bytes."); 82 | } 83 | 84 | SlaveAddress = frame[0]; 85 | FunctionCode = frame[1]; 86 | StartAddress = (ushort)IPAddress.NetworkToHostOrder(BitConverter.ToInt16(frame, 2)); 87 | NumberOfPoints = (ushort)IPAddress.NetworkToHostOrder(BitConverter.ToInt16(frame, 4)); 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /NModbus4.IntegrationTests/EnronFixture.cs: -------------------------------------------------------------------------------- 1 | using Modbus.Extensions.Enron; 2 | using Xunit; 3 | 4 | namespace Modbus.IntegrationTests 5 | { 6 | internal class EnronFixture : NModbusSerialRtuMasterDl06SlaveFixture 7 | { 8 | [Fact] 9 | public virtual void ReadHoldingRegisters32() 10 | { 11 | uint[] registers = Master.ReadHoldingRegisters32(SlaveAddress, 104, 2); 12 | Assert.Equal(new uint[] { 0, 0 }, registers); 13 | } 14 | 15 | [Fact] 16 | public virtual void ReadInputRegisters32() 17 | { 18 | uint[] registers = Master.ReadInputRegisters32(SlaveAddress, 104, 2); 19 | Assert.Equal(new uint[] { 0, 0 }, registers); 20 | } 21 | 22 | [Fact] 23 | public virtual void WriteSingleRegister32() 24 | { 25 | ushort testAddress = 200; 26 | uint testValue = 350; 27 | 28 | uint originalValue = Master.ReadHoldingRegisters32(SlaveAddress, testAddress, 1)[0]; 29 | Master.WriteSingleRegister32(SlaveAddress, testAddress, testValue); 30 | Assert.Equal(testValue, Master.ReadHoldingRegisters32(SlaveAddress, testAddress, 1)[0]); 31 | Master.WriteSingleRegister32(SlaveAddress, testAddress, originalValue); 32 | Assert.Equal(originalValue, Master.ReadHoldingRegisters(SlaveAddress, testAddress, 1)[0]); 33 | } 34 | 35 | [Fact] 36 | public virtual void WriteMultipleRegisters32() 37 | { 38 | ushort testAddress = 120; 39 | uint[] testValues = new uint[] { 10, 20, 30, 40, 50 }; 40 | 41 | uint[] originalValues = Master.ReadHoldingRegisters32(SlaveAddress, testAddress, (ushort)testValues.Length); 42 | Master.WriteMultipleRegisters32(SlaveAddress, testAddress, testValues); 43 | uint[] newValues = Master.ReadHoldingRegisters32(SlaveAddress, testAddress, (ushort)testValues.Length); 44 | Assert.Equal(testValues, newValues); 45 | Master.WriteMultipleRegisters32(SlaveAddress, testAddress, originalValues); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /NModbus4.IntegrationTests/ModbusIpMasterFixture.cs: -------------------------------------------------------------------------------- 1 | using System.Net.Sockets; 2 | using System.Threading; 3 | using Modbus.Device; 4 | using Xunit; 5 | 6 | namespace Modbus.IntegrationTests 7 | { 8 | internal class ModbusIpMasterFixture 9 | { 10 | [Fact] 11 | public void OverrideTimeoutOnTcpClient() 12 | { 13 | var listener = new TcpListener(ModbusMasterFixture.TcpHost, ModbusMasterFixture.Port); 14 | using (var slave = ModbusTcpSlave.CreateTcp(ModbusMasterFixture.SlaveAddress, listener)) 15 | { 16 | var slaveThread = new Thread(slave.Listen); 17 | slaveThread.Start(); 18 | 19 | var client = new TcpClient(ModbusMasterFixture.TcpHost.ToString(), ModbusMasterFixture.Port); 20 | client.ReceiveTimeout = 1500; 21 | client.SendTimeout = 3000; 22 | using (var master = ModbusIpMaster.CreateIp(client)) 23 | { 24 | Assert.Equal(1500, client.GetStream().ReadTimeout); 25 | Assert.Equal(3000, client.GetStream().WriteTimeout); 26 | } 27 | } 28 | } 29 | 30 | [Fact] 31 | public void OverrideTimeoutOnNetworkStream() 32 | { 33 | var listener = new TcpListener(ModbusMasterFixture.TcpHost, ModbusMasterFixture.Port); 34 | using (var slave = ModbusTcpSlave.CreateTcp(ModbusMasterFixture.SlaveAddress, listener)) 35 | { 36 | var slaveThread = new Thread(slave.Listen); 37 | slaveThread.Start(); 38 | 39 | var client = new TcpClient(ModbusMasterFixture.TcpHost.ToString(), ModbusMasterFixture.Port); 40 | client.GetStream().ReadTimeout = 1500; 41 | client.GetStream().WriteTimeout = 3000; 42 | using (var master = ModbusIpMaster.CreateIp(client)) 43 | { 44 | Assert.Equal(1500, client.GetStream().ReadTimeout); 45 | Assert.Equal(3000, client.GetStream().WriteTimeout); 46 | } 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /NModbus4.IntegrationTests/ModbusSerialMasterFixture.cs: -------------------------------------------------------------------------------- 1 | using Modbus.Device; 2 | using Xunit; 3 | 4 | namespace Modbus.IntegrationTests 5 | { 6 | internal abstract class ModbusSerialMasterFixture : ModbusMasterFixture 7 | { 8 | [Fact] 9 | public virtual void ReturnQueryData() 10 | { 11 | Assert.True(((ModbusSerialMaster)Master).ReturnQueryData(SlaveAddress, 18)); 12 | Assert.True(((ModbusSerialMaster)Master).ReturnQueryData(SlaveAddress, 5)); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /NModbus4.IntegrationTests/NModbusSerialAsciiMasterFixture.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO.Ports; 3 | using Modbus.Device; 4 | using Xunit; 5 | 6 | namespace Modbus.IntegrationTests 7 | { 8 | internal class NModbusSerialAsciiMasterFixture 9 | { 10 | [Fact] 11 | public void NModbusAsciiMaster_ReadTimeout() 12 | { 13 | SerialPort port = ModbusMasterFixture.CreateAndOpenSerialPort(ModbusMasterFixture.DefaultMasterSerialPortName); 14 | using (IModbusSerialMaster master = ModbusSerialMaster.CreateAscii(port)) 15 | { 16 | master.Transport.ReadTimeout = master.Transport.WriteTimeout = 1000; 17 | Assert.Throws(() => master.ReadCoils(100, 1, 1)); 18 | } 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /NModbus4.IntegrationTests/NModbusSerialAsciiMasterJamodSerialAsciiSlaveFixture.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | using Modbus.Device; 3 | using Xunit; 4 | 5 | namespace Modbus.IntegrationTests 6 | { 7 | internal class NModbusSerialAsciiMasterJamodSerialAsciiSlaveFixture : ModbusMasterFixture 8 | { 9 | private string program = $"SerialSlave {DefaultSlaveSerialPortName} ASCII"; 10 | 11 | public NModbusSerialAsciiMasterJamodSerialAsciiSlaveFixture() 12 | { 13 | StartJamodSlave(program); 14 | 15 | MasterSerialPort = CreateAndOpenSerialPort(DefaultMasterSerialPortName); 16 | Master = ModbusSerialMaster.CreateAscii(MasterSerialPort); 17 | } 18 | 19 | /// 20 | /// Jamod slave does not support this function 21 | /// 22 | public override void ReadWriteMultipleRegisters() 23 | { 24 | } 25 | 26 | [Fact] 27 | public override void ReadCoils() 28 | { 29 | base.ReadCoils(); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /NModbus4.IntegrationTests/NModbusSerialAsciiMasterNModbusSerialAsciiSlaveFixture.cs: -------------------------------------------------------------------------------- 1 | using Modbus.Device; 2 | using Xunit; 3 | 4 | namespace Modbus.IntegrationTests 5 | { 6 | internal class NModbusSerialAsciiMasterNModbusSerialAsciiSlaveFixture : ModbusSerialMasterFixture 7 | { 8 | public NModbusSerialAsciiMasterNModbusSerialAsciiSlaveFixture() 9 | { 10 | MasterSerialPort = CreateAndOpenSerialPort(DefaultMasterSerialPortName); 11 | Master = ModbusSerialMaster.CreateAscii(MasterSerialPort); 12 | SetupSlaveSerialPort(); 13 | Slave = ModbusSerialSlave.CreateAscii(SlaveAddress, SlaveSerialPort); 14 | 15 | StartSlave(); 16 | } 17 | 18 | [Fact] 19 | public override void ReadCoils() 20 | { 21 | base.ReadCoils(); 22 | } 23 | 24 | [Fact] 25 | public override void ReadInputs() 26 | { 27 | base.ReadInputs(); 28 | } 29 | 30 | [Fact] 31 | public override void ReadHoldingRegisters() 32 | { 33 | base.ReadHoldingRegisters(); 34 | } 35 | 36 | [Fact] 37 | public override void ReadInputRegisters() 38 | { 39 | base.ReadInputRegisters(); 40 | } 41 | 42 | [Fact] 43 | public override void WriteSingleCoil() 44 | { 45 | base.WriteSingleCoil(); 46 | } 47 | 48 | [Fact] 49 | public override void WriteMultipleCoils() 50 | { 51 | base.WriteMultipleCoils(); 52 | } 53 | 54 | [Fact] 55 | public override void WriteSingleRegister() 56 | { 57 | base.WriteSingleRegister(); 58 | } 59 | 60 | [Fact] 61 | public override void WriteMultipleRegisters() 62 | { 63 | base.WriteMultipleRegisters(); 64 | } 65 | 66 | [Fact] 67 | public override void ReadWriteMultipleRegisters() 68 | { 69 | base.ReadWriteMultipleRegisters(); 70 | } 71 | 72 | [Fact] 73 | public override void ReturnQueryData() 74 | { 75 | base.ReturnQueryData(); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /NModbus4.IntegrationTests/NModbusSerialRtuMasterDl06SlaveFixture.cs: -------------------------------------------------------------------------------- 1 | using Modbus.Device; 2 | using Xunit; 3 | 4 | namespace Modbus.IntegrationTests 5 | { 6 | internal class NModbusSerialRtuMasterDl06SlaveFixture : ModbusSerialMasterFixture 7 | { 8 | public NModbusSerialRtuMasterDl06SlaveFixture() 9 | { 10 | MasterSerialPort = CreateAndOpenSerialPort("COM1"); 11 | Master = ModbusSerialMaster.CreateRtu(MasterSerialPort); 12 | } 13 | 14 | /// 15 | /// Not supported by the DL06 16 | /// 17 | public override void ReadWriteMultipleRegisters() 18 | { 19 | } 20 | 21 | /// 22 | /// Not supported by the DL06 23 | /// 24 | public override void ReturnQueryData() 25 | { 26 | } 27 | 28 | [Fact] 29 | public override void ReadCoils() 30 | { 31 | base.ReadCoils(); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /NModbus4.IntegrationTests/NModbusSerialRtuMasterFixture.cs: -------------------------------------------------------------------------------- 1 | using System.IO.Ports; 2 | using Modbus.Device; 3 | using Xunit; 4 | 5 | namespace Modbus.IntegrationTests 6 | { 7 | internal class NModbusSerialRtuMasterFixture 8 | { 9 | [Fact] 10 | public void NModbusRtuMaster_ReadTimeout() 11 | { 12 | SerialPort port = ModbusMasterFixture.CreateAndOpenSerialPort(ModbusMasterFixture.DefaultMasterSerialPortName); 13 | using (var master = ModbusSerialMaster.CreateRtu(port)) 14 | { 15 | master.Transport.ReadTimeout = master.Transport.WriteTimeout = 1000; 16 | master.ReadCoils(100, 1, 1); 17 | } 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /NModbus4.IntegrationTests/NModbusSerialRtuMasterNModbusSerialRtuSlaveFixture.cs: -------------------------------------------------------------------------------- 1 | using Modbus.Device; 2 | using Xunit; 3 | 4 | namespace Modbus.IntegrationTests 5 | { 6 | internal class NModbusSerialRtuMasterNModbusSerialRtuSlaveFixture : ModbusSerialMasterFixture 7 | { 8 | public NModbusSerialRtuMasterNModbusSerialRtuSlaveFixture() 9 | { 10 | SetupSlaveSerialPort(); 11 | Slave = ModbusSerialSlave.CreateRtu(SlaveAddress, SlaveSerialPort); 12 | StartSlave(); 13 | 14 | MasterSerialPort = CreateAndOpenSerialPort(DefaultMasterSerialPortName); 15 | Master = ModbusSerialMaster.CreateRtu(MasterSerialPort); 16 | } 17 | 18 | [Fact] 19 | public override void ReadCoils() 20 | { 21 | base.ReadCoils(); 22 | } 23 | 24 | [Fact] 25 | public override void ReadHoldingRegisters() 26 | { 27 | base.ReadHoldingRegisters(); 28 | } 29 | 30 | [Fact] 31 | public override void ReadInputs() 32 | { 33 | base.ReadInputs(); 34 | } 35 | 36 | [Fact] 37 | public override void WriteSingleCoil() 38 | { 39 | base.WriteSingleCoil(); 40 | } 41 | 42 | [Fact] 43 | public override void WriteMultipleCoils() 44 | { 45 | base.WriteMultipleCoils(); 46 | } 47 | 48 | [Fact] 49 | public override void WriteSingleRegister() 50 | { 51 | base.WriteSingleRegister(); 52 | } 53 | 54 | [Fact] 55 | public override void WriteMultipleRegisters() 56 | { 57 | base.WriteMultipleRegisters(); 58 | } 59 | 60 | [Fact(Skip = "Need to fix RTU slave for this function code")] 61 | public override void ReadWriteMultipleRegisters() 62 | { 63 | base.ReadWriteMultipleRegisters(); 64 | } 65 | 66 | [Fact] 67 | public override void ReturnQueryData() 68 | { 69 | base.ReturnQueryData(); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /NModbus4.IntegrationTests/NModbusSerialRtuSlaveFixture.cs: -------------------------------------------------------------------------------- 1 | using System.IO.Ports; 2 | using System.Threading; 3 | using Modbus.Data; 4 | using Modbus.Device; 5 | using Xunit; 6 | 7 | namespace Modbus.IntegrationTests 8 | { 9 | internal class NModbusSerialRtuSlaveFixture 10 | { 11 | [Fact] 12 | public void NModbusSerialRtuSlave_BonusCharacter_VerifyTimeout() 13 | { 14 | SerialPort masterPort = ModbusMasterFixture.CreateAndOpenSerialPort(ModbusMasterFixture.DefaultMasterSerialPortName); 15 | SerialPort slavePort = ModbusMasterFixture.CreateAndOpenSerialPort(ModbusMasterFixture.DefaultSlaveSerialPortName); 16 | 17 | using (var master = ModbusSerialMaster.CreateRtu(masterPort)) 18 | using (var slave = ModbusSerialSlave.CreateRtu(1, slavePort)) 19 | { 20 | master.Transport.ReadTimeout = master.Transport.WriteTimeout = 1000; 21 | slave.DataStore = DataStoreFactory.CreateTestDataStore(); 22 | 23 | Thread slaveThread = new Thread(slave.Listen); 24 | slaveThread.IsBackground = true; 25 | slaveThread.Start(); 26 | 27 | // assert successful communication 28 | Assert.Equal(new bool[] { false, true }, master.ReadCoils(1, 1, 2)); 29 | 30 | // write "bonus" character 31 | masterPort.Write("*"); 32 | 33 | // assert successful communication 34 | Assert.Equal(new bool[] { false, true }, master.ReadCoils(1, 1, 2)); 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /NModbus4.IntegrationTests/NModbusTcpMasterJamodTcpSlaveFixture.cs: -------------------------------------------------------------------------------- 1 | using System.Net.Sockets; 2 | using Modbus.Device; 3 | 4 | namespace Modbus.IntegrationTests 5 | { 6 | internal class NModbusTcpMasterJamodTcpSlaveFixture : ModbusMasterFixture 7 | { 8 | public NModbusTcpMasterJamodTcpSlaveFixture() 9 | { 10 | string program = $"TcpSlave {Port}"; 11 | StartJamodSlave(program); 12 | 13 | MasterTcp = new TcpClient(TcpHost.ToString(), Port); 14 | Master = ModbusIpMaster.CreateIp(MasterTcp); 15 | } 16 | 17 | /// 18 | /// Not supported by the Jamod Slave 19 | /// 20 | public override void ReadWriteMultipleRegisters() 21 | { 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /NModbus4.IntegrationTests/NModbusTcpMasterNModbusTcpSlaveFixture.cs: -------------------------------------------------------------------------------- 1 | using System.Net.Sockets; 2 | using Modbus.Device; 3 | 4 | namespace Modbus.IntegrationTests 5 | { 6 | internal class NModbusTcpMasterNModbusTcpSlaveFixture : ModbusMasterFixture 7 | { 8 | public NModbusTcpMasterNModbusTcpSlaveFixture() 9 | { 10 | SlaveTcp = new TcpListener(TcpHost, Port); 11 | SlaveTcp.Start(); 12 | Slave = ModbusTcpSlave.CreateTcp(SlaveAddress, SlaveTcp); 13 | StartSlave(); 14 | 15 | MasterTcp = new TcpClient(TcpHost.ToString(), Port); 16 | Master = ModbusIpMaster.CreateIp(MasterTcp); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /NModbus4.IntegrationTests/NModbusUdpMasterNModbusUdpSlaveFixture.cs: -------------------------------------------------------------------------------- 1 | using System.Net.Sockets; 2 | using Modbus.Device; 3 | 4 | namespace Modbus.IntegrationTests 5 | { 6 | internal class NModbusUdpMasterNModbusUdpSlaveFixture : ModbusMasterFixture 7 | { 8 | public NModbusUdpMasterNModbusUdpSlaveFixture() 9 | { 10 | SlaveUdp = new UdpClient(Port); 11 | Slave = ModbusUdpSlave.CreateUdp(SlaveUdp); 12 | StartSlave(); 13 | 14 | MasterUdp = new UdpClient(); 15 | MasterUdp.Connect(DefaultModbusIPEndPoint); 16 | Master = ModbusIpMaster.CreateIp(MasterUdp); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /NModbus4.IntegrationTests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using System.Runtime.InteropServices; 4 | 5 | using Xunit; 6 | 7 | [assembly: AssemblyTitle("Modbus.IntegrationTests")] 8 | [assembly: AssemblyProduct("NModbus")] 9 | [assembly: AssemblyCopyright("Licensed under MIT License.")] 10 | [assembly: ComVisible(false)] 11 | [assembly: CLSCompliant(false)] 12 | [assembly: Guid("6ddc8745-7d6d-4b5e-8e81-c23c7338bbab")] 13 | [assembly: AssemblyVersion("1.11.0.0")] 14 | [assembly: AssemblyFileVersion("1.11.0.0")] 15 | 16 | [assembly: CollectionBehavior(CollectionBehavior.CollectionPerAssembly, DisableTestParallelization = true, MaxParallelThreads = 1)] 17 | -------------------------------------------------------------------------------- /NModbus4.IntegrationTests/TestCases.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO.Ports; 3 | using System.Net; 4 | using System.Net.Sockets; 5 | using System.Threading; 6 | using Modbus.Data; 7 | using Modbus.Device; 8 | 9 | namespace Modbus.IntegrationTests 10 | { 11 | internal class TestCases 12 | { 13 | public static void Serial() 14 | { 15 | using (var masterPort = new SerialPort("COM2")) 16 | using (var slavePort = new SerialPort("COM1")) 17 | { 18 | // configure serial ports 19 | masterPort.BaudRate = slavePort.BaudRate = 9600; 20 | masterPort.DataBits = slavePort.DataBits = 8; 21 | masterPort.Parity = slavePort.Parity = Parity.None; 22 | masterPort.StopBits = slavePort.StopBits = StopBits.One; 23 | masterPort.Open(); 24 | slavePort.Open(); 25 | 26 | using (var slave = ModbusSerialSlave.CreateRtu(1, slavePort)) 27 | { 28 | StartSlave(slave); 29 | 30 | // create modbus master 31 | using (var master = ModbusSerialMaster.CreateRtu(masterPort)) 32 | { 33 | ReadRegisters(master); 34 | } 35 | } 36 | } 37 | } 38 | 39 | public static void Tcp() 40 | { 41 | var slaveClient = new TcpListener(new IPAddress(new byte[] { 127, 0, 0, 1 }), 502); 42 | using (var slave = ModbusTcpSlave.CreateTcp((byte)1, slaveClient)) 43 | { 44 | StartSlave(slave); 45 | 46 | IPAddress address = new IPAddress(new byte[] { 127, 0, 0, 1 }); 47 | var masterClient = new TcpClient(address.ToString(), 502); 48 | 49 | using (var master = ModbusIpMaster.CreateIp(masterClient)) 50 | { 51 | ReadRegisters(master); 52 | } 53 | } 54 | } 55 | 56 | public static void Udp() 57 | { 58 | var slaveClient = new UdpClient(502); 59 | using (var slave = ModbusUdpSlave.CreateUdp(slaveClient)) 60 | { 61 | StartSlave(slave); 62 | 63 | var masterClient = new UdpClient(); 64 | IPEndPoint endPoint = new IPEndPoint(new IPAddress(new byte[] { 127, 0, 0, 1 }), 502); 65 | masterClient.Connect(endPoint); 66 | 67 | using (var master = ModbusIpMaster.CreateIp(masterClient)) 68 | { 69 | ReadRegisters(master); 70 | } 71 | } 72 | } 73 | 74 | public static void StartSlave(ModbusSlave slave) 75 | { 76 | slave.DataStore = DataStoreFactory.CreateTestDataStore(); 77 | var slaveThread = new Thread(slave.Listen); 78 | slaveThread.Start(); 79 | } 80 | 81 | public static void ReadRegisters(IModbusMaster master) 82 | { 83 | var result = master.ReadHoldingRegisters(1, 0, 5); 84 | 85 | for (int i = 0; i < 5; i++) 86 | { 87 | if (result[i] != i + 1) 88 | { 89 | throw new Exception(); 90 | } 91 | } 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /NModbus4.IntegrationTests/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /NModbus4.Serial/NModbus4.Serial.xproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 14.0 5 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 6 | 7 | 8 | 9 | 2246217d-d3c2-41cb-b866-d093df087846 10 | Modbus.Serial 11 | .\obj\ 12 | .\bin\ 13 | 14 | 15 | 2.0 16 | 17 | 18 | True 19 | 20 | 21 | True 22 | 23 | 24 | -------------------------------------------------------------------------------- /NModbus4.Serial/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("NModbus4.Serial")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("NModbus4.Serial")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | -------------------------------------------------------------------------------- /NModbus4.Serial/SerialPortAdapter.cs: -------------------------------------------------------------------------------- 1 | namespace Modbus.Serial 2 | { 3 | using System; 4 | using System.Diagnostics; 5 | using System.IO.Ports; 6 | 7 | using Modbus.IO; 8 | 9 | /// 10 | /// Concrete Implementor - http://en.wikipedia.org/wiki/Bridge_Pattern 11 | /// 12 | public class SerialPortAdapter : IStreamResource 13 | { 14 | private const string NewLine = "\r\n"; 15 | private SerialPort _serialPort; 16 | 17 | public SerialPortAdapter(SerialPort serialPort) 18 | { 19 | Debug.Assert(serialPort != null, "Argument serialPort cannot be null."); 20 | 21 | _serialPort = serialPort; 22 | _serialPort.NewLine = NewLine; 23 | } 24 | 25 | public int InfiniteTimeout 26 | { 27 | get { return SerialPort.InfiniteTimeout; } 28 | } 29 | 30 | public int ReadTimeout 31 | { 32 | get { return _serialPort.ReadTimeout; } 33 | set { _serialPort.ReadTimeout = value; } 34 | } 35 | 36 | public int WriteTimeout 37 | { 38 | get { return _serialPort.WriteTimeout; } 39 | set { _serialPort.WriteTimeout = value; } 40 | } 41 | 42 | public void DiscardInBuffer() 43 | { 44 | _serialPort.DiscardInBuffer(); 45 | } 46 | 47 | public int Read(byte[] buffer, int offset, int count) 48 | { 49 | return _serialPort.Read(buffer, offset, count); 50 | } 51 | 52 | public void Write(byte[] buffer, int offset, int count) 53 | { 54 | _serialPort.Write(buffer, offset, count); 55 | } 56 | 57 | public void Dispose() 58 | { 59 | Dispose(true); 60 | GC.SuppressFinalize(this); 61 | } 62 | 63 | protected virtual void Dispose(bool disposing) 64 | { 65 | if (disposing) 66 | { 67 | _serialPort?.Dispose(); 68 | _serialPort = null; 69 | } 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /NModbus4.Serial/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0-alpha1", 3 | "description": "NModbus4.Serial Class Library", 4 | "authors": [ "Dmitry Turin" ], 5 | 6 | "packOptions": { 7 | "tags": [ "modbus", "nmodbus", "serial", "master", "slave" ], 8 | "projectUrl": "https://github.com/NModbus4/NModbus4", 9 | "licenseUrl": "http://opensource.org/licenses/MIT" 10 | }, 11 | 12 | "dependencies": { 13 | "NModbus4": "3.0.0-*" 14 | }, 15 | 16 | "buildOptions": { 17 | "warningsAsErrors": true 18 | }, 19 | 20 | "frameworks": { 21 | "net45": {} 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /NModbus4.TestDriver/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /NModbus4.TestDriver/NModbus4.TestDriver.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {7E6D4E84-848F-444C-AD5C-852D0665E091} 8 | Exe 9 | Properties 10 | NModbus4.TestDriver 11 | NModbus4.TestDriver 12 | v4.5 13 | 512 14 | 15 | 16 | AnyCPU 17 | true 18 | full 19 | false 20 | bin\Debug 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | AnyCPU 27 | pdbonly 28 | true 29 | bin\Release 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | 36 | ..\packages\NModbus4.2.1.0\lib\net40\NModbus4.dll 37 | True 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 63 | -------------------------------------------------------------------------------- /NModbus4.TestDriver/Program.cs: -------------------------------------------------------------------------------- 1 | namespace NModbus4.TestDriver 2 | { 3 | using System; 4 | using System.Net.Sockets; 5 | using Modbus.Device; 6 | 7 | class Program 8 | { 9 | static void Main(string[] args) 10 | { 11 | try 12 | { 13 | using (TcpClient client = new TcpClient("127.0.0.1", 502)) 14 | { 15 | client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true); 16 | 17 | var master = ModbusIpMaster.CreateIp(client); 18 | // read five input values 19 | ushort startAddress = 100; 20 | ushort numInputs = 5; 21 | bool[] inputs = master.ReadInputs(startAddress, numInputs); 22 | 23 | for (int i = 0; i < numInputs; i++) 24 | { 25 | Console.WriteLine($"Input {(startAddress + i)}={(inputs[i] ? 1 : 0)}"); 26 | } 27 | 28 | while (true) 29 | { 30 | } 31 | } 32 | } 33 | catch (Exception e) 34 | { 35 | Console.WriteLine(e.Message); 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /NModbus4.TestDriver/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("NModbus4.TestDriver")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("NModbus4.TestDriver")] 13 | [assembly: AssemblyCopyright("Copyright © 2015")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("7e6d4e84-848f-444c-ad5c-852d0665e091")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /NModbus4.TestDriver/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /NModbus4.UnitTests/Data/BoolModbusDataCollectionFixture.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.ObjectModel; 2 | using Modbus.Data; 3 | using Xunit; 4 | 5 | namespace Modbus.UnitTests.Data 6 | { 7 | public class BoolModbusDataCollectionFixture : ModbusDataCollectionFixture 8 | { 9 | [Fact] 10 | public void Remove_FromReadOnly() 11 | { 12 | bool[] source = { false, false, false, true, false, false }; 13 | var col = new ModbusDataCollection(new ReadOnlyCollection(source)); 14 | int expectedCount = source.Length; 15 | 16 | Assert.True(col.Remove(source[3])); 17 | 18 | Assert.Equal(expectedCount, col.Count); 19 | } 20 | 21 | protected override bool[] GetArray() 22 | { 23 | return new[] { false, false, true, false, false }; 24 | } 25 | 26 | protected override bool GetNonExistentElement() 27 | { 28 | return true; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /NModbus4.UnitTests/Data/DataStoreEventArgsFixture.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using Modbus.Data; 4 | using Xunit; 5 | 6 | namespace Modbus.UnitTests.Data 7 | { 8 | public class DataStoreEventArgsFixture 9 | { 10 | [Fact] 11 | public void CreateDataStoreEventArgs() 12 | { 13 | var eventArgs = DataStoreEventArgs.CreateDataStoreEventArgs(5, ModbusDataType.HoldingRegister, 14 | new ushort[] { 1, 2, 3 }); 15 | Assert.Equal(ModbusDataType.HoldingRegister, eventArgs.ModbusDataType); 16 | Assert.Equal(5, eventArgs.StartAddress); 17 | Assert.Equal(new ushort[] { 1, 2, 3 }, eventArgs.Data.B.ToArray()); 18 | } 19 | 20 | [Fact] 21 | public void CreateDataStoreEventArgs_InvalidType() 22 | { 23 | Assert.Throws(() => DataStoreEventArgs.CreateDataStoreEventArgs(5, ModbusDataType.HoldingRegister, 24 | new int[] { 1, 2, 3 })); 25 | } 26 | 27 | [Fact] 28 | public void CreateDataStoreEventArgs_DataNull() 29 | { 30 | Assert.Throws(() => 31 | DataStoreEventArgs.CreateDataStoreEventArgs(5, ModbusDataType.HoldingRegister, 32 | default(ushort[]))); 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /NModbus4.UnitTests/Data/ModbusDataCollectionFixture.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.ObjectModel; 4 | using Modbus.Data; 5 | using Xunit; 6 | 7 | namespace Modbus.UnitTests.Data 8 | { 9 | public abstract class ModbusDataCollectionFixture 10 | { 11 | [Fact] 12 | public void DefaultContstructor() 13 | { 14 | var col = new ModbusDataCollection(); 15 | Assert.NotEmpty(col); 16 | Assert.Equal(1, col.Count); 17 | 18 | col.Add(default(TData)); 19 | Assert.Equal(2, col.Count); 20 | } 21 | 22 | [Fact] 23 | public void ContstructorWithParams() 24 | { 25 | TData[] source = GetArray(); 26 | var col = new ModbusDataCollection(source); 27 | Assert.Equal(source.Length + 1, col.Count); 28 | Assert.NotEmpty(col); 29 | 30 | col.Add(default(TData)); 31 | Assert.Equal(source.Length + 2, col.Count); 32 | } 33 | 34 | [Fact] 35 | public void ContstructorWithIList() 36 | { 37 | List source = GetList(); 38 | int expectedCount = source.Count; 39 | 40 | var col = new ModbusDataCollection(source); 41 | 42 | Assert.Equal(expectedCount + 1, source.Count); 43 | Assert.Equal(expectedCount + 1, col.Count); 44 | 45 | source.Insert(0, default(TData)); 46 | Assert.Equal(source, col); 47 | } 48 | 49 | [Fact] 50 | public void ContstructorWithIList_FromReadOnlyList() 51 | { 52 | List source = GetList(); 53 | var readOnly = new ReadOnlyCollection(source); 54 | int expectedCount = source.Count; 55 | 56 | var col = new ModbusDataCollection(readOnly); 57 | 58 | Assert.Equal(expectedCount, source.Count); 59 | Assert.Equal(expectedCount + 1, col.Count); 60 | 61 | source.Insert(0, default(TData)); 62 | Assert.Equal(source, col); 63 | } 64 | 65 | [Fact] 66 | public void SetZeroElementUsingItem() 67 | { 68 | var source = GetArray(); 69 | var col = new ModbusDataCollection(source); 70 | Assert.Throws(() => col[0] = source[3]); 71 | } 72 | 73 | [Fact] 74 | public void ZeroElementUsingItem_Negative() 75 | { 76 | var source = GetArray(); 77 | var col = new ModbusDataCollection(source); 78 | 79 | Assert.Throws(() => col[0] = source[3]); 80 | Assert.Throws(() => col.Insert(0, source[3])); 81 | Assert.Throws(() => col.RemoveAt(0)); 82 | 83 | // Remove forst zero/false 84 | Assert.Throws(() => col.Remove(default(TData))); 85 | } 86 | 87 | [Fact] 88 | public void Clear() 89 | { 90 | var col = new ModbusDataCollection(GetArray()); 91 | col.Clear(); 92 | 93 | Assert.Equal(1, col.Count); 94 | } 95 | 96 | [Fact] 97 | public void Remove() 98 | { 99 | List source = GetList(); 100 | var col = new ModbusDataCollection(source); 101 | int expectedCount = source.Count - 1; 102 | 103 | Assert.True(col.Remove(source[3])); 104 | 105 | Assert.Equal(expectedCount, col.Count); 106 | Assert.Equal(expectedCount, source.Count); 107 | Assert.Equal(source, col); 108 | } 109 | 110 | protected abstract TData[] GetArray(); 111 | 112 | protected abstract TData GetNonExistentElement(); 113 | 114 | protected List GetList() 115 | { 116 | return new List(GetArray()); 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /NModbus4.UnitTests/Data/RegisterCollectionFixture.cs: -------------------------------------------------------------------------------- 1 | using Modbus.Data; 2 | using Xunit; 3 | 4 | namespace Modbus.UnitTests.Data 5 | { 6 | public class RegisterCollectionFixture 7 | { 8 | [Fact] 9 | public void ByteCount() 10 | { 11 | RegisterCollection col = new RegisterCollection(1, 2, 3); 12 | Assert.Equal(6, col.ByteCount); 13 | } 14 | 15 | [Fact] 16 | public void NewRegisterCollection() 17 | { 18 | RegisterCollection col = new RegisterCollection(5, 3, 4, 6); 19 | Assert.NotNull(col); 20 | Assert.Equal(4, col.Count); 21 | Assert.Equal(5, col[0]); 22 | } 23 | 24 | [Fact] 25 | public void NewRegisterCollectionFromBytes() 26 | { 27 | RegisterCollection col = new RegisterCollection(new byte[] { 0, 1, 0, 2, 0, 3 }); 28 | Assert.NotNull(col); 29 | Assert.Equal(3, col.Count); 30 | Assert.Equal(1, col[0]); 31 | Assert.Equal(2, col[1]); 32 | Assert.Equal(3, col[2]); 33 | } 34 | 35 | [Fact] 36 | public void RegisterCollectionNetworkBytes() 37 | { 38 | RegisterCollection col = new RegisterCollection(5, 3, 4, 6); 39 | byte[] bytes = col.NetworkBytes; 40 | Assert.NotNull(bytes); 41 | Assert.Equal(8, bytes.Length); 42 | Assert.Equal(new byte[] { 0, 5, 0, 3, 0, 4, 0, 6 }, bytes); 43 | } 44 | 45 | [Fact] 46 | public void RegisterCollectionEmpty() 47 | { 48 | RegisterCollection col = new RegisterCollection(); 49 | Assert.NotNull(col); 50 | Assert.Equal(0, col.NetworkBytes.Length); 51 | } 52 | 53 | [Fact] 54 | public void ModifyRegister() 55 | { 56 | RegisterCollection col = new RegisterCollection(1, 2, 3, 4); 57 | col[0] = 5; 58 | } 59 | 60 | [Fact] 61 | public void AddRegister() 62 | { 63 | RegisterCollection col = new RegisterCollection(); 64 | Assert.Equal(0, col.Count); 65 | col.Add(45); 66 | Assert.Equal(1, col.Count); 67 | } 68 | 69 | [Fact] 70 | public void RemoveRegister() 71 | { 72 | RegisterCollection col = new RegisterCollection(3, 4, 5); 73 | Assert.Equal(3, col.Count); 74 | col.RemoveAt(2); 75 | Assert.Equal(2, col.Count); 76 | } 77 | } 78 | } -------------------------------------------------------------------------------- /NModbus4.UnitTests/Data/UshortModbusDataCollectionFixture.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.ObjectModel; 2 | using Modbus.Data; 3 | using Xunit; 4 | 5 | namespace Modbus.UnitTests.Data 6 | { 7 | public class UshortModbusDataCollectionFixture : ModbusDataCollectionFixture 8 | { 9 | [Fact] 10 | public void Remove_FromReadOnly() 11 | { 12 | ushort[] source = this.GetArray(); 13 | var col = new ModbusDataCollection(new ReadOnlyCollection(source)); 14 | int expectedCount = source.Length; 15 | 16 | Assert.False(col.Remove(this.GetNonExistentElement())); 17 | Assert.True(col.Remove(source[3])); 18 | 19 | Assert.Equal(expectedCount, col.Count); 20 | } 21 | 22 | protected override ushort[] GetArray() 23 | { 24 | return new ushort[] { 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; 25 | } 26 | 27 | protected override ushort GetNonExistentElement() 28 | { 29 | return 42; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /NModbus4.UnitTests/Device/ModbusMasterFixture.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using Modbus.Device; 4 | using Modbus.IO; 5 | using Moq; 6 | using Xunit; 7 | 8 | namespace Modbus.UnitTests.Device 9 | { 10 | public class ModbusMasterFixture 11 | { 12 | private static IStreamResource StreamRsource => new Mock(MockBehavior.Strict).Object; 13 | 14 | private ModbusSerialMaster Master => ModbusSerialMaster.CreateRtu(StreamRsource); 15 | 16 | [Fact] 17 | public void ReadCoils() 18 | { 19 | Assert.Throws(() => Master.ReadCoils(1, 1, 0)); 20 | Assert.Throws(() => Master.ReadCoils(1, 1, 2001)); 21 | } 22 | 23 | [Fact] 24 | public void ReadInputs() 25 | { 26 | Assert.Throws(() => Master.ReadInputs(1, 1, 0)); 27 | Assert.Throws(() => Master.ReadInputs(1, 1, 2001)); 28 | } 29 | 30 | [Fact] 31 | public void ReadHoldingRegisters() 32 | { 33 | Assert.Throws(() => Master.ReadHoldingRegisters(1, 1, 0)); 34 | Assert.Throws(() => Master.ReadHoldingRegisters(1, 1, 126)); 35 | } 36 | 37 | [Fact] 38 | public void ReadInputRegisters() 39 | { 40 | Assert.Throws(() => Master.ReadInputRegisters(1, 1, 0)); 41 | Assert.Throws(() => Master.ReadInputRegisters(1, 1, 126)); 42 | } 43 | 44 | [Fact] 45 | public void WriteMultipleRegisters() 46 | { 47 | Assert.Throws(() => Master.WriteMultipleRegisters(1, 1, null)); 48 | Assert.Throws(() => Master.WriteMultipleRegisters(1, 1, new ushort[0])); 49 | Assert.Throws(() => Master.WriteMultipleRegisters(1, 1, Enumerable.Repeat(1, 124).ToArray())); 50 | } 51 | 52 | [Fact] 53 | public void WriteMultipleCoils() 54 | { 55 | Assert.Throws(() => Master.WriteMultipleCoils(1, 1, null)); 56 | Assert.Throws(() => Master.WriteMultipleCoils(1, 1, new bool[0])); 57 | Assert.Throws(() => Master.WriteMultipleCoils(1, 1, Enumerable.Repeat(false, 1969).ToArray())); 58 | } 59 | 60 | [Fact] 61 | public void ReadWriteMultipleRegisters() 62 | { 63 | // validate numberOfPointsToRead 64 | Assert.Throws(() => Master.ReadWriteMultipleRegisters(1, 1, 0, 1, new ushort[] { 1 })); 65 | Assert.Throws(() => Master.ReadWriteMultipleRegisters(1, 1, 126, 1, new ushort[] { 1 })); 66 | 67 | // validate writeData 68 | Assert.Throws(() => Master.ReadWriteMultipleRegisters(1, 1, 1, 1, null)); 69 | Assert.Throws(() => Master.ReadWriteMultipleRegisters(1, 1, 1, 1, new ushort[0])); 70 | Assert.Throws(() => Master.ReadWriteMultipleRegisters(1, 1, 1, 1, Enumerable.Repeat(1, 122).ToArray())); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /NModbus4.UnitTests/Device/TcpConnectionEventArgsFixture.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Modbus.Device; 3 | using Xunit; 4 | 5 | namespace Modbus.UnitTests.Device 6 | { 7 | public class TcpConnectionEventArgsFixture 8 | { 9 | [Fact] 10 | public void TcpConnectionEventArgs_NullEndPoint() 11 | { 12 | Assert.Throws(() => new TcpConnectionEventArgs(null)); 13 | } 14 | 15 | [Fact] 16 | public void TcpConnectionEventArgs_EmptyEndPoint() 17 | { 18 | Assert.Throws(() => new TcpConnectionEventArgs(string.Empty)); 19 | } 20 | 21 | [Fact] 22 | public void TcpConnectionEventArgs() 23 | { 24 | var args = new TcpConnectionEventArgs("foo"); 25 | 26 | Assert.Equal("foo", args.EndPoint); 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /NModbus4.UnitTests/IO/EmptyTransportFixture.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Modbus.IO; 3 | using Modbus.Message; 4 | using Xunit; 5 | 6 | namespace Modbus.UnitTests.IO 7 | { 8 | public static class EmptyTransportFixture 9 | { 10 | [Fact] 11 | public static void Negative() 12 | { 13 | var transport = new EmptyTransport(); 14 | Assert.Throws(() => transport.ReadRequest()); 15 | Assert.Throws(() => transport.ReadResponse()); 16 | Assert.Throws(() => transport.BuildMessageFrame(null)); 17 | Assert.Throws(() => transport.Write(null)); 18 | Assert.Throws(() => transport.OnValidateResponse(null, null)); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /NModbus4.UnitTests/IO/ModbusAsciiTransportFixture.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Text; 3 | using Modbus.IO; 4 | using Modbus.Message; 5 | using Moq; 6 | using Xunit; 7 | 8 | namespace Modbus.UnitTests.IO 9 | { 10 | public class ModbusAsciiTransportFixture 11 | { 12 | private static IStreamResource StreamResource => new Mock(MockBehavior.Strict).Object; 13 | 14 | [Fact] 15 | public void BuildMessageFrame() 16 | { 17 | byte[] expected = { 58, 48, 50, 48, 49, 48, 48, 48, 48, 48, 48, 48, 49, 70, 67, 13, 10 }; 18 | var request = new ReadCoilsInputsRequest(Modbus.ReadCoils, 2, 0, 1); 19 | var actual = new ModbusAsciiTransport(StreamResource) 20 | .BuildMessageFrame(request); 21 | 22 | Assert.Equal(expected, actual); 23 | } 24 | 25 | [Fact] 26 | public void ReadRequestResponse() 27 | { 28 | var mock = new Mock(MockBehavior.Strict); 29 | IStreamResource stream = mock.Object; 30 | var transport = new ModbusAsciiTransport(stream); 31 | int calls = 0; 32 | byte[] bytes = Encoding.ASCII.GetBytes(":110100130025B6\r\n"); 33 | 34 | mock.Setup(s => s.Read(It.Is(x => x.Length == 1), 0, 1)) 35 | .Returns((byte[] buffer, int offset, int count) => 36 | { 37 | buffer[offset] = bytes[calls++]; 38 | return 1; 39 | }); 40 | 41 | Assert.Equal(new byte[] { 17, 1, 0, 19, 0, 37, 182 }, transport.ReadRequestResponse()); 42 | mock.VerifyAll(); 43 | } 44 | 45 | [Fact] 46 | public void ReadRequestResponseNotEnoughBytes() 47 | { 48 | var mock = new Mock(MockBehavior.Strict); 49 | IStreamResource stream = mock.Object; 50 | var transport = new ModbusAsciiTransport(stream); 51 | int calls = 0; 52 | byte[] bytes = Encoding.ASCII.GetBytes(":10\r\n"); 53 | 54 | mock.Setup(s => s.Read(It.Is(x => x.Length == 1), 0, 1)) 55 | .Returns((byte[] buffer, int offset, int count) => 56 | { 57 | buffer[offset] = bytes[calls++]; 58 | return 1; 59 | }); 60 | 61 | Assert.Throws(() => transport.ReadRequestResponse()); 62 | mock.VerifyAll(); 63 | } 64 | 65 | [Fact] 66 | public void ChecksumsMatchSucceed() 67 | { 68 | var transport = new ModbusAsciiTransport(StreamResource); 69 | var message = new ReadCoilsInputsRequest(Modbus.ReadCoils, 17, 19, 37); 70 | byte[] frame = { 17, Modbus.ReadCoils, 0, 19, 0, 37, 182 }; 71 | 72 | Assert.True(transport.ChecksumsMatch(message, frame)); 73 | } 74 | 75 | [Fact] 76 | public void ChecksumsMatchFail() 77 | { 78 | var transport = new ModbusAsciiTransport(StreamResource); 79 | var message = new ReadCoilsInputsRequest(Modbus.ReadCoils, 17, 19, 37); 80 | byte[] frame = { 17, Modbus.ReadCoils, 0, 19, 0, 37, 181 }; 81 | 82 | Assert.False(transport.ChecksumsMatch(message, frame)); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /NModbus4.UnitTests/IO/ModbusSerialTransportFixture.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using Modbus.Data; 4 | using Modbus.IO; 5 | using Modbus.Message; 6 | using Modbus.UnitTests.Message; 7 | using Modbus.Utility; 8 | using Moq; 9 | using Xunit; 10 | 11 | namespace Modbus.UnitTests.IO 12 | { 13 | public class ModbusSerialTransportFixture 14 | { 15 | private static IStreamResource StreamResource => new Mock(MockBehavior.Strict).Object; 16 | 17 | [Fact] 18 | public void CreateResponse() 19 | { 20 | var transport = new ModbusAsciiTransport(StreamResource); 21 | var expectedResponse = new ReadCoilsInputsResponse(Modbus.ReadCoils, 2, 1, new DiscreteCollection(true, false, false, false, false, false, false, true)); 22 | byte lrc = ModbusUtility.CalculateLrc(expectedResponse.MessageFrame); 23 | var response = transport.CreateResponse(new byte[] { 2, Modbus.ReadCoils, 1, 129, lrc }); 24 | 25 | Assert.IsType(response); 26 | ModbusMessageFixture.AssertModbusMessagePropertiesAreEqual(expectedResponse, response); 27 | } 28 | 29 | [Fact] 30 | public void CreateResponseErroneousLrc() 31 | { 32 | var transport = new ModbusAsciiTransport(StreamResource) { CheckFrame = true }; 33 | var frame = new byte[] { 19, Modbus.ReadCoils, 0, 0, 0, 2, 115 }; 34 | 35 | Assert.Throws( 36 | () => transport.CreateResponse(frame)); 37 | } 38 | 39 | [Fact] 40 | public void CreateResponseErroneousLrcDoNotCheckFrame() 41 | { 42 | var transport = new ModbusAsciiTransport(StreamResource) { CheckFrame = false }; 43 | 44 | transport.CreateResponse(new byte[] { 19, Modbus.ReadCoils, 0, 0, 0, 2, 115 }); 45 | } 46 | 47 | /// 48 | /// When using the serial RTU protocol the beginning of the message could get mangled leading to an unsupported message type. 49 | /// We want to be sure to try the message again so clear the RX buffer and try again. 50 | /// 51 | [Fact] 52 | public void UnicastMessage_PurgeReceiveBuffer() 53 | { 54 | var mock = new Mock(MockBehavior.Strict); 55 | IStreamResource serialResource = mock.Object; 56 | var transport = new ModbusRtuTransport(serialResource); 57 | 58 | mock.Setup(s => s.DiscardInBuffer()); 59 | mock.Setup(s => s.Write(It.IsAny(), 0, 0)); 60 | 61 | serialResource.DiscardInBuffer(); 62 | serialResource.Write(null, 0, 0); 63 | 64 | // mangled response 65 | mock.Setup(s => s.Read(It.Is(x => x.Length == 4), 0, 4)).Returns(4); 66 | 67 | serialResource.DiscardInBuffer(); 68 | serialResource.Write(null, 0, 0); 69 | 70 | // normal response 71 | var response = new ReadCoilsInputsResponse(Modbus.ReadCoils, 2, 1, new DiscreteCollection(true, false, true, false, false, false, false, false)); 72 | 73 | // write request 74 | mock.Setup(s => s.Write(It.Is(x => x.Length == 8), 0, 8)); 75 | 76 | // read header 77 | mock.Setup(s => s.Read(It.Is(x => x.Length == 4), 0, 4)) 78 | .Returns((byte[] buf, int offset, int count) => 79 | { 80 | Array.Copy(response.MessageFrame, 0, buf, 0, 4); 81 | return 4; 82 | }); 83 | 84 | // read remainder 85 | mock.Setup(s => s.Read(It.Is(x => x.Length == 2), 0, 2)) 86 | .Returns((byte[] buf, int offset, int count) => 87 | { 88 | Array.Copy(ModbusUtility.CalculateCrc(response.MessageFrame), 0, buf, 0, 2); 89 | return 2; 90 | }); 91 | 92 | var request = new ReadCoilsInputsRequest(Modbus.ReadCoils, 2, 3, 4); 93 | var actualResponse = transport.UnicastMessage(request); 94 | 95 | ModbusMessageFixture.AssertModbusMessagePropertiesAreEqual(response, actualResponse); 96 | mock.VerifyAll(); 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /NModbus4.UnitTests/IO/UdpClientAdapterFixture.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net.Sockets; 3 | using Modbus.IO; 4 | using Xunit; 5 | 6 | namespace Modbus.UnitTests.IO 7 | { 8 | public class UdpClientAdapterFixture 9 | { 10 | [Fact] 11 | public void Read_ArgumentValidation() 12 | { 13 | var adapter = new UdpClientAdapter(new UdpClient()); 14 | 15 | // buffer 16 | Assert.Throws(() => adapter.Read(null, 1, 1)); 17 | 18 | // offset 19 | Assert.Throws(() => adapter.Read(new byte[2], -1, 2)); 20 | Assert.Throws(() => adapter.Read(new byte[2], 3, 3)); 21 | 22 | Assert.Throws(() => adapter.Read(new byte[2], 0, -1)); 23 | Assert.Throws(() => adapter.Read(new byte[2], 1, 2)); 24 | } 25 | 26 | [Fact] 27 | public void Write_ArgumentValidation() 28 | { 29 | var adapter = new UdpClientAdapter(new UdpClient()); 30 | 31 | // buffer 32 | Assert.Throws(() => adapter.Write(null, 1, 1)); 33 | 34 | // offset 35 | Assert.Throws(() => adapter.Write(new byte[2], -1, 2)); 36 | Assert.Throws(() => adapter.Write(new byte[2], 3, 3)); 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /NModbus4.UnitTests/InvalidModbusRequestExceptionFixture.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | #if NET46 3 | using System.Runtime.Serialization.Formatters.Binary; 4 | #endif 5 | using Xunit; 6 | 7 | namespace Modbus.UnitTests 8 | { 9 | public class InvalidModbusRequestExceptionFixture 10 | { 11 | [Fact] 12 | public void ConstructorWithExceptionCode() 13 | { 14 | var e = new InvalidModbusRequestException(Modbus.SlaveDeviceBusy); 15 | Assert.Equal($"Modbus exception code {Modbus.SlaveDeviceBusy}.", e.Message); 16 | Assert.Equal(Modbus.SlaveDeviceBusy, e.ExceptionCode); 17 | Assert.Null(e.InnerException); 18 | } 19 | 20 | [Fact] 21 | public void ConstructorWithExceptionCodeAndInnerException() 22 | { 23 | var inner = new IOException("Bar"); 24 | var e = new InvalidModbusRequestException(42, inner); 25 | Assert.Equal("Modbus exception code 42.", e.Message); 26 | Assert.Equal(42, e.ExceptionCode); 27 | Assert.Same(inner, e.InnerException); 28 | } 29 | 30 | [Fact] 31 | public void ConstructorWithMessageAndExceptionCode() 32 | { 33 | var e = new InvalidModbusRequestException("Hello World", Modbus.IllegalFunction); 34 | Assert.Equal("Hello World", e.Message); 35 | Assert.Equal(Modbus.IllegalFunction, e.ExceptionCode); 36 | Assert.Null(e.InnerException); 37 | } 38 | 39 | [Fact] 40 | public void ConstructorWithCustomMessageAndSlaveExceptionResponse() 41 | { 42 | var inner = new IOException("Bar"); 43 | var e = new InvalidModbusRequestException("Hello World", Modbus.IllegalDataAddress, inner); 44 | Assert.Equal("Hello World", e.Message); 45 | Assert.Equal(Modbus.IllegalDataAddress, e.ExceptionCode); 46 | Assert.Same(inner, e.InnerException); 47 | } 48 | 49 | #if NET46 50 | [Fact] 51 | public void Serializable() 52 | { 53 | var formatter = new BinaryFormatter(); 54 | var e = new InvalidModbusRequestException(Modbus.SlaveDeviceBusy); 55 | 56 | using (var stream = new MemoryStream()) 57 | { 58 | formatter.Serialize(stream, e); 59 | stream.Position = 0; 60 | 61 | var e2 = (InvalidModbusRequestException)formatter.Deserialize(stream); 62 | Assert.NotNull(e2); 63 | Assert.Equal(Modbus.SlaveDeviceBusy, e2.ExceptionCode); 64 | Assert.Equal($"Modbus exception code {Modbus.SlaveDeviceBusy}.", e2.Message); 65 | } 66 | } 67 | #endif 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /NModbus4.UnitTests/Message/DiagnosticsRequestResponseFixture.cs: -------------------------------------------------------------------------------- 1 | using Modbus.Data; 2 | using Modbus.Message; 3 | using Xunit; 4 | 5 | namespace Modbus.UnitTests.Message 6 | { 7 | public class DiagnosticsRequestResponseFixture 8 | { 9 | [Fact] 10 | public void ToString_Test() 11 | { 12 | DiagnosticsRequestResponse response; 13 | 14 | response = new DiagnosticsRequestResponse(Modbus.DiagnosticsReturnQueryData, 3, new RegisterCollection(5)); 15 | Assert.Equal("Diagnostics message, sub-function return query data - {5}.", response.ToString()); 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /NModbus4.UnitTests/Message/MessageUtility.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Modbus.UnitTests.Message 5 | { 6 | public static class MessageUtility 7 | { 8 | /// 9 | /// Creates a collection initialized to a default value. 10 | /// 11 | public static T CreateDefaultCollection(V defaultValue, int size) 12 | where T : ICollection, new() 13 | { 14 | if (size < 0) 15 | { 16 | throw new ArgumentOutOfRangeException("Collection size cannot be less than 0."); 17 | } 18 | 19 | T col = new T(); 20 | 21 | for (int i = 0; i < size; i++) 22 | { 23 | col.Add(defaultValue); 24 | } 25 | 26 | return col; 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /NModbus4.UnitTests/Message/ModbusMessageFixture.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Reflection; 4 | using Modbus.Message; 5 | using Xunit; 6 | 7 | namespace Modbus.UnitTests.Message 8 | { 9 | public class ModbusMessageFixture 10 | { 11 | [Fact] 12 | public void ProtocolDataUnitReadCoilsRequest() 13 | { 14 | AbstractModbusMessage message = new ReadCoilsInputsRequest(Modbus.ReadCoils, 1, 100, 9); 15 | byte[] expectedResult = { Modbus.ReadCoils, 0, 100, 0, 9 }; 16 | Assert.Equal(expectedResult, message.ProtocolDataUnit); 17 | } 18 | 19 | [Fact] 20 | public void MessageFrameReadCoilsRequest() 21 | { 22 | AbstractModbusMessage message = new ReadCoilsInputsRequest(Modbus.ReadCoils, 1, 2, 3); 23 | byte[] expectedMessageFrame = { 1, Modbus.ReadCoils, 0, 2, 0, 3 }; 24 | Assert.Equal(expectedMessageFrame, message.MessageFrame); 25 | } 26 | 27 | [Fact] 28 | public void ModbusMessageToStringOverriden() 29 | { 30 | var messageTypes = from message in typeof(AbstractModbusMessage).GetTypeInfo().Assembly.GetTypes() 31 | let typeInfo = message.GetTypeInfo() 32 | where !typeInfo.IsAbstract && typeInfo.IsSubclassOf(typeof(AbstractModbusMessage)) 33 | select message; 34 | 35 | foreach (Type messageType in messageTypes) 36 | { 37 | Assert.NotNull( 38 | messageType.GetMethod("ToString", 39 | BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)); 40 | } 41 | } 42 | 43 | internal static void AssertModbusMessagePropertiesAreEqual(IModbusMessage obj1, IModbusMessage obj2) 44 | { 45 | Assert.Equal(obj1.FunctionCode, obj2.FunctionCode); 46 | Assert.Equal(obj1.SlaveAddress, obj2.SlaveAddress); 47 | Assert.Equal(obj1.MessageFrame, obj2.MessageFrame); 48 | Assert.Equal(obj1.ProtocolDataUnit, obj2.ProtocolDataUnit); 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /NModbus4.UnitTests/Message/ModbusMessageImplFixture.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Modbus.Message; 3 | using Xunit; 4 | 5 | namespace Modbus.UnitTests.Message 6 | { 7 | public class ModbusMessageImplFixture 8 | { 9 | [Fact] 10 | public void ModbusMessageCtorInitializesProperties() 11 | { 12 | ModbusMessageImpl messageImpl = new ModbusMessageImpl(5, Modbus.ReadCoils); 13 | Assert.Equal(5, messageImpl.SlaveAddress); 14 | Assert.Equal(Modbus.ReadCoils, messageImpl.FunctionCode); 15 | } 16 | 17 | [Fact] 18 | public void Initialize() 19 | { 20 | ModbusMessageImpl messageImpl = new ModbusMessageImpl(); 21 | messageImpl.Initialize(new byte[] { 1, 2, 9, 9, 9, 9 }); 22 | Assert.Equal(1, messageImpl.SlaveAddress); 23 | Assert.Equal(2, messageImpl.FunctionCode); 24 | } 25 | 26 | [Fact] 27 | public void ChecckInitializeFrameNull() 28 | { 29 | ModbusMessageImpl messageImpl = new ModbusMessageImpl(); 30 | Assert.Throws(() => messageImpl.Initialize(null)); 31 | } 32 | 33 | [Fact] 34 | public void InitializeInvalidFrame() 35 | { 36 | ModbusMessageImpl messageImpl = new ModbusMessageImpl(); 37 | Assert.Throws(() => messageImpl.Initialize(new byte[] { 1 })); 38 | } 39 | 40 | [Fact] 41 | public void ProtocolDataUnit() 42 | { 43 | ModbusMessageImpl messageImpl = new ModbusMessageImpl(11, Modbus.ReadCoils); 44 | byte[] expectedResult = { Modbus.ReadCoils }; 45 | Assert.Equal(expectedResult, messageImpl.ProtocolDataUnit); 46 | } 47 | 48 | [Fact] 49 | public void MessageFrame() 50 | { 51 | ModbusMessageImpl messageImpl = new ModbusMessageImpl(11, Modbus.ReadHoldingRegisters); 52 | byte[] expectedMessageFrame = { 11, Modbus.ReadHoldingRegisters }; 53 | Assert.Equal(expectedMessageFrame, messageImpl.MessageFrame); 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /NModbus4.UnitTests/Message/ModbusMessageWithDataFixture.cs: -------------------------------------------------------------------------------- 1 | using Modbus.Data; 2 | using Modbus.Message; 3 | using Xunit; 4 | 5 | namespace Modbus.UnitTests.Message 6 | { 7 | public class ModbusMessageWithDataFixture 8 | { 9 | [Fact] 10 | public void ModbusMessageWithDataFixtureCtorInitializesProperties() 11 | { 12 | AbstractModbusMessageWithData message = new ReadCoilsInputsResponse(Modbus.ReadCoils, 10, 1, 13 | new DiscreteCollection(true, false, true)); 14 | Assert.Equal(Modbus.ReadCoils, message.FunctionCode); 15 | Assert.Equal(10, message.SlaveAddress); 16 | } 17 | 18 | [Fact] 19 | public void ProtocolDataUnitReadCoilsResponse() 20 | { 21 | AbstractModbusMessageWithData message = new ReadCoilsInputsResponse(Modbus.ReadCoils, 1, 2, 22 | new DiscreteCollection(true)); 23 | byte[] expectedResult = { 1, 2, 1 }; 24 | Assert.Equal(expectedResult, message.ProtocolDataUnit); 25 | } 26 | 27 | [Fact] 28 | public void DataReadCoilsResponse() 29 | { 30 | DiscreteCollection col = new DiscreteCollection(false, true, false, true, false, true, false, false, false, 31 | false); 32 | AbstractModbusMessageWithData message = new ReadCoilsInputsResponse(Modbus.ReadCoils, 11, 1, col); 33 | Assert.Equal(col.Count, message.Data.Count); 34 | Assert.Equal(col.NetworkBytes, message.Data.NetworkBytes); 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /NModbus4.UnitTests/Message/ReadCoilsInputsRequestFixture.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Modbus.Message; 3 | using Xunit; 4 | 5 | namespace Modbus.UnitTests.Message 6 | { 7 | public class ReadCoilsInputsRequestFixture 8 | { 9 | [Fact] 10 | public void CreateReadCoilsRequest() 11 | { 12 | ReadCoilsInputsRequest request = new ReadCoilsInputsRequest(Modbus.ReadCoils, 5, 1, 10); 13 | Assert.Equal(Modbus.ReadCoils, request.FunctionCode); 14 | Assert.Equal(5, request.SlaveAddress); 15 | Assert.Equal(1, request.StartAddress); 16 | Assert.Equal(10, request.NumberOfPoints); 17 | } 18 | 19 | [Fact] 20 | public void CreateReadInputsRequest() 21 | { 22 | ReadCoilsInputsRequest request = new ReadCoilsInputsRequest(Modbus.ReadInputs, 5, 1, 10); 23 | Assert.Equal(Modbus.ReadInputs, request.FunctionCode); 24 | Assert.Equal(5, request.SlaveAddress); 25 | Assert.Equal(1, request.StartAddress); 26 | Assert.Equal(10, request.NumberOfPoints); 27 | } 28 | 29 | [Fact] 30 | public void CreateReadCoilsInputsRequestTooMuchData() 31 | { 32 | Assert.Throws(() => new ReadCoilsInputsRequest(Modbus.ReadCoils, 1, 2, Modbus.MaximumDiscreteRequestResponseSize + 1)); 33 | } 34 | 35 | [Fact] 36 | public void CreateReadCoilsInputsRequestMaxSize() 37 | { 38 | ReadCoilsInputsRequest response = new ReadCoilsInputsRequest(Modbus.ReadCoils, 1, 2, 39 | Modbus.MaximumDiscreteRequestResponseSize); 40 | Assert.Equal(Modbus.MaximumDiscreteRequestResponseSize, response.NumberOfPoints); 41 | } 42 | 43 | [Fact] 44 | public void ToString_ReadCoilsRequest() 45 | { 46 | ReadCoilsInputsRequest request = new ReadCoilsInputsRequest(Modbus.ReadCoils, 5, 1, 10); 47 | 48 | Assert.Equal("Read 10 coils starting at address 1.", request.ToString()); 49 | } 50 | 51 | [Fact] 52 | public void ToString_ReadInputsRequest() 53 | { 54 | ReadCoilsInputsRequest request = new ReadCoilsInputsRequest(Modbus.ReadInputs, 5, 1, 10); 55 | 56 | Assert.Equal("Read 10 inputs starting at address 1.", request.ToString()); 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /NModbus4.UnitTests/Message/ReadCoilsInputsResponseFixture.cs: -------------------------------------------------------------------------------- 1 | using Modbus.Data; 2 | using Modbus.Message; 3 | using Xunit; 4 | 5 | namespace Modbus.UnitTests.Message 6 | { 7 | public class ReadCoilsInputsResponseFixture 8 | { 9 | [Fact] 10 | public void CreateReadCoilsResponse() 11 | { 12 | ReadCoilsInputsResponse response = new ReadCoilsInputsResponse(Modbus.ReadCoils, 5, 2, 13 | new DiscreteCollection(true, true, true, true, true, true, false, false, true, true, false)); 14 | Assert.Equal(Modbus.ReadCoils, response.FunctionCode); 15 | Assert.Equal(5, response.SlaveAddress); 16 | Assert.Equal(2, response.ByteCount); 17 | DiscreteCollection col = new DiscreteCollection(true, true, true, true, true, true, false, false, true, true, 18 | false); 19 | Assert.Equal(col.NetworkBytes, response.Data.NetworkBytes); 20 | } 21 | 22 | [Fact] 23 | public void CreateReadInputsResponse() 24 | { 25 | ReadCoilsInputsResponse response = new ReadCoilsInputsResponse(Modbus.ReadInputs, 5, 2, 26 | new DiscreteCollection(true, true, true, true, true, true, false, false, true, true, false)); 27 | Assert.Equal(Modbus.ReadInputs, response.FunctionCode); 28 | Assert.Equal(5, response.SlaveAddress); 29 | Assert.Equal(2, response.ByteCount); 30 | DiscreteCollection col = new DiscreteCollection(true, true, true, true, true, true, false, false, true, true, 31 | false); 32 | Assert.Equal(col.NetworkBytes, response.Data.NetworkBytes); 33 | } 34 | 35 | [Fact] 36 | public void ToString_Coils() 37 | { 38 | ReadCoilsInputsResponse response = new ReadCoilsInputsResponse(Modbus.ReadCoils, 5, 2, 39 | new DiscreteCollection(true, true, true, true, true, true, false, false, true, true, false)); 40 | 41 | Assert.Equal("Read 11 coils - {1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0}.", response.ToString()); 42 | } 43 | 44 | [Fact] 45 | public void ToString_Inputs() 46 | { 47 | ReadCoilsInputsResponse response = new ReadCoilsInputsResponse(Modbus.ReadInputs, 5, 2, 48 | new DiscreteCollection(true, true, true, true, true, true, false, false, true, true, false)); 49 | 50 | Assert.Equal("Read 11 inputs - {1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0}.", response.ToString()); 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /NModbus4.UnitTests/Message/ReadHoldingInputRegistersRequestFixture.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Modbus.Message; 3 | using Xunit; 4 | 5 | namespace Modbus.UnitTests.Message 6 | { 7 | public class ReadHoldingInputRegistersRequestFixture 8 | { 9 | [Fact] 10 | public void CreateReadHoldingRegistersRequest() 11 | { 12 | ReadHoldingInputRegistersRequest request = new ReadHoldingInputRegistersRequest( 13 | Modbus.ReadHoldingRegisters, 5, 1, 10); 14 | Assert.Equal(Modbus.ReadHoldingRegisters, request.FunctionCode); 15 | Assert.Equal(5, request.SlaveAddress); 16 | Assert.Equal(1, request.StartAddress); 17 | Assert.Equal(10, request.NumberOfPoints); 18 | } 19 | 20 | [Fact] 21 | public void CreateReadInputRegistersRequest() 22 | { 23 | ReadHoldingInputRegistersRequest request = new ReadHoldingInputRegistersRequest(Modbus.ReadInputRegisters, 5, 24 | 1, 10); 25 | Assert.Equal(Modbus.ReadInputRegisters, request.FunctionCode); 26 | Assert.Equal(5, request.SlaveAddress); 27 | Assert.Equal(1, request.StartAddress); 28 | Assert.Equal(10, request.NumberOfPoints); 29 | } 30 | 31 | [Fact] 32 | public void CreateReadHoldingInputRegistersRequestTooMuchData() 33 | { 34 | Assert.Throws(() => new ReadHoldingInputRegistersRequest(Modbus.ReadHoldingRegisters, 1, 2, 35 | Modbus.MaximumRegisterRequestResponseSize + 1)); 36 | } 37 | 38 | [Fact] 39 | public void CreateReadHoldingInputRegistersRequestMaxSize() 40 | { 41 | ReadHoldingInputRegistersRequest response = new ReadHoldingInputRegistersRequest( 42 | Modbus.ReadHoldingRegisters, 1, 2, Modbus.MaximumRegisterRequestResponseSize); 43 | Assert.Equal(Modbus.MaximumRegisterRequestResponseSize, response.NumberOfPoints); 44 | } 45 | 46 | [Fact] 47 | public void ToString_ReadHoldingRegistersRequest() 48 | { 49 | ReadHoldingInputRegistersRequest request = new ReadHoldingInputRegistersRequest( 50 | Modbus.ReadHoldingRegisters, 5, 1, 10); 51 | 52 | Assert.Equal("Read 10 holding registers starting at address 1.", request.ToString()); 53 | } 54 | 55 | [Fact] 56 | public void ToString_ReadInputRegistersRequest() 57 | { 58 | ReadHoldingInputRegistersRequest request = new ReadHoldingInputRegistersRequest(Modbus.ReadInputRegisters, 5, 59 | 1, 10); 60 | 61 | Assert.Equal("Read 10 input registers starting at address 1.", request.ToString()); 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /NModbus4.UnitTests/Message/ReadHoldingInputRegistersResponseFixture.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Modbus.Data; 3 | using Modbus.Message; 4 | using Xunit; 5 | 6 | namespace Modbus.UnitTests.Message 7 | { 8 | public class ReadHoldingInputRegistersResponseFixture 9 | { 10 | [Fact] 11 | public void ReadHoldingInputRegistersResponse_NullData() 12 | { 13 | Assert.Throws(() => new ReadHoldingInputRegistersResponse(0, 0, null)); 14 | } 15 | 16 | [Fact] 17 | public void ReadHoldingRegistersResponse() 18 | { 19 | ReadHoldingInputRegistersResponse response = 20 | new ReadHoldingInputRegistersResponse(Modbus.ReadHoldingRegisters, 5, new RegisterCollection(1, 2)); 21 | Assert.Equal(Modbus.ReadHoldingRegisters, response.FunctionCode); 22 | Assert.Equal(5, response.SlaveAddress); 23 | Assert.Equal(4, response.ByteCount); 24 | RegisterCollection col = new RegisterCollection(1, 2); 25 | Assert.Equal(col.NetworkBytes, response.Data.NetworkBytes); 26 | } 27 | 28 | [Fact] 29 | public void ToString_ReadHoldingRegistersResponse() 30 | { 31 | ReadHoldingInputRegistersResponse response = 32 | new ReadHoldingInputRegistersResponse(Modbus.ReadHoldingRegisters, 1, new RegisterCollection(1)); 33 | Assert.Equal("Read 1 holding registers.", response.ToString()); 34 | } 35 | 36 | [Fact] 37 | public void ReadInputRegistersResponse() 38 | { 39 | ReadHoldingInputRegistersResponse response = new ReadHoldingInputRegistersResponse( 40 | Modbus.ReadInputRegisters, 5, new RegisterCollection(1, 2)); 41 | Assert.Equal(Modbus.ReadInputRegisters, response.FunctionCode); 42 | Assert.Equal(5, response.SlaveAddress); 43 | Assert.Equal(4, response.ByteCount); 44 | RegisterCollection col = new RegisterCollection(1, 2); 45 | Assert.Equal(col.NetworkBytes, response.Data.NetworkBytes); 46 | } 47 | 48 | [Fact] 49 | public void ToString_ReadInputRegistersResponse() 50 | { 51 | ReadHoldingInputRegistersResponse response = new ReadHoldingInputRegistersResponse( 52 | Modbus.ReadInputRegisters, 1, new RegisterCollection(1)); 53 | Assert.Equal("Read 1 input registers.", response.ToString()); 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /NModbus4.UnitTests/Message/ReadWriteMultipleRegistersRequestFixture.cs: -------------------------------------------------------------------------------- 1 | using Modbus.Data; 2 | using Modbus.Message; 3 | using Xunit; 4 | 5 | namespace Modbus.UnitTests.Message 6 | { 7 | public class ReadWriteMultipleRegistersRequestFixture 8 | { 9 | [Fact] 10 | public void ReadWriteMultipleRegistersRequest() 11 | { 12 | RegisterCollection writeCollection = new RegisterCollection(255, 255, 255); 13 | ReadWriteMultipleRegistersRequest request = new ReadWriteMultipleRegistersRequest(5, 3, 6, 14, 14 | writeCollection); 15 | Assert.Equal(Modbus.ReadWriteMultipleRegisters, request.FunctionCode); 16 | Assert.Equal(5, request.SlaveAddress); 17 | 18 | // test read 19 | Assert.NotNull(request.ReadRequest); 20 | Assert.Equal(request.SlaveAddress, request.ReadRequest.SlaveAddress); 21 | Assert.Equal(3, request.ReadRequest.StartAddress); 22 | Assert.Equal(6, request.ReadRequest.NumberOfPoints); 23 | 24 | // test write 25 | Assert.NotNull(request.WriteRequest); 26 | Assert.Equal(request.SlaveAddress, request.WriteRequest.SlaveAddress); 27 | Assert.Equal(14, request.WriteRequest.StartAddress); 28 | Assert.Equal(writeCollection.NetworkBytes, request.WriteRequest.Data.NetworkBytes); 29 | } 30 | 31 | [Fact] 32 | public void ProtocolDataUnit() 33 | { 34 | RegisterCollection writeCollection = new RegisterCollection(255, 255, 255); 35 | ReadWriteMultipleRegistersRequest request = new ReadWriteMultipleRegistersRequest(5, 3, 6, 14, 36 | writeCollection); 37 | byte[] pdu = 38 | { 39 | 0x17, 0x00, 0x03, 0x00, 0x06, 0x00, 0x0e, 0x00, 0x03, 0x06, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff 40 | }; 41 | Assert.Equal(pdu, request.ProtocolDataUnit); 42 | } 43 | 44 | [Fact] 45 | public void ToString_ReadWriteMultipleRegistersRequest() 46 | { 47 | RegisterCollection writeCollection = new RegisterCollection(255, 255, 255); 48 | ReadWriteMultipleRegistersRequest request = new ReadWriteMultipleRegistersRequest(5, 3, 6, 14, 49 | writeCollection); 50 | 51 | Assert.Equal( 52 | "Write 3 holding registers starting at address 14, and read 6 registers starting at address 3.", 53 | request.ToString()); 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /NModbus4.UnitTests/Message/ReturnQueryDataRequestResponseFixture.cs: -------------------------------------------------------------------------------- 1 | using Modbus.Data; 2 | using Modbus.Message; 3 | using Xunit; 4 | 5 | namespace Modbus.UnitTests.Message 6 | { 7 | public class ReturnQueryDataRequestResponseFixture 8 | { 9 | [Fact] 10 | public void ReturnQueryDataRequestResponse() 11 | { 12 | RegisterCollection data = new RegisterCollection(1, 2, 3, 4); 13 | DiagnosticsRequestResponse request = new DiagnosticsRequestResponse(Modbus.DiagnosticsReturnQueryData, 5, 14 | data); 15 | Assert.Equal(Modbus.Diagnostics, request.FunctionCode); 16 | Assert.Equal(Modbus.DiagnosticsReturnQueryData, request.SubFunctionCode); 17 | Assert.Equal(5, request.SlaveAddress); 18 | Assert.Equal(data.NetworkBytes, request.Data.NetworkBytes); 19 | } 20 | 21 | [Fact] 22 | public void ProtocolDataUnit() 23 | { 24 | RegisterCollection data = new RegisterCollection(1, 2, 3, 4); 25 | DiagnosticsRequestResponse request = new DiagnosticsRequestResponse(Modbus.DiagnosticsReturnQueryData, 5, 26 | data); 27 | Assert.Equal(new byte[] { 8, 0, 0, 0, 1, 0, 2, 0, 3, 0, 4 }, request.ProtocolDataUnit); 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /NModbus4.UnitTests/Message/SlaveExceptionResponseFixture.cs: -------------------------------------------------------------------------------- 1 | using Modbus.Message; 2 | using Xunit; 3 | 4 | namespace Modbus.UnitTests.Message 5 | { 6 | public class SlaveExceptionResponseFixture 7 | { 8 | [Fact] 9 | public void CreateSlaveExceptionResponse() 10 | { 11 | SlaveExceptionResponse response = new SlaveExceptionResponse(11, Modbus.ReadCoils + Modbus.ExceptionOffset, 12 | 2); 13 | Assert.Equal(11, response.SlaveAddress); 14 | Assert.Equal(Modbus.ReadCoils + Modbus.ExceptionOffset, response.FunctionCode); 15 | Assert.Equal(2, response.SlaveExceptionCode); 16 | } 17 | 18 | [Fact] 19 | public void SlaveExceptionResponsePDU() 20 | { 21 | SlaveExceptionResponse response = new SlaveExceptionResponse(11, Modbus.ReadCoils + Modbus.ExceptionOffset, 22 | 2); 23 | Assert.Equal(new byte[] { response.FunctionCode, response.SlaveExceptionCode }, response.ProtocolDataUnit); 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /NModbus4.UnitTests/Message/WriteMultipleCoilsRequestFixture.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Modbus.Data; 3 | using Modbus.Message; 4 | using Xunit; 5 | 6 | namespace Modbus.UnitTests.Message 7 | { 8 | public class WriteMultipleCoilsRequestFixture 9 | { 10 | [Fact] 11 | public void CreateWriteMultipleCoilsRequest() 12 | { 13 | DiscreteCollection col = new DiscreteCollection(true, false, true, false, true, true, true, false, false); 14 | WriteMultipleCoilsRequest request = new WriteMultipleCoilsRequest(34, 45, col); 15 | Assert.Equal(Modbus.WriteMultipleCoils, request.FunctionCode); 16 | Assert.Equal(34, request.SlaveAddress); 17 | Assert.Equal(45, request.StartAddress); 18 | Assert.Equal(9, request.NumberOfPoints); 19 | Assert.Equal(2, request.ByteCount); 20 | Assert.Equal(col.NetworkBytes, request.Data.NetworkBytes); 21 | } 22 | 23 | [Fact] 24 | public void CreateWriteMultipleCoilsRequestTooMuchData() 25 | { 26 | Assert.Throws(() => 27 | new WriteMultipleCoilsRequest(1, 2, MessageUtility.CreateDefaultCollection(true, Modbus.MaximumDiscreteRequestResponseSize + 1))); 28 | } 29 | 30 | [Fact] 31 | public void CreateWriteMultipleCoilsRequestMaxSize() 32 | { 33 | WriteMultipleCoilsRequest request = new WriteMultipleCoilsRequest(1, 2, 34 | MessageUtility.CreateDefaultCollection(true, Modbus.MaximumDiscreteRequestResponseSize)); 35 | Assert.Equal(Modbus.MaximumDiscreteRequestResponseSize, request.Data.Count); 36 | } 37 | 38 | [Fact] 39 | public void ToString_WriteMultipleCoilsRequest() 40 | { 41 | DiscreteCollection col = new DiscreteCollection(true, false, true, false, true, true, true, false, false); 42 | WriteMultipleCoilsRequest request = new WriteMultipleCoilsRequest(34, 45, col); 43 | 44 | Assert.Equal("Write 9 coils starting at address 45.", request.ToString()); 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /NModbus4.UnitTests/Message/WriteMultipleCoilsResponseFixture.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Modbus.Message; 3 | using Xunit; 4 | 5 | namespace Modbus.UnitTests.Message 6 | { 7 | public class WriteMultipleCoilsResponseFixture 8 | { 9 | [Fact] 10 | public void CreateWriteMultipleCoilsResponse() 11 | { 12 | WriteMultipleCoilsResponse response = new WriteMultipleCoilsResponse(17, 19, 45); 13 | Assert.Equal(Modbus.WriteMultipleCoils, response.FunctionCode); 14 | Assert.Equal(17, response.SlaveAddress); 15 | Assert.Equal(19, response.StartAddress); 16 | Assert.Equal(45, response.NumberOfPoints); 17 | } 18 | 19 | [Fact] 20 | public void CreateWriteMultipleCoilsResponseTooMuchData() 21 | { 22 | Assert.Throws(() => new WriteMultipleCoilsResponse(1, 2, Modbus.MaximumDiscreteRequestResponseSize + 1)); 23 | } 24 | 25 | [Fact] 26 | public void CreateWriteMultipleCoilsResponseMaxSize() 27 | { 28 | WriteMultipleCoilsResponse response = new WriteMultipleCoilsResponse(1, 2, 29 | Modbus.MaximumDiscreteRequestResponseSize); 30 | Assert.Equal(Modbus.MaximumDiscreteRequestResponseSize, response.NumberOfPoints); 31 | } 32 | 33 | [Fact] 34 | public void ToString_Test() 35 | { 36 | var response = new WriteMultipleCoilsResponse(1, 2, 3); 37 | 38 | Assert.Equal("Wrote 3 coils starting at address 2.", response.ToString()); 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /NModbus4.UnitTests/Message/WriteMultipleRegistersRequestFixture.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Modbus.Data; 3 | using Modbus.Message; 4 | using Xunit; 5 | 6 | namespace Modbus.UnitTests.Message 7 | { 8 | public class WriteMultipleRegistersRequestFixture 9 | { 10 | [Fact] 11 | public void CreateWriteMultipleRegistersRequestFixture() 12 | { 13 | RegisterCollection col = new RegisterCollection(10, 20, 30, 40, 50); 14 | WriteMultipleRegistersRequest request = new WriteMultipleRegistersRequest(11, 34, col); 15 | Assert.Equal(Modbus.WriteMultipleRegisters, request.FunctionCode); 16 | Assert.Equal(11, request.SlaveAddress); 17 | Assert.Equal(34, request.StartAddress); 18 | Assert.Equal(10, request.ByteCount); 19 | Assert.Equal(col.NetworkBytes, request.Data.NetworkBytes); 20 | } 21 | 22 | [Fact] 23 | public void CreateWriteMultipleRegistersRequestTooMuchData() 24 | { 25 | Assert.Throws(() => 26 | new WriteMultipleRegistersRequest(1, 2, 27 | MessageUtility.CreateDefaultCollection(3, Modbus.MaximumRegisterRequestResponseSize + 1))); 28 | } 29 | 30 | [Fact] 31 | public void CreateWriteMultipleRegistersRequestMaxSize() 32 | { 33 | WriteMultipleRegistersRequest request = new WriteMultipleRegistersRequest(1, 2, 34 | MessageUtility.CreateDefaultCollection(3, Modbus.MaximumRegisterRequestResponseSize)); 35 | Assert.Equal(Modbus.MaximumRegisterRequestResponseSize, request.NumberOfPoints); 36 | } 37 | 38 | [Fact] 39 | public void ToString_WriteMultipleRegistersRequest() 40 | { 41 | RegisterCollection col = new RegisterCollection(10, 20, 30, 40, 50); 42 | WriteMultipleRegistersRequest request = new WriteMultipleRegistersRequest(11, 34, col); 43 | 44 | Assert.Equal("Write 5 holding registers starting at address 34.", request.ToString()); 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /NModbus4.UnitTests/Message/WriteMultipleRegistersResponseFixture.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Modbus.Message; 3 | using Xunit; 4 | 5 | namespace Modbus.UnitTests.Message 6 | { 7 | public class WriteMultipleRegistersResponseFixture 8 | { 9 | [Fact] 10 | public void CreateWriteMultipleRegistersResponse() 11 | { 12 | WriteMultipleRegistersResponse response = new WriteMultipleRegistersResponse(12, 39, 2); 13 | Assert.Equal(Modbus.WriteMultipleRegisters, response.FunctionCode); 14 | Assert.Equal(12, response.SlaveAddress); 15 | Assert.Equal(39, response.StartAddress); 16 | Assert.Equal(2, response.NumberOfPoints); 17 | } 18 | 19 | [Fact] 20 | public void CreateWriteMultipleRegistersResponseTooMuchData() 21 | { 22 | Assert.Throws(() => new WriteMultipleRegistersResponse(1, 2, Modbus.MaximumRegisterRequestResponseSize + 1)); 23 | } 24 | 25 | [Fact] 26 | public void CreateWriteMultipleRegistersResponseMaxSize() 27 | { 28 | WriteMultipleRegistersResponse response = new WriteMultipleRegistersResponse(1, 2, 29 | Modbus.MaximumRegisterRequestResponseSize); 30 | Assert.Equal(Modbus.MaximumRegisterRequestResponseSize, response.NumberOfPoints); 31 | } 32 | 33 | [Fact] 34 | public void ToString_Test() 35 | { 36 | var response = new WriteMultipleRegistersResponse(1, 2, 3); 37 | 38 | Assert.Equal("Wrote 3 holding registers starting at address 2.", response.ToString()); 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /NModbus4.UnitTests/Message/WriteSingleCoilRequestResponseFixture.cs: -------------------------------------------------------------------------------- 1 | using Modbus.Message; 2 | using Xunit; 3 | 4 | namespace Modbus.UnitTests.Message 5 | { 6 | public class WriteSingleCoilRequestResponseFixture 7 | { 8 | [Fact] 9 | public void NewWriteSingleCoilRequestResponse() 10 | { 11 | WriteSingleCoilRequestResponse request = new WriteSingleCoilRequestResponse(11, 5, true); 12 | Assert.Equal(11, request.SlaveAddress); 13 | Assert.Equal(5, request.StartAddress); 14 | Assert.Equal(1, request.Data.Count); 15 | Assert.Equal(Modbus.CoilOn, request.Data[0]); 16 | } 17 | 18 | [Fact] 19 | public void ToString_True() 20 | { 21 | var request = new WriteSingleCoilRequestResponse(11, 5, true); 22 | 23 | Assert.Equal("Write single coil 1 at address 5.", request.ToString()); 24 | } 25 | 26 | [Fact] 27 | public void ToString_False() 28 | { 29 | var request = new WriteSingleCoilRequestResponse(11, 5, false); 30 | 31 | Assert.Equal("Write single coil 0 at address 5.", request.ToString()); 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /NModbus4.UnitTests/Message/WriteSingleRegisterRequestResponseFixture.cs: -------------------------------------------------------------------------------- 1 | using Modbus.Message; 2 | using Xunit; 3 | 4 | namespace Modbus.UnitTests.Message 5 | { 6 | public class WriteSingleRegisterRequestResponseFixture 7 | { 8 | [Fact] 9 | public void NewWriteSingleRegisterRequestResponse() 10 | { 11 | WriteSingleRegisterRequestResponse message = new WriteSingleRegisterRequestResponse(12, 5, 1200); 12 | Assert.Equal(12, message.SlaveAddress); 13 | Assert.Equal(5, message.StartAddress); 14 | Assert.Equal(1, message.Data.Count); 15 | Assert.Equal(1200, message.Data[0]); 16 | } 17 | 18 | [Fact] 19 | public void ToStringOverride() 20 | { 21 | WriteSingleRegisterRequestResponse message = new WriteSingleRegisterRequestResponse(12, 5, 1200); 22 | Assert.Equal("Write single holding register 1200 at address 5.", message.ToString()); 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /NModbus4.UnitTests/NModbus4.UnitTests.xproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 14.0 5 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 6 | 7 | 8 | 9 | 099155af-8348-4829-b959-2f83afcdd4e3 10 | Modbus.UnitTests 11 | .\obj\ 12 | .\bin\ 13 | 14 | 15 | 2.0 16 | 17 | 18 | True 19 | 20 | 21 | True 22 | 23 | 24 | -------------------------------------------------------------------------------- /NModbus4.UnitTests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("NModbus4.UnitTests")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("NModbus4.UnitTests")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("099155af-8348-4829-b959-2f83afcdd4e3")] 24 | -------------------------------------------------------------------------------- /NModbus4.UnitTests/SlaveExceptionFixture.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | #if NET46 4 | using System.Runtime.Serialization.Formatters.Binary; 5 | #endif 6 | using Modbus.Message; 7 | using Xunit; 8 | 9 | namespace Modbus.UnitTests 10 | { 11 | public class SlaveExceptionFixture 12 | { 13 | [Fact] 14 | public void EmptyConstructor() 15 | { 16 | var e = new SlaveException(); 17 | Assert.Equal($"Exception of type '{typeof(SlaveException).FullName}' was thrown.", e.Message); 18 | Assert.Equal(0, e.SlaveAddress); 19 | Assert.Equal(0, e.FunctionCode); 20 | Assert.Equal(0, e.SlaveExceptionCode); 21 | Assert.Null(e.InnerException); 22 | } 23 | 24 | [Fact] 25 | public void ConstructorWithMessage() 26 | { 27 | var e = new SlaveException("Hello World"); 28 | Assert.Equal("Hello World", e.Message); 29 | Assert.Equal(0, e.SlaveAddress); 30 | Assert.Equal(0, e.FunctionCode); 31 | Assert.Equal(0, e.SlaveExceptionCode); 32 | Assert.Null(e.InnerException); 33 | } 34 | 35 | [Fact] 36 | public void ConstructorWithMessageAndInnerException() 37 | { 38 | var inner = new IOException("Bar"); 39 | var e = new SlaveException("Foo", inner); 40 | Assert.Equal("Foo", e.Message); 41 | Assert.Same(inner, e.InnerException); 42 | Assert.Equal(0, e.SlaveAddress); 43 | Assert.Equal(0, e.FunctionCode); 44 | Assert.Equal(0, e.SlaveExceptionCode); 45 | } 46 | 47 | [Fact] 48 | public void ConstructorWithSlaveExceptionResponse() 49 | { 50 | var response = new SlaveExceptionResponse(12, Modbus.ReadCoils, 1); 51 | var e = new SlaveException(response); 52 | 53 | Assert.Equal(12, e.SlaveAddress); 54 | Assert.Equal(Modbus.ReadCoils, e.FunctionCode); 55 | Assert.Equal(1, e.SlaveExceptionCode); 56 | Assert.Null(e.InnerException); 57 | 58 | Assert.Equal( 59 | $@"Exception of type '{typeof(SlaveException).FullName}' was thrown.{Environment.NewLine}Function Code: {response.FunctionCode}{Environment.NewLine}Exception Code: {response.SlaveExceptionCode} - {Resources.IllegalFunction}", 60 | e.Message); 61 | } 62 | 63 | [Fact] 64 | public void ConstructorWithCustomMessageAndSlaveExceptionResponse() 65 | { 66 | var response = new SlaveExceptionResponse(12, Modbus.ReadCoils, 2); 67 | string customMessage = "custom message"; 68 | var e = new SlaveException(customMessage, response); 69 | 70 | Assert.Equal(12, e.SlaveAddress); 71 | Assert.Equal(Modbus.ReadCoils, e.FunctionCode); 72 | Assert.Equal(2, e.SlaveExceptionCode); 73 | Assert.Null(e.InnerException); 74 | 75 | Assert.Equal( 76 | $@"{customMessage}{Environment.NewLine}Function Code: {response.FunctionCode}{Environment.NewLine}Exception Code: {response.SlaveExceptionCode} - {Resources.IllegalDataAddress}", 77 | e.Message); 78 | } 79 | 80 | #if NET46 81 | [Fact] 82 | public void Serializable() 83 | { 84 | var formatter = new BinaryFormatter(); 85 | var e = new SlaveException(new SlaveExceptionResponse(1, 2, 3)); 86 | 87 | using (var stream = new MemoryStream()) 88 | { 89 | formatter.Serialize(stream, e); 90 | stream.Position = 0; 91 | 92 | var e2 = (SlaveException)formatter.Deserialize(stream); 93 | Assert.NotNull(e2); 94 | Assert.Equal(1, e2.SlaveAddress); 95 | Assert.Equal(2, e2.FunctionCode); 96 | Assert.Equal(3, e2.SlaveExceptionCode); 97 | } 98 | } 99 | #endif 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /NModbus4.UnitTests/Utility/CollectionUtilityFixture.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.ObjectModel; 4 | using System.Linq; 5 | using Modbus.Data; 6 | using Modbus.UnitTests.Message; 7 | using Modbus.Unme.Common; 8 | using Xunit; 9 | 10 | namespace Modbus.UnitTests.Utility 11 | { 12 | public class CollectionUtilityFixture 13 | { 14 | [Fact] 15 | public void SliceMiddle() 16 | { 17 | byte[] test = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; 18 | Assert.Equal(new byte[] { 3, 4, 5, 6, 7 }, test.Slice(2, 5).ToArray()); 19 | } 20 | 21 | [Fact] 22 | public void SliceBeginning() 23 | { 24 | byte[] test = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; 25 | Assert.Equal(new byte[] { 1, 2 }, test.Slice(0, 2).ToArray()); 26 | } 27 | 28 | [Fact] 29 | public void SliceEnd() 30 | { 31 | byte[] test = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; 32 | Assert.Equal(new byte[] { 9, 10 }, test.Slice(8, 2).ToArray()); 33 | } 34 | 35 | [Fact] 36 | public void SliceCollection() 37 | { 38 | Collection col = new Collection(new bool[] { true, false, false, false, true, true }); 39 | Assert.Equal(new bool[] { false, false, true }, col.Slice(2, 3).ToArray()); 40 | } 41 | 42 | [Fact] 43 | public void SliceReadOnlyCollection() 44 | { 45 | ReadOnlyCollection col = 46 | new ReadOnlyCollection(new bool[] { true, false, false, false, true, true }); 47 | Assert.Equal(new bool[] { false, false, true }, col.Slice(2, 3).ToArray()); 48 | } 49 | 50 | [Fact] 51 | public void SliceNullICollection() 52 | { 53 | ICollection col = null; 54 | Assert.Throws(() => col.Slice(1, 1).ToArray()); 55 | } 56 | 57 | [Fact] 58 | public void SliceNullArray() 59 | { 60 | bool[] array = null; 61 | Assert.Throws(() => array.Slice(1, 1).ToArray()); 62 | } 63 | 64 | [Fact] 65 | public void CreateDefaultCollectionNegativeSize() 66 | { 67 | Assert.Throws(() => MessageUtility.CreateDefaultCollection(0, -1)); 68 | } 69 | 70 | [Fact] 71 | public void CreateDefaultCollection() 72 | { 73 | RegisterCollection col = MessageUtility.CreateDefaultCollection(3, 5); 74 | Assert.Equal(5, col.Count); 75 | Assert.Equal(new ushort[] { 3, 3, 3, 3, 3 }, col.ToArray()); 76 | } 77 | } 78 | } -------------------------------------------------------------------------------- /NModbus4.UnitTests/Utility/DiscriminatedUnionFixture.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Modbus.Utility; 3 | using Xunit; 4 | 5 | namespace Modbus.UnitTests.Utility 6 | { 7 | public class DiscriminatedUnionFixture 8 | { 9 | [Fact] 10 | public void DiscriminatedUnion_CreateA() 11 | { 12 | var du = DiscriminatedUnion.CreateA("foo"); 13 | Assert.Equal(DiscriminatedUnionOption.A, du.Option); 14 | Assert.Equal("foo", du.A); 15 | } 16 | 17 | [Fact] 18 | public void DiscriminatedUnion_CreateB() 19 | { 20 | var du = DiscriminatedUnion.CreateB("foo"); 21 | Assert.Equal(DiscriminatedUnionOption.B, du.Option); 22 | Assert.Equal("foo", du.B); 23 | } 24 | 25 | [Fact] 26 | public void DiscriminatedUnion_AllowNulls() 27 | { 28 | var du = DiscriminatedUnion.CreateB(null); 29 | Assert.Equal(DiscriminatedUnionOption.B, du.Option); 30 | Assert.Equal(null, du.B); 31 | } 32 | 33 | [Fact] 34 | public void AccessInvalidOption_A() 35 | { 36 | var du = DiscriminatedUnion.CreateB("foo"); 37 | Assert.Throws(() => du.A.ToString()); 38 | } 39 | 40 | [Fact] 41 | public void AccessInvalidOption_B() 42 | { 43 | var du = DiscriminatedUnion.CreateA("foo"); 44 | Assert.Throws(() => du.B.ToString()); 45 | } 46 | 47 | [Fact] 48 | public void DiscriminatedUnion_ToString() 49 | { 50 | var du = DiscriminatedUnion.CreateA("foo"); 51 | Assert.Equal(du.ToString(), "foo"); 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /NModbus4.UnitTests/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0-*", 3 | "testRunner": "xunit", 4 | 5 | "dependencies": { 6 | "dotnet-test-xunit": "2.2.0-*", 7 | "Moq": "4.6.25-alpha", 8 | "NModbus4": { "target": "project" }, 9 | "OpenCover": "4.6.166", 10 | "System.Diagnostics.TraceSource": "4.0.0", 11 | "xunit": "2.1.0" 12 | }, 13 | 14 | "buildOptions": { 15 | "warningsAsErrors": true 16 | }, 17 | 18 | "frameworks": { 19 | "netcoreapp1.0": { 20 | "dependencies": { 21 | "Microsoft.NETCore.App": { 22 | "type": "platform", 23 | "version": "1.0.0" 24 | } 25 | }, 26 | "imports": [ 27 | "dnxcore50", 28 | "portable-net45+win8" 29 | ] 30 | 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /NModbus4.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.24720.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "NModbus4", "NModbus4\NModbus4.xproj", "{9B07ED72-3AD9-45EC-AAE9-295849B309AA}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{67F4885B-CF1F-4640-943F-363213B100A9}" 9 | ProjectSection(SolutionItems) = preProject 10 | global.json = global.json 11 | NuGet.config = NuGet.config 12 | EndProjectSection 13 | EndProject 14 | Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "NModbus4.UnitTests", "NModbus4.UnitTests\NModbus4.UnitTests.xproj", "{099155AF-8348-4829-B959-2F83AFCDD4E3}" 15 | EndProject 16 | Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "NModbus4.Serial", "NModbus4.Serial\NModbus4.Serial.xproj", "{2246217D-D3C2-41CB-B866-D093DF087846}" 17 | EndProject 18 | Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Samples", "Samples\Samples.xproj", "{24958647-C7E3-46C0-885D-89521C4811B8}" 19 | EndProject 20 | Global 21 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 22 | Debug|Any CPU = Debug|Any CPU 23 | Release|Any CPU = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 26 | {9B07ED72-3AD9-45EC-AAE9-295849B309AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {9B07ED72-3AD9-45EC-AAE9-295849B309AA}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {9B07ED72-3AD9-45EC-AAE9-295849B309AA}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {9B07ED72-3AD9-45EC-AAE9-295849B309AA}.Release|Any CPU.Build.0 = Release|Any CPU 30 | {099155AF-8348-4829-B959-2F83AFCDD4E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 31 | {099155AF-8348-4829-B959-2F83AFCDD4E3}.Debug|Any CPU.Build.0 = Debug|Any CPU 32 | {099155AF-8348-4829-B959-2F83AFCDD4E3}.Release|Any CPU.ActiveCfg = Release|Any CPU 33 | {099155AF-8348-4829-B959-2F83AFCDD4E3}.Release|Any CPU.Build.0 = Release|Any CPU 34 | {2246217D-D3C2-41CB-B866-D093DF087846}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 35 | {2246217D-D3C2-41CB-B866-D093DF087846}.Debug|Any CPU.Build.0 = Debug|Any CPU 36 | {2246217D-D3C2-41CB-B866-D093DF087846}.Release|Any CPU.ActiveCfg = Release|Any CPU 37 | {2246217D-D3C2-41CB-B866-D093DF087846}.Release|Any CPU.Build.0 = Release|Any CPU 38 | {24958647-C7E3-46C0-885D-89521C4811B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 39 | {24958647-C7E3-46C0-885D-89521C4811B8}.Debug|Any CPU.Build.0 = Debug|Any CPU 40 | {24958647-C7E3-46C0-885D-89521C4811B8}.Release|Any CPU.ActiveCfg = Release|Any CPU 41 | {24958647-C7E3-46C0-885D-89521C4811B8}.Release|Any CPU.Build.0 = Release|Any CPU 42 | EndGlobalSection 43 | GlobalSection(SolutionProperties) = preSolution 44 | HideSolutionNode = FALSE 45 | EndGlobalSection 46 | EndGlobal 47 | -------------------------------------------------------------------------------- /NModbus4/Data/DataStoreEventArgs.cs: -------------------------------------------------------------------------------- 1 | namespace Modbus.Data 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Collections.ObjectModel; 6 | using System.Diagnostics.CodeAnalysis; 7 | using System.Linq; 8 | 9 | using Utility; 10 | 11 | /// 12 | /// Event args for read write actions performed on the DataStore. 13 | /// 14 | public class DataStoreEventArgs : EventArgs 15 | { 16 | private DataStoreEventArgs(ushort startAddress, ModbusDataType modbusDataType) 17 | { 18 | StartAddress = startAddress; 19 | ModbusDataType = modbusDataType; 20 | } 21 | 22 | /// 23 | /// Type of Modbus data (e.g. Holding register). 24 | /// 25 | public ModbusDataType ModbusDataType { get; } 26 | 27 | /// 28 | /// Start address of data. 29 | /// 30 | public ushort StartAddress { get; } 31 | 32 | /// 33 | /// Data that was read or written. 34 | /// 35 | [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")] 36 | public DiscriminatedUnion, ReadOnlyCollection> Data { get; private set; } 37 | 38 | internal static DataStoreEventArgs CreateDataStoreEventArgs(ushort startAddress, ModbusDataType modbusDataType, IEnumerable data) 39 | { 40 | if (data == null) 41 | { 42 | throw new ArgumentNullException(nameof(data)); 43 | } 44 | 45 | DataStoreEventArgs eventArgs; 46 | 47 | if (typeof(T) == typeof(bool)) 48 | { 49 | var a = new ReadOnlyCollection(data.Cast().ToArray()); 50 | 51 | eventArgs = new DataStoreEventArgs(startAddress, modbusDataType) 52 | { 53 | Data = DiscriminatedUnion, ReadOnlyCollection>.CreateA(a) 54 | }; 55 | } 56 | else if (typeof(T) == typeof(ushort)) 57 | { 58 | var b = new ReadOnlyCollection(data.Cast().ToArray()); 59 | 60 | eventArgs = new DataStoreEventArgs(startAddress, modbusDataType) 61 | { 62 | Data = DiscriminatedUnion, ReadOnlyCollection>.CreateB(b) 63 | }; 64 | } 65 | else 66 | { 67 | throw new ArgumentException("Generic type T should be of type bool or ushort"); 68 | } 69 | 70 | return eventArgs; 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /NModbus4/Data/DataStoreFactory.cs: -------------------------------------------------------------------------------- 1 | namespace Modbus.Data 2 | { 3 | /// 4 | /// Data story factory. 5 | /// 6 | public static class DataStoreFactory 7 | { 8 | /// 9 | /// Factory method for default data store - register values set to 0 and discrete values set to false. 10 | /// 11 | public static DataStore CreateDefaultDataStore() 12 | { 13 | return CreateDefaultDataStore(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue, ushort.MaxValue); 14 | } 15 | 16 | /// 17 | /// Factory method for default data store - register values set to 0 and discrete values set to false. 18 | /// 19 | /// Number of discrete coils. 20 | /// Number of discrete inputs. 21 | /// Number of holding registers. 22 | /// Number of input registers. 23 | /// New instance of Data store with defined inputs/outputs. 24 | public static DataStore CreateDefaultDataStore(ushort coilsCount, ushort inputsCount, ushort holdingRegistersCount, ushort inputRegistersCount) 25 | { 26 | var coils = new bool[coilsCount]; 27 | var inputs = new bool[inputsCount]; 28 | var holdingRegs = new ushort[holdingRegistersCount]; 29 | var inputRegs = new ushort[inputRegistersCount]; 30 | 31 | return new DataStore(coils, inputs, holdingRegs, inputRegs); 32 | } 33 | 34 | /// 35 | /// Factory method for test data store. 36 | /// 37 | internal static DataStore CreateTestDataStore() 38 | { 39 | DataStore dataStore = new DataStore(); 40 | 41 | for (int i = 1; i < 3000; i++) 42 | { 43 | bool value = i % 2 > 0; 44 | dataStore.CoilDiscretes.Add(value); 45 | dataStore.InputDiscretes.Add(!value); 46 | dataStore.HoldingRegisters.Add((ushort)i); 47 | dataStore.InputRegisters.Add((ushort)(i * 10)); 48 | } 49 | 50 | return dataStore; 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /NModbus4/Data/IModbusMessageDataCollection.cs: -------------------------------------------------------------------------------- 1 | namespace Modbus.Data 2 | { 3 | using System.Diagnostics.CodeAnalysis; 4 | 5 | /// 6 | /// Modbus message containing data. 7 | /// 8 | [SuppressMessage("Microsoft.Naming", "CA1711:IdentifiersShouldNotHaveIncorrectSuffix")] 9 | public interface IModbusMessageDataCollection 10 | { 11 | /// 12 | /// Gets the network bytes. 13 | /// 14 | [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] 15 | byte[] NetworkBytes { get; } 16 | 17 | /// 18 | /// Gets the byte count. 19 | /// 20 | byte ByteCount { get; } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /NModbus4/Data/ModbusDataType.cs: -------------------------------------------------------------------------------- 1 | namespace Modbus.Data 2 | { 3 | /// 4 | /// Types of data supported by the Modbus protocol. 5 | /// 6 | public enum ModbusDataType 7 | { 8 | /// 9 | /// Read/write register. 10 | /// 11 | HoldingRegister, 12 | 13 | /// 14 | /// Readonly register. 15 | /// 16 | InputRegister, 17 | 18 | /// 19 | /// Read/write discrete. 20 | /// 21 | Coil, 22 | 23 | /// 24 | /// Readonly discrete. 25 | /// 26 | Input 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /NModbus4/Data/RegisterCollection.cs: -------------------------------------------------------------------------------- 1 | namespace Modbus.Data 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Collections.ObjectModel; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Net; 9 | 10 | using Utility; 11 | 12 | /// 13 | /// Collection of 16 bit registers. 14 | /// 15 | public class RegisterCollection : Collection, IModbusMessageDataCollection 16 | { 17 | /// 18 | /// Initializes a new instance of the class. 19 | /// 20 | public RegisterCollection() 21 | { 22 | } 23 | 24 | /// 25 | /// Initializes a new instance of the class. 26 | /// 27 | /// Array for register collection. 28 | public RegisterCollection(byte[] bytes) 29 | : this((IList)ModbusUtility.NetworkBytesToHostUInt16(bytes)) 30 | { 31 | } 32 | 33 | /// 34 | /// Initializes a new instance of the class. 35 | /// 36 | /// Array for register collection. 37 | public RegisterCollection(params ushort[] registers) 38 | : this((IList)registers) 39 | { 40 | } 41 | 42 | /// 43 | /// Initializes a new instance of the class. 44 | /// 45 | /// List for register collection. 46 | public RegisterCollection(IList registers) 47 | : base(registers.IsReadOnly ? new List(registers) : registers) 48 | { 49 | } 50 | 51 | public byte[] NetworkBytes 52 | { 53 | get 54 | { 55 | var bytes = new MemoryStream(ByteCount); 56 | 57 | foreach (ushort register in this) 58 | { 59 | var b = BitConverter.GetBytes((ushort)IPAddress.HostToNetworkOrder((short)register)); 60 | bytes.Write(b, 0, b.Length); 61 | } 62 | 63 | return bytes.ToArray(); 64 | } 65 | } 66 | 67 | /// 68 | /// Gets the byte count. 69 | /// 70 | public byte ByteCount 71 | { 72 | get { return (byte)(Count * 2); } 73 | } 74 | 75 | /// 76 | /// Returns a that represents the current . 77 | /// 78 | /// 79 | /// A that represents the current . 80 | /// 81 | public override string ToString() 82 | { 83 | return string.Concat("{", string.Join(", ", this.Select(v => v.ToString()).ToArray()), "}"); 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /NModbus4/Device/IModbusSerialMaster.cs: -------------------------------------------------------------------------------- 1 | namespace Modbus.Device 2 | { 3 | using IO; 4 | 5 | /// 6 | /// Modbus Serial Master device. 7 | /// 8 | public interface IModbusSerialMaster : IModbusMaster 9 | { 10 | /// 11 | /// Transport for used by this master. 12 | /// 13 | new ModbusSerialTransport Transport { get; } 14 | 15 | /// 16 | /// Serial Line only. 17 | /// Diagnostic function which loops back the original data. 18 | /// NModbus only supports looping back one ushort value, this is a 19 | /// limitation of the "Best Effort" implementation of the RTU protocol. 20 | /// 21 | /// Address of device to test. 22 | /// Data to return. 23 | /// Return true if slave device echoed data. 24 | bool ReturnQueryData(byte slaveAddress, ushort data); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /NModbus4/Device/ModbusDevice.cs: -------------------------------------------------------------------------------- 1 | namespace Modbus.Device 2 | { 3 | using System; 4 | 5 | using IO; 6 | 7 | using Unme.Common; 8 | 9 | /// 10 | /// Modbus device. 11 | /// 12 | public abstract class ModbusDevice : IDisposable 13 | { 14 | private ModbusTransport _transport; 15 | 16 | internal ModbusDevice(ModbusTransport transport) 17 | { 18 | _transport = transport; 19 | } 20 | 21 | /// 22 | /// Gets the Modbus Transport. 23 | /// 24 | public ModbusTransport Transport 25 | { 26 | get { return _transport; } 27 | } 28 | 29 | /// 30 | /// Releases unmanaged and - optionally - managed resources. 31 | /// 32 | public void Dispose() 33 | { 34 | Dispose(true); 35 | GC.SuppressFinalize(this); 36 | } 37 | 38 | /// 39 | /// Releases unmanaged and - optionally - managed resources. 40 | /// 41 | /// 42 | /// true to release both managed and unmanaged resources; 43 | /// false to release only unmanaged resources. 44 | /// 45 | protected virtual void Dispose(bool disposing) 46 | { 47 | if (disposing) 48 | { 49 | DisposableUtility.Dispose(ref _transport); 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /NModbus4/Device/ModbusSlaveRequestEventArgs.cs: -------------------------------------------------------------------------------- 1 | namespace Modbus.Device 2 | { 3 | using System; 4 | 5 | using Message; 6 | 7 | /// 8 | /// Modbus Slave request event args containing information on the message. 9 | /// 10 | public class ModbusSlaveRequestEventArgs : EventArgs 11 | { 12 | private readonly IModbusMessage _message; 13 | 14 | internal ModbusSlaveRequestEventArgs(IModbusMessage message) 15 | { 16 | _message = message; 17 | } 18 | 19 | /// 20 | /// Gets the message. 21 | /// 22 | public IModbusMessage Message 23 | { 24 | get { return _message; } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /NModbus4/Device/ModbusUdpSlave.cs: -------------------------------------------------------------------------------- 1 | namespace Modbus.Device 2 | { 3 | using System; 4 | using System.Diagnostics; 5 | using System.Linq; 6 | using System.Net; 7 | using System.Net.Sockets; 8 | using System.Threading.Tasks; 9 | 10 | using IO; 11 | using Message; 12 | 13 | using Unme.Common; 14 | 15 | /// 16 | /// Modbus UDP slave device. 17 | /// 18 | public class ModbusUdpSlave : ModbusSlave 19 | { 20 | private readonly UdpClient _udpClient; 21 | 22 | private ModbusUdpSlave(byte unitId, UdpClient udpClient) 23 | : base(unitId, new ModbusIpTransport(new UdpClientAdapter(udpClient))) 24 | { 25 | _udpClient = udpClient; 26 | } 27 | 28 | /// 29 | /// Modbus UDP slave factory method. 30 | /// Creates NModbus UDP slave with default 31 | /// 32 | public static ModbusUdpSlave CreateUdp(UdpClient client) 33 | { 34 | return new ModbusUdpSlave(Modbus.DefaultIpSlaveUnitId, client); 35 | } 36 | 37 | /// 38 | /// Modbus UDP slave factory method. 39 | /// 40 | public static ModbusUdpSlave CreateUdp(byte unitId, UdpClient client) 41 | { 42 | return new ModbusUdpSlave(unitId, client); 43 | } 44 | 45 | /// 46 | /// Start slave listening for requests. 47 | /// 48 | public override async Task ListenAsync() 49 | { 50 | Debug.WriteLine("Start Modbus Udp Server."); 51 | 52 | try 53 | { 54 | while (true) 55 | { 56 | UdpReceiveResult receiveResult = await _udpClient.ReceiveAsync().ConfigureAwait(false); 57 | IPEndPoint masterEndPoint = receiveResult.RemoteEndPoint; 58 | byte[] frame = receiveResult.Buffer; 59 | 60 | Debug.WriteLine($"Read Frame completed {frame.Length} bytes"); 61 | Debug.WriteLine($"RX: {string.Join(", ", frame)}"); 62 | 63 | IModbusMessage request = 64 | ModbusMessageFactory.CreateModbusRequest(frame.Slice(6, frame.Length - 6).ToArray()); 65 | request.TransactionId = (ushort)IPAddress.NetworkToHostOrder(BitConverter.ToInt16(frame, 0)); 66 | 67 | // perform action and build response 68 | IModbusMessage response = ApplyRequest(request); 69 | response.TransactionId = request.TransactionId; 70 | 71 | // write response 72 | byte[] responseFrame = Transport.BuildMessageFrame(response); 73 | Debug.WriteLine($"TX: {string.Join(", ", responseFrame)}"); 74 | await _udpClient.SendAsync(responseFrame, responseFrame.Length, masterEndPoint).ConfigureAwait(false); 75 | } 76 | } 77 | catch (SocketException se) 78 | { 79 | // this hapens when slave stops 80 | if (se.SocketErrorCode != SocketError.Interrupted) 81 | { 82 | throw; 83 | } 84 | } 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /NModbus4/Device/TcpConnectionEventArgs.cs: -------------------------------------------------------------------------------- 1 | namespace Modbus.Device 2 | { 3 | using System; 4 | 5 | internal class TcpConnectionEventArgs : EventArgs 6 | { 7 | public TcpConnectionEventArgs(string endPoint) 8 | { 9 | if (endPoint == null) 10 | { 11 | throw new ArgumentNullException(nameof(endPoint)); 12 | } 13 | 14 | if (endPoint == string.Empty) 15 | { 16 | throw new ArgumentException(Resources.EmptyEndPoint); 17 | } 18 | 19 | EndPoint = endPoint; 20 | } 21 | 22 | public string EndPoint { get; set; } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /NModbus4/GlobalSuppressions.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics.CodeAnalysis; 2 | 3 | [module: 4 | SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", 5 | Target = "Modbus")] 6 | [module: 7 | SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", 8 | Target = "Modbus.Utility")] 9 | -------------------------------------------------------------------------------- /NModbus4/IO/EmptyTransport.cs: -------------------------------------------------------------------------------- 1 | namespace Modbus.IO 2 | { 3 | using System; 4 | using Message; 5 | 6 | public class EmptyTransport : ModbusTransport 7 | { 8 | internal override byte[] ReadRequest() 9 | { 10 | throw new NotImplementedException(); 11 | } 12 | 13 | internal override IModbusMessage ReadResponse() 14 | { 15 | throw new NotImplementedException(); 16 | } 17 | 18 | internal override byte[] BuildMessageFrame(Message.IModbusMessage message) 19 | { 20 | throw new NotImplementedException(); 21 | } 22 | 23 | internal override void Write(IModbusMessage message) 24 | { 25 | throw new NotImplementedException(); 26 | } 27 | 28 | internal override void OnValidateResponse(IModbusMessage request, IModbusMessage response) 29 | { 30 | throw new NotImplementedException(); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /NModbus4/IO/IStreamResource.cs: -------------------------------------------------------------------------------- 1 | namespace Modbus.IO 2 | { 3 | using System; 4 | 5 | /// 6 | /// Represents a serial resource. 7 | /// Implementor - http://en.wikipedia.org/wiki/Bridge_Pattern 8 | /// 9 | public interface IStreamResource : IDisposable 10 | { 11 | /// 12 | /// Indicates that no timeout should occur. 13 | /// 14 | int InfiniteTimeout { get; } 15 | 16 | /// 17 | /// Gets or sets the number of milliseconds before a timeout occurs when a read operation does not finish. 18 | /// 19 | int ReadTimeout { get; set; } 20 | 21 | /// 22 | /// Gets or sets the number of milliseconds before a timeout occurs when a write operation does not finish. 23 | /// 24 | int WriteTimeout { get; set; } 25 | 26 | /// 27 | /// Purges the receive buffer. 28 | /// 29 | void DiscardInBuffer(); 30 | 31 | /// 32 | /// Reads a number of bytes from the input buffer and writes those bytes into a byte array at the specified offset. 33 | /// 34 | /// The byte array to write the input to. 35 | /// The offset in the buffer array to begin writing. 36 | /// The number of bytes to read. 37 | /// The number of bytes read. 38 | int Read(byte[] buffer, int offset, int count); 39 | 40 | /// 41 | /// Writes a specified number of bytes to the port from an output buffer, starting at the specified offset. 42 | /// 43 | /// The byte array that contains the data to write to the port. 44 | /// The offset in the buffer array to begin writing. 45 | /// The number of bytes to write. 46 | void Write(byte[] buffer, int offset, int count); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /NModbus4/IO/ModbusAsciiTransport.cs: -------------------------------------------------------------------------------- 1 | namespace Modbus.IO 2 | { 3 | using System.Diagnostics; 4 | using System.IO; 5 | using System.Text; 6 | 7 | using Message; 8 | using Utility; 9 | 10 | /// 11 | /// Refined Abstraction - http://en.wikipedia.org/wiki/Bridge_Pattern 12 | /// 13 | internal class ModbusAsciiTransport : ModbusSerialTransport 14 | { 15 | internal ModbusAsciiTransport(IStreamResource streamResource) 16 | : base(streamResource) 17 | { 18 | Debug.Assert(streamResource != null, "Argument streamResource cannot be null."); 19 | } 20 | 21 | internal override byte[] BuildMessageFrame(IModbusMessage message) 22 | { 23 | var msgFrame = message.MessageFrame; 24 | 25 | var msgFrameAscii = ModbusUtility.GetAsciiBytes(msgFrame); 26 | var lrcAscii = ModbusUtility.GetAsciiBytes(ModbusUtility.CalculateLrc(msgFrame)); 27 | var nlAscii = Encoding.UTF8.GetBytes(Modbus.NewLine.ToCharArray()); 28 | 29 | var frame = new MemoryStream(1 + msgFrameAscii.Length + lrcAscii.Length + nlAscii.Length); 30 | frame.WriteByte((byte)':'); 31 | frame.Write(msgFrameAscii, 0, msgFrameAscii.Length); 32 | frame.Write(lrcAscii, 0, lrcAscii.Length); 33 | frame.Write(nlAscii, 0, nlAscii.Length); 34 | 35 | return frame.ToArray(); 36 | } 37 | 38 | internal override bool ChecksumsMatch(IModbusMessage message, byte[] messageFrame) 39 | { 40 | return ModbusUtility.CalculateLrc(message.MessageFrame) == messageFrame[messageFrame.Length - 1]; 41 | } 42 | 43 | internal override byte[] ReadRequest() 44 | { 45 | return ReadRequestResponse(); 46 | } 47 | 48 | internal override IModbusMessage ReadResponse() 49 | { 50 | return CreateResponse(ReadRequestResponse()); 51 | } 52 | 53 | internal byte[] ReadRequestResponse() 54 | { 55 | // read message frame, removing frame start ':' 56 | string frameHex = StreamResourceUtility.ReadLine(StreamResource).Substring(1); 57 | 58 | // convert hex to bytes 59 | byte[] frame = ModbusUtility.HexToBytes(frameHex); 60 | Debug.WriteLine($"RX: {string.Join(", ", frame)}"); 61 | 62 | if (frame.Length < 3) 63 | { 64 | throw new IOException("Premature end of stream, message truncated."); 65 | } 66 | 67 | return frame; 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /NModbus4/IO/ModbusSerialTransport.cs: -------------------------------------------------------------------------------- 1 | namespace Modbus.IO 2 | { 3 | using System.Diagnostics; 4 | using System.IO; 5 | 6 | using Message; 7 | 8 | /// 9 | /// Transport for Serial protocols. 10 | /// Refined Abstraction - http://en.wikipedia.org/wiki/Bridge_Pattern 11 | /// 12 | public abstract class ModbusSerialTransport : ModbusTransport 13 | { 14 | private bool _checkFrame = true; 15 | 16 | internal ModbusSerialTransport(IStreamResource streamResource) 17 | : base(streamResource) 18 | { 19 | Debug.Assert(streamResource != null, "Argument streamResource cannot be null."); 20 | } 21 | 22 | /// 23 | /// Gets or sets a value indicating whether LRC/CRC frame checking is performed on messages. 24 | /// 25 | public bool CheckFrame 26 | { 27 | get { return _checkFrame; } 28 | set { _checkFrame = value; } 29 | } 30 | 31 | internal void DiscardInBuffer() 32 | { 33 | StreamResource.DiscardInBuffer(); 34 | } 35 | 36 | internal override void Write(IModbusMessage message) 37 | { 38 | DiscardInBuffer(); 39 | 40 | byte[] frame = BuildMessageFrame(message); 41 | Debug.WriteLine($"TX: {string.Join(", ", frame)}"); 42 | StreamResource.Write(frame, 0, frame.Length); 43 | } 44 | 45 | internal override IModbusMessage CreateResponse(byte[] frame) 46 | { 47 | IModbusMessage response = base.CreateResponse(frame); 48 | 49 | // compare checksum 50 | if (CheckFrame && !ChecksumsMatch(response, frame)) 51 | { 52 | string msg = $"Checksums failed to match {string.Join(", ", response.MessageFrame)} != {string.Join(", ", frame)}"; 53 | Debug.WriteLine(msg); 54 | throw new IOException(msg); 55 | } 56 | 57 | return response; 58 | } 59 | 60 | internal abstract bool ChecksumsMatch(IModbusMessage message, byte[] messageFrame); 61 | 62 | internal override void OnValidateResponse(IModbusMessage request, IModbusMessage response) 63 | { 64 | // no-op 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /NModbus4/IO/StreamResourceUtility.cs: -------------------------------------------------------------------------------- 1 | namespace Modbus.IO 2 | { 3 | using System.Linq; 4 | using System.Text; 5 | 6 | internal static class StreamResourceUtility 7 | { 8 | internal static string ReadLine(IStreamResource stream) 9 | { 10 | var result = new StringBuilder(); 11 | var singleByteBuffer = new byte[1]; 12 | 13 | do 14 | { 15 | if (stream.Read(singleByteBuffer, 0, 1) == 0) 16 | { 17 | continue; 18 | } 19 | 20 | result.Append(Encoding.UTF8.GetChars(singleByteBuffer).First()); 21 | } 22 | while (!result.ToString().EndsWith(Modbus.NewLine)); 23 | 24 | return result.ToString().Substring(0, result.Length - Modbus.NewLine.Length); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /NModbus4/IO/TcpClientAdapter.cs: -------------------------------------------------------------------------------- 1 | namespace Modbus.IO 2 | { 3 | using System; 4 | using System.Diagnostics; 5 | using System.Net.Sockets; 6 | using System.Threading; 7 | 8 | using Unme.Common; 9 | 10 | /// 11 | /// Concrete Implementor - http://en.wikipedia.org/wiki/Bridge_Pattern 12 | /// 13 | internal class TcpClientAdapter : IStreamResource 14 | { 15 | private TcpClient _tcpClient; 16 | 17 | public TcpClientAdapter(TcpClient tcpClient) 18 | { 19 | Debug.Assert(tcpClient != null, "Argument tcpClient cannot be null."); 20 | 21 | _tcpClient = tcpClient; 22 | } 23 | 24 | public int InfiniteTimeout 25 | { 26 | get { return Timeout.Infinite; } 27 | } 28 | 29 | public int ReadTimeout 30 | { 31 | get { return _tcpClient.GetStream().ReadTimeout; } 32 | set { _tcpClient.GetStream().ReadTimeout = value; } 33 | } 34 | 35 | public int WriteTimeout 36 | { 37 | get { return _tcpClient.GetStream().WriteTimeout; } 38 | set { _tcpClient.GetStream().WriteTimeout = value; } 39 | } 40 | 41 | public void Write(byte[] buffer, int offset, int size) 42 | { 43 | _tcpClient.GetStream().Write(buffer, offset, size); 44 | } 45 | 46 | public int Read(byte[] buffer, int offset, int size) 47 | { 48 | return _tcpClient.GetStream().Read(buffer, offset, size); 49 | } 50 | 51 | public void DiscardInBuffer() 52 | { 53 | _tcpClient.GetStream().Flush(); 54 | } 55 | 56 | public void Dispose() 57 | { 58 | Dispose(true); 59 | GC.SuppressFinalize(this); 60 | } 61 | 62 | protected virtual void Dispose(bool disposing) 63 | { 64 | if (disposing) 65 | { 66 | DisposableUtility.Dispose(ref _tcpClient); 67 | } 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /NModbus4/Message/AbstractModbusMessage.cs: -------------------------------------------------------------------------------- 1 | namespace Modbus.Message 2 | { 3 | using System; 4 | 5 | /// 6 | /// Abstract Modbus message. 7 | /// 8 | public abstract class AbstractModbusMessage 9 | { 10 | private readonly ModbusMessageImpl _messageImpl; 11 | 12 | /// 13 | /// Abstract Modbus message. 14 | /// 15 | internal AbstractModbusMessage() 16 | { 17 | _messageImpl = new ModbusMessageImpl(); 18 | } 19 | 20 | /// 21 | /// Abstract Modbus message. 22 | /// 23 | internal AbstractModbusMessage(byte slaveAddress, byte functionCode) 24 | { 25 | _messageImpl = new ModbusMessageImpl(slaveAddress, functionCode); 26 | } 27 | 28 | public ushort TransactionId 29 | { 30 | get { return _messageImpl.TransactionId; } 31 | set { _messageImpl.TransactionId = value; } 32 | } 33 | 34 | public byte FunctionCode 35 | { 36 | get { return _messageImpl.FunctionCode; } 37 | set { _messageImpl.FunctionCode = value; } 38 | } 39 | 40 | public byte SlaveAddress 41 | { 42 | get { return _messageImpl.SlaveAddress; } 43 | set { _messageImpl.SlaveAddress = value; } 44 | } 45 | 46 | public byte[] MessageFrame 47 | { 48 | get { return _messageImpl.MessageFrame; } 49 | } 50 | 51 | public virtual byte[] ProtocolDataUnit 52 | { 53 | get { return _messageImpl.ProtocolDataUnit; } 54 | } 55 | 56 | public abstract int MinimumFrameSize { get; } 57 | 58 | internal ModbusMessageImpl MessageImpl 59 | { 60 | get { return _messageImpl; } 61 | } 62 | 63 | public void Initialize(byte[] frame) 64 | { 65 | if (frame.Length < MinimumFrameSize) 66 | { 67 | string msg = $"Message frame must contain at least {MinimumFrameSize} bytes of data."; 68 | throw new FormatException(msg); 69 | } 70 | 71 | _messageImpl.Initialize(frame); 72 | InitializeUnique(frame); 73 | } 74 | 75 | protected abstract void InitializeUnique(byte[] frame); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /NModbus4/Message/AbstractModbusMessageWithData.cs: -------------------------------------------------------------------------------- 1 | namespace Modbus.Message 2 | { 3 | using Data; 4 | 5 | public abstract class AbstractModbusMessageWithData : AbstractModbusMessage 6 | where TData : IModbusMessageDataCollection 7 | { 8 | internal AbstractModbusMessageWithData() 9 | { 10 | } 11 | 12 | internal AbstractModbusMessageWithData(byte slaveAddress, byte functionCode) 13 | : base(slaveAddress, functionCode) 14 | { 15 | } 16 | 17 | public TData Data 18 | { 19 | get { return (TData)MessageImpl.Data; } 20 | set { MessageImpl.Data = value; } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /NModbus4/Message/DiagnosticsRequestResponse.cs: -------------------------------------------------------------------------------- 1 | namespace Modbus.Message 2 | { 3 | using System; 4 | using System.Diagnostics; 5 | using System.Diagnostics.CodeAnalysis; 6 | using System.Linq; 7 | using System.Net; 8 | 9 | using Data; 10 | 11 | using Unme.Common; 12 | 13 | internal class DiagnosticsRequestResponse : AbstractModbusMessageWithData, IModbusMessage 14 | { 15 | public DiagnosticsRequestResponse() 16 | { 17 | } 18 | 19 | public DiagnosticsRequestResponse(ushort subFunctionCode, byte slaveAddress, RegisterCollection data) 20 | : base(slaveAddress, Modbus.Diagnostics) 21 | { 22 | SubFunctionCode = subFunctionCode; 23 | Data = data; 24 | } 25 | 26 | public override int MinimumFrameSize 27 | { 28 | get { return 6; } 29 | } 30 | 31 | [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "May implement addtional sub function codes in the future.")] 32 | public ushort SubFunctionCode 33 | { 34 | get { return MessageImpl.SubFunctionCode.Value; } 35 | set { MessageImpl.SubFunctionCode = value; } 36 | } 37 | 38 | public override string ToString() 39 | { 40 | Debug.Assert( 41 | SubFunctionCode == Modbus.DiagnosticsReturnQueryData, 42 | "Need to add support for additional sub-function."); 43 | 44 | return $"Diagnostics message, sub-function return query data - {Data}."; 45 | } 46 | 47 | protected override void InitializeUnique(byte[] frame) 48 | { 49 | SubFunctionCode = (ushort)IPAddress.NetworkToHostOrder(BitConverter.ToInt16(frame, 2)); 50 | Data = new RegisterCollection(frame.Slice(4, 2).ToArray()); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /NModbus4/Message/IModbusMessage.cs: -------------------------------------------------------------------------------- 1 | namespace Modbus.Message 2 | { 3 | using System.Diagnostics.CodeAnalysis; 4 | 5 | /// 6 | /// A message built by the master (client) that initiates a Modbus transaction. 7 | /// 8 | public interface IModbusMessage 9 | { 10 | /// 11 | /// The function code tells the server what kind of action to perform. 12 | /// 13 | byte FunctionCode { get; set; } 14 | 15 | /// 16 | /// Address of the slave (server). 17 | /// 18 | byte SlaveAddress { get; set; } 19 | 20 | /// 21 | /// Composition of the slave address and protocol data unit. 22 | /// 23 | [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] 24 | byte[] MessageFrame { get; } 25 | 26 | /// 27 | /// Composition of the function code and message data. 28 | /// 29 | [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] 30 | byte[] ProtocolDataUnit { get; } 31 | 32 | /// 33 | /// A unique identifier assigned to a message when using the IP protocol. 34 | /// 35 | ushort TransactionId { get; set; } 36 | 37 | /// 38 | /// Initializes a modbus message from the specified message frame. 39 | /// 40 | /// Bytes of Modbus frame. 41 | void Initialize(byte[] frame); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /NModbus4/Message/IModbusRequest.cs: -------------------------------------------------------------------------------- 1 | namespace Modbus.Message 2 | { 3 | /// 4 | /// Methods specific to a modbus request message. 5 | /// 6 | public interface IModbusRequest : IModbusMessage 7 | { 8 | /// 9 | /// Validate the specified response against the current request. 10 | /// 11 | void ValidateResponse(IModbusMessage response); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /NModbus4/Message/ModbusMessageFactory.cs: -------------------------------------------------------------------------------- 1 | namespace Modbus.Message 2 | { 3 | using System; 4 | 5 | /// 6 | /// Modbus message factory. 7 | /// 8 | public static class ModbusMessageFactory 9 | { 10 | /// 11 | /// Minimum request frame length. 12 | /// 13 | private const int MinRequestFrameLength = 3; 14 | 15 | /// 16 | /// Create a Modbus message. 17 | /// 18 | /// Modbus message type. 19 | /// Bytes of Modbus frame. 20 | /// New Modbus message based on type and frame bytes. 21 | public static T CreateModbusMessage(byte[] frame) 22 | where T : IModbusMessage, new() 23 | { 24 | IModbusMessage message = new T(); 25 | message.Initialize(frame); 26 | 27 | return (T)message; 28 | } 29 | 30 | /// 31 | /// Create a Modbus request. 32 | /// 33 | /// Bytes of Modbus frame. 34 | /// Modbus request. 35 | public static IModbusMessage CreateModbusRequest(byte[] frame) 36 | { 37 | if (frame.Length < MinRequestFrameLength) 38 | { 39 | string msg = $"Argument 'frame' must have a length of at least {MinRequestFrameLength} bytes."; 40 | throw new FormatException(msg); 41 | } 42 | 43 | IModbusMessage request; 44 | byte functionCode = frame[1]; 45 | 46 | switch (functionCode) 47 | { 48 | case Modbus.ReadCoils: 49 | case Modbus.ReadInputs: 50 | request = CreateModbusMessage(frame); 51 | break; 52 | case Modbus.ReadHoldingRegisters: 53 | case Modbus.ReadInputRegisters: 54 | request = CreateModbusMessage(frame); 55 | break; 56 | case Modbus.WriteSingleCoil: 57 | request = CreateModbusMessage(frame); 58 | break; 59 | case Modbus.WriteSingleRegister: 60 | request = CreateModbusMessage(frame); 61 | break; 62 | case Modbus.Diagnostics: 63 | request = CreateModbusMessage(frame); 64 | break; 65 | case Modbus.WriteMultipleCoils: 66 | request = CreateModbusMessage(frame); 67 | break; 68 | case Modbus.WriteMultipleRegisters: 69 | request = CreateModbusMessage(frame); 70 | break; 71 | case Modbus.ReadWriteMultipleRegisters: 72 | request = CreateModbusMessage(frame); 73 | break; 74 | default: 75 | string msg = $"Unsupported function code {functionCode}"; 76 | throw new ArgumentException(msg, nameof(frame)); 77 | } 78 | 79 | return request; 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /NModbus4/Message/ModbusMessageImpl.cs: -------------------------------------------------------------------------------- 1 | namespace Modbus.Message 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.IO; 6 | using System.Net; 7 | 8 | using Data; 9 | 10 | /// 11 | /// Class holding all implementation shared between two or more message types. 12 | /// Interfaces expose subsets of type specific implementations. 13 | /// 14 | internal class ModbusMessageImpl 15 | { 16 | public ModbusMessageImpl() 17 | { 18 | } 19 | 20 | public ModbusMessageImpl(byte slaveAddress, byte functionCode) 21 | { 22 | SlaveAddress = slaveAddress; 23 | FunctionCode = functionCode; 24 | } 25 | 26 | public byte? ByteCount { get; set; } 27 | 28 | public byte? ExceptionCode { get; set; } 29 | 30 | public ushort TransactionId { get; set; } 31 | 32 | public byte FunctionCode { get; set; } 33 | 34 | public ushort? NumberOfPoints { get; set; } 35 | 36 | public byte SlaveAddress { get; set; } 37 | 38 | public ushort? StartAddress { get; set; } 39 | 40 | public ushort? SubFunctionCode { get; set; } 41 | 42 | public IModbusMessageDataCollection Data { get; set; } 43 | 44 | public byte[] MessageFrame 45 | { 46 | get 47 | { 48 | var pdu = ProtocolDataUnit; 49 | var frame = new MemoryStream(1 + pdu.Length); 50 | 51 | frame.WriteByte(SlaveAddress); 52 | frame.Write(pdu, 0, pdu.Length); 53 | 54 | return frame.ToArray(); 55 | } 56 | } 57 | 58 | public byte[] ProtocolDataUnit 59 | { 60 | get 61 | { 62 | List pdu = new List(); 63 | 64 | pdu.Add(FunctionCode); 65 | 66 | if (ExceptionCode.HasValue) 67 | { 68 | pdu.Add(ExceptionCode.Value); 69 | } 70 | 71 | if (SubFunctionCode.HasValue) 72 | { 73 | pdu.AddRange(BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)SubFunctionCode.Value))); 74 | } 75 | 76 | if (StartAddress.HasValue) 77 | { 78 | pdu.AddRange(BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)StartAddress.Value))); 79 | } 80 | 81 | if (NumberOfPoints.HasValue) 82 | { 83 | pdu.AddRange(BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)NumberOfPoints.Value))); 84 | } 85 | 86 | if (ByteCount.HasValue) 87 | { 88 | pdu.Add(ByteCount.Value); 89 | } 90 | 91 | if (Data != null) 92 | { 93 | pdu.AddRange(Data.NetworkBytes); 94 | } 95 | 96 | return pdu.ToArray(); 97 | } 98 | } 99 | 100 | public void Initialize(byte[] frame) 101 | { 102 | if (frame == null) 103 | { 104 | throw new ArgumentNullException(nameof(frame), "Argument frame cannot be null."); 105 | } 106 | 107 | if (frame.Length < Modbus.MinimumFrameSize) 108 | { 109 | string msg = $"Message frame must contain at least {Modbus.MinimumFrameSize} bytes of data."; 110 | throw new FormatException(msg); 111 | } 112 | 113 | SlaveAddress = frame[0]; 114 | FunctionCode = frame[1]; 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /NModbus4/Message/ReadCoilsInputsRequest.cs: -------------------------------------------------------------------------------- 1 | namespace Modbus.Message 2 | { 3 | using System; 4 | using System.IO; 5 | using System.Net; 6 | 7 | public class ReadCoilsInputsRequest : AbstractModbusMessage, IModbusRequest 8 | { 9 | public ReadCoilsInputsRequest() 10 | { 11 | } 12 | 13 | public ReadCoilsInputsRequest(byte functionCode, byte slaveAddress, ushort startAddress, ushort numberOfPoints) 14 | : base(slaveAddress, functionCode) 15 | { 16 | StartAddress = startAddress; 17 | NumberOfPoints = numberOfPoints; 18 | } 19 | 20 | public ushort StartAddress 21 | { 22 | get { return MessageImpl.StartAddress.Value; } 23 | set { MessageImpl.StartAddress = value; } 24 | } 25 | 26 | public override int MinimumFrameSize 27 | { 28 | get { return 6; } 29 | } 30 | 31 | public ushort NumberOfPoints 32 | { 33 | get 34 | { 35 | return MessageImpl.NumberOfPoints.Value; 36 | } 37 | 38 | set 39 | { 40 | if (value > Modbus.MaximumDiscreteRequestResponseSize) 41 | { 42 | string msg = $"Maximum amount of data {Modbus.MaximumDiscreteRequestResponseSize} coils."; 43 | throw new ArgumentOutOfRangeException(nameof(NumberOfPoints), msg); 44 | } 45 | 46 | MessageImpl.NumberOfPoints = value; 47 | } 48 | } 49 | 50 | public override string ToString() 51 | { 52 | string msg = $"Read {NumberOfPoints} {(FunctionCode == Modbus.ReadCoils ? "coils" : "inputs")} starting at address {StartAddress}."; 53 | return msg; 54 | } 55 | 56 | public void ValidateResponse(IModbusMessage response) 57 | { 58 | var typedResponse = (ReadCoilsInputsResponse)response; 59 | 60 | // best effort validation - the same response for a request for 1 vs 6 coils (same byte count) will pass validation. 61 | var expectedByteCount = (NumberOfPoints + 7) / 8; 62 | 63 | if (expectedByteCount != typedResponse.ByteCount) 64 | { 65 | string msg = $"Unexpected byte count. Expected {expectedByteCount}, received {typedResponse.ByteCount}."; 66 | throw new IOException(msg); 67 | } 68 | } 69 | 70 | protected override void InitializeUnique(byte[] frame) 71 | { 72 | StartAddress = (ushort)IPAddress.NetworkToHostOrder(BitConverter.ToInt16(frame, 2)); 73 | NumberOfPoints = (ushort)IPAddress.NetworkToHostOrder(BitConverter.ToInt16(frame, 4)); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /NModbus4/Message/ReadCoilsInputsResponse.cs: -------------------------------------------------------------------------------- 1 | namespace Modbus.Message 2 | { 3 | using System; 4 | using System.Linq; 5 | using Data; 6 | 7 | using Unme.Common; 8 | 9 | public class ReadCoilsInputsResponse : AbstractModbusMessageWithData, IModbusMessage 10 | { 11 | public ReadCoilsInputsResponse() 12 | { 13 | } 14 | 15 | public ReadCoilsInputsResponse(byte functionCode, byte slaveAddress, byte byteCount, DiscreteCollection data) 16 | : base(slaveAddress, functionCode) 17 | { 18 | ByteCount = byteCount; 19 | Data = data; 20 | } 21 | 22 | public byte ByteCount 23 | { 24 | get { return MessageImpl.ByteCount.Value; } 25 | set { MessageImpl.ByteCount = value; } 26 | } 27 | 28 | public override int MinimumFrameSize 29 | { 30 | get { return 3; } 31 | } 32 | 33 | public override string ToString() 34 | { 35 | string msg = $"Read {Data.Count()} {(FunctionCode == Modbus.ReadInputs ? "inputs" : "coils")} - {Data}."; 36 | return msg; 37 | } 38 | 39 | protected override void InitializeUnique(byte[] frame) 40 | { 41 | if (frame.Length < 3 + frame[2]) 42 | { 43 | throw new FormatException("Message frame data segment does not contain enough bytes."); 44 | } 45 | 46 | ByteCount = frame[2]; 47 | Data = new DiscreteCollection(frame.Slice(3, ByteCount).ToArray()); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /NModbus4/Message/ReadHoldingInputRegistersRequest.cs: -------------------------------------------------------------------------------- 1 | namespace Modbus.Message 2 | { 3 | using System; 4 | using System.Diagnostics; 5 | using System.IO; 6 | using System.Net; 7 | 8 | public class ReadHoldingInputRegistersRequest : AbstractModbusMessage, IModbusRequest 9 | { 10 | public ReadHoldingInputRegistersRequest() 11 | { 12 | } 13 | 14 | public ReadHoldingInputRegistersRequest(byte functionCode, byte slaveAddress, ushort startAddress, ushort numberOfPoints) 15 | : base(slaveAddress, functionCode) 16 | { 17 | StartAddress = startAddress; 18 | NumberOfPoints = numberOfPoints; 19 | } 20 | 21 | public ushort StartAddress 22 | { 23 | get { return MessageImpl.StartAddress.Value; } 24 | set { MessageImpl.StartAddress = value; } 25 | } 26 | 27 | public override int MinimumFrameSize 28 | { 29 | get { return 6; } 30 | } 31 | 32 | public ushort NumberOfPoints 33 | { 34 | get 35 | { 36 | return MessageImpl.NumberOfPoints.Value; 37 | } 38 | 39 | set 40 | { 41 | if (value > Modbus.MaximumRegisterRequestResponseSize) 42 | { 43 | string msg = $"Maximum amount of data {Modbus.MaximumRegisterRequestResponseSize} registers."; 44 | throw new ArgumentOutOfRangeException(nameof(NumberOfPoints), msg); 45 | } 46 | 47 | MessageImpl.NumberOfPoints = value; 48 | } 49 | } 50 | 51 | public override string ToString() 52 | { 53 | string msg = $"Read {NumberOfPoints} {(FunctionCode == Modbus.ReadHoldingRegisters ? "holding" : "input")} registers starting at address {StartAddress}."; 54 | return msg; 55 | } 56 | 57 | public void ValidateResponse(IModbusMessage response) 58 | { 59 | var typedResponse = response as ReadHoldingInputRegistersResponse; 60 | Debug.Assert(typedResponse != null, "Argument response should be of type ReadHoldingInputRegistersResponse."); 61 | var expectedByteCount = NumberOfPoints * 2; 62 | 63 | if (expectedByteCount != typedResponse.ByteCount) 64 | { 65 | string msg = $"Unexpected byte count. Expected {expectedByteCount}, received {typedResponse.ByteCount}."; 66 | throw new IOException(msg); 67 | } 68 | } 69 | 70 | protected override void InitializeUnique(byte[] frame) 71 | { 72 | StartAddress = (ushort)IPAddress.NetworkToHostOrder(BitConverter.ToInt16(frame, 2)); 73 | NumberOfPoints = (ushort)IPAddress.NetworkToHostOrder(BitConverter.ToInt16(frame, 4)); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /NModbus4/Message/ReadHoldingInputRegistersResponse.cs: -------------------------------------------------------------------------------- 1 | namespace Modbus.Message 2 | { 3 | using System; 4 | using System.Linq; 5 | 6 | using Data; 7 | 8 | using Unme.Common; 9 | 10 | public class ReadHoldingInputRegistersResponse : AbstractModbusMessageWithData, IModbusMessage 11 | { 12 | public ReadHoldingInputRegistersResponse() 13 | { 14 | } 15 | 16 | public ReadHoldingInputRegistersResponse(byte functionCode, byte slaveAddress, RegisterCollection data) 17 | : base(slaveAddress, functionCode) 18 | { 19 | if (data == null) 20 | { 21 | throw new ArgumentNullException(nameof(data)); 22 | } 23 | 24 | ByteCount = data.ByteCount; 25 | Data = data; 26 | } 27 | 28 | public byte ByteCount 29 | { 30 | get { return MessageImpl.ByteCount.Value; } 31 | set { MessageImpl.ByteCount = value; } 32 | } 33 | 34 | public override int MinimumFrameSize 35 | { 36 | get { return 3; } 37 | } 38 | 39 | public override string ToString() 40 | { 41 | string msg = $"Read {Data.Count} {(FunctionCode == Modbus.ReadHoldingRegisters ? "holding" : "input")} registers."; 42 | return msg; 43 | } 44 | 45 | protected override void InitializeUnique(byte[] frame) 46 | { 47 | if (frame.Length < MinimumFrameSize + frame[2]) 48 | { 49 | throw new FormatException("Message frame does not contain enough bytes."); 50 | } 51 | 52 | ByteCount = frame[2]; 53 | Data = new RegisterCollection(frame.Slice(3, ByteCount).ToArray()); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /NModbus4/Message/ReadWriteMultipleRegistersRequest.cs: -------------------------------------------------------------------------------- 1 | namespace Modbus.Message 2 | { 3 | using System; 4 | using System.IO; 5 | 6 | using Data; 7 | 8 | public class ReadWriteMultipleRegistersRequest : AbstractModbusMessage, IModbusRequest 9 | { 10 | private ReadHoldingInputRegistersRequest _readRequest; 11 | private WriteMultipleRegistersRequest _writeRequest; 12 | 13 | public ReadWriteMultipleRegistersRequest() 14 | { 15 | } 16 | 17 | public ReadWriteMultipleRegistersRequest( 18 | byte slaveAddress, 19 | ushort startReadAddress, 20 | ushort numberOfPointsToRead, 21 | ushort startWriteAddress, 22 | RegisterCollection writeData) 23 | : base(slaveAddress, Modbus.ReadWriteMultipleRegisters) 24 | { 25 | _readRequest = new ReadHoldingInputRegistersRequest( 26 | Modbus.ReadHoldingRegisters, 27 | slaveAddress, 28 | startReadAddress, 29 | numberOfPointsToRead); 30 | 31 | _writeRequest = new WriteMultipleRegistersRequest( 32 | slaveAddress, 33 | startWriteAddress, 34 | writeData); 35 | } 36 | 37 | public override byte[] ProtocolDataUnit 38 | { 39 | get 40 | { 41 | byte[] readPdu = _readRequest.ProtocolDataUnit; 42 | byte[] writePdu = _writeRequest.ProtocolDataUnit; 43 | var stream = new MemoryStream(readPdu.Length + writePdu.Length); 44 | 45 | stream.WriteByte(FunctionCode); 46 | 47 | // read and write PDUs without function codes 48 | stream.Write(readPdu, 1, readPdu.Length - 1); 49 | stream.Write(writePdu, 1, writePdu.Length - 1); 50 | 51 | return stream.ToArray(); 52 | } 53 | } 54 | 55 | public ReadHoldingInputRegistersRequest ReadRequest 56 | { 57 | get { return _readRequest; } 58 | } 59 | 60 | public WriteMultipleRegistersRequest WriteRequest 61 | { 62 | get { return _writeRequest; } 63 | } 64 | 65 | public override int MinimumFrameSize 66 | { 67 | get { return 11; } 68 | } 69 | 70 | public override string ToString() 71 | { 72 | string msg = $"Write {_writeRequest.NumberOfPoints} holding registers starting at address {_writeRequest.StartAddress}, and read {_readRequest.NumberOfPoints} registers starting at address {_readRequest.StartAddress}."; 73 | return msg; 74 | } 75 | 76 | public void ValidateResponse(IModbusMessage response) 77 | { 78 | var typedResponse = (ReadHoldingInputRegistersResponse)response; 79 | var expectedByteCount = ReadRequest.NumberOfPoints * 2; 80 | 81 | if (expectedByteCount != typedResponse.ByteCount) 82 | { 83 | string msg = $"Unexpected byte count in response. Expected {expectedByteCount}, received {typedResponse.ByteCount}."; 84 | throw new IOException(msg); 85 | } 86 | } 87 | 88 | protected override void InitializeUnique(byte[] frame) 89 | { 90 | if (frame.Length < MinimumFrameSize + frame[10]) 91 | { 92 | throw new FormatException("Message frame does not contain enough bytes."); 93 | } 94 | 95 | byte[] readFrame = new byte[2 + 4]; 96 | byte[] writeFrame = new byte[frame.Length - 6 + 2]; 97 | 98 | readFrame[0] = writeFrame[0] = SlaveAddress; 99 | readFrame[1] = writeFrame[1] = FunctionCode; 100 | 101 | Buffer.BlockCopy(frame, 2, readFrame, 2, 4); 102 | Buffer.BlockCopy(frame, 6, writeFrame, 2, frame.Length - 6); 103 | 104 | _readRequest = ModbusMessageFactory.CreateModbusMessage(readFrame); 105 | _writeRequest = ModbusMessageFactory.CreateModbusMessage(writeFrame); 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /NModbus4/Message/SlaveExceptionResponse.cs: -------------------------------------------------------------------------------- 1 | namespace Modbus.Message 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Globalization; 6 | 7 | public class SlaveExceptionResponse : AbstractModbusMessage, IModbusMessage 8 | { 9 | private static readonly Dictionary _exceptionMessages = CreateExceptionMessages(); 10 | 11 | public SlaveExceptionResponse() 12 | { 13 | } 14 | 15 | public SlaveExceptionResponse(byte slaveAddress, byte functionCode, byte exceptionCode) 16 | : base(slaveAddress, functionCode) 17 | { 18 | SlaveExceptionCode = exceptionCode; 19 | } 20 | 21 | public override int MinimumFrameSize 22 | { 23 | get { return 3; } 24 | } 25 | 26 | public byte SlaveExceptionCode 27 | { 28 | get { return MessageImpl.ExceptionCode.Value; } 29 | set { MessageImpl.ExceptionCode = value; } 30 | } 31 | 32 | /// 33 | /// Returns a that represents the current . 34 | /// 35 | /// 36 | /// A that represents the current . 37 | /// 38 | public override string ToString() 39 | { 40 | string msg = _exceptionMessages.ContainsKey(SlaveExceptionCode) 41 | ? _exceptionMessages[SlaveExceptionCode] 42 | : Resources.Unknown; 43 | 44 | return string.Format( 45 | CultureInfo.InvariantCulture, 46 | Resources.SlaveExceptionResponseFormat, 47 | Environment.NewLine, 48 | FunctionCode, 49 | SlaveExceptionCode, 50 | msg); 51 | } 52 | 53 | internal static Dictionary CreateExceptionMessages() 54 | { 55 | Dictionary messages = new Dictionary(9); 56 | 57 | messages.Add(1, Resources.IllegalFunction); 58 | messages.Add(2, Resources.IllegalDataAddress); 59 | messages.Add(3, Resources.IllegalDataValue); 60 | messages.Add(4, Resources.SlaveDeviceFailure); 61 | messages.Add(5, Resources.Acknowlege); 62 | messages.Add(6, Resources.SlaveDeviceBusy); 63 | messages.Add(8, Resources.MemoryParityError); 64 | messages.Add(10, Resources.GatewayPathUnavailable); 65 | messages.Add(11, Resources.GatewayTargetDeviceFailedToRespond); 66 | 67 | return messages; 68 | } 69 | 70 | protected override void InitializeUnique(byte[] frame) 71 | { 72 | if (FunctionCode <= Modbus.ExceptionOffset) 73 | { 74 | throw new FormatException(Resources.SlaveExceptionResponseInvalidFunctionCode); 75 | } 76 | 77 | SlaveExceptionCode = frame[2]; 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /NModbus4/Message/WriteMultipleCoilsRequest.cs: -------------------------------------------------------------------------------- 1 | namespace Modbus.Message 2 | { 3 | using System; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Net; 7 | 8 | using Data; 9 | 10 | using Unme.Common; 11 | 12 | /// 13 | /// Write Multiple Coils request. 14 | /// 15 | public class WriteMultipleCoilsRequest : AbstractModbusMessageWithData, IModbusRequest 16 | { 17 | /// 18 | /// Write Multiple Coils request. 19 | /// 20 | public WriteMultipleCoilsRequest() 21 | { 22 | } 23 | 24 | /// 25 | /// Write Multiple Coils request. 26 | /// 27 | public WriteMultipleCoilsRequest(byte slaveAddress, ushort startAddress, DiscreteCollection data) 28 | : base(slaveAddress, Modbus.WriteMultipleCoils) 29 | { 30 | StartAddress = startAddress; 31 | NumberOfPoints = (ushort)data.Count; 32 | ByteCount = (byte)((data.Count + 7) / 8); 33 | Data = data; 34 | } 35 | 36 | public byte ByteCount 37 | { 38 | get { return MessageImpl.ByteCount.Value; } 39 | set { MessageImpl.ByteCount = value; } 40 | } 41 | 42 | public ushort NumberOfPoints 43 | { 44 | get 45 | { 46 | return MessageImpl.NumberOfPoints.Value; 47 | } 48 | 49 | set 50 | { 51 | if (value > Modbus.MaximumDiscreteRequestResponseSize) 52 | { 53 | string msg = $"Maximum amount of data {Modbus.MaximumDiscreteRequestResponseSize} coils."; 54 | throw new ArgumentOutOfRangeException("NumberOfPoints", msg); 55 | } 56 | 57 | MessageImpl.NumberOfPoints = value; 58 | } 59 | } 60 | 61 | public ushort StartAddress 62 | { 63 | get { return MessageImpl.StartAddress.Value; } 64 | set { MessageImpl.StartAddress = value; } 65 | } 66 | 67 | public override int MinimumFrameSize 68 | { 69 | get { return 7; } 70 | } 71 | 72 | public override string ToString() 73 | { 74 | string msg = $"Write {NumberOfPoints} coils starting at address {StartAddress}."; 75 | return msg; 76 | } 77 | 78 | public void ValidateResponse(IModbusMessage response) 79 | { 80 | var typedResponse = (WriteMultipleCoilsResponse)response; 81 | 82 | if (StartAddress != typedResponse.StartAddress) 83 | { 84 | string msg = $"Unexpected start address in response. Expected {StartAddress}, received {typedResponse.StartAddress}."; 85 | throw new IOException(msg); 86 | } 87 | 88 | if (NumberOfPoints != typedResponse.NumberOfPoints) 89 | { 90 | string msg = $"Unexpected number of points in response. Expected {NumberOfPoints}, received {typedResponse.NumberOfPoints}."; 91 | throw new IOException(msg); 92 | } 93 | } 94 | 95 | protected override void InitializeUnique(byte[] frame) 96 | { 97 | if (frame.Length < MinimumFrameSize + frame[6]) 98 | { 99 | throw new FormatException("Message frame does not contain enough bytes."); 100 | } 101 | 102 | StartAddress = (ushort)IPAddress.NetworkToHostOrder(BitConverter.ToInt16(frame, 2)); 103 | NumberOfPoints = (ushort)IPAddress.NetworkToHostOrder(BitConverter.ToInt16(frame, 4)); 104 | ByteCount = frame[6]; 105 | Data = new DiscreteCollection(frame.Slice(7, ByteCount).ToArray()); 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /NModbus4/Message/WriteMultipleCoilsResponse.cs: -------------------------------------------------------------------------------- 1 | namespace Modbus.Message 2 | { 3 | using System; 4 | using System.Net; 5 | 6 | public class WriteMultipleCoilsResponse : AbstractModbusMessage, IModbusMessage 7 | { 8 | public WriteMultipleCoilsResponse() 9 | { 10 | } 11 | 12 | public WriteMultipleCoilsResponse(byte slaveAddress, ushort startAddress, ushort numberOfPoints) 13 | : base(slaveAddress, Modbus.WriteMultipleCoils) 14 | { 15 | StartAddress = startAddress; 16 | NumberOfPoints = numberOfPoints; 17 | } 18 | 19 | public ushort NumberOfPoints 20 | { 21 | get 22 | { 23 | return MessageImpl.NumberOfPoints.Value; 24 | } 25 | 26 | set 27 | { 28 | if (value > Modbus.MaximumDiscreteRequestResponseSize) 29 | { 30 | string msg = $"Maximum amount of data {Modbus.MaximumDiscreteRequestResponseSize} coils."; 31 | throw new ArgumentOutOfRangeException("NumberOfPoints", msg); 32 | } 33 | 34 | MessageImpl.NumberOfPoints = value; 35 | } 36 | } 37 | 38 | public ushort StartAddress 39 | { 40 | get { return MessageImpl.StartAddress.Value; } 41 | set { MessageImpl.StartAddress = value; } 42 | } 43 | 44 | public override int MinimumFrameSize 45 | { 46 | get { return 6; } 47 | } 48 | 49 | public override string ToString() 50 | { 51 | string msg = $"Wrote {NumberOfPoints} coils starting at address {StartAddress}."; 52 | return msg; 53 | } 54 | 55 | protected override void InitializeUnique(byte[] frame) 56 | { 57 | StartAddress = (ushort)IPAddress.NetworkToHostOrder(BitConverter.ToInt16(frame, 2)); 58 | NumberOfPoints = (ushort)IPAddress.NetworkToHostOrder(BitConverter.ToInt16(frame, 4)); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /NModbus4/Message/WriteMultipleRegistersRequest.cs: -------------------------------------------------------------------------------- 1 | namespace Modbus.Message 2 | { 3 | using System; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Net; 7 | 8 | using Data; 9 | 10 | using Unme.Common; 11 | 12 | public class WriteMultipleRegistersRequest : AbstractModbusMessageWithData, IModbusRequest 13 | { 14 | public WriteMultipleRegistersRequest() 15 | { 16 | } 17 | 18 | public WriteMultipleRegistersRequest(byte slaveAddress, ushort startAddress, RegisterCollection data) 19 | : base(slaveAddress, Modbus.WriteMultipleRegisters) 20 | { 21 | StartAddress = startAddress; 22 | NumberOfPoints = (ushort)data.Count; 23 | ByteCount = (byte)(data.Count * 2); 24 | Data = data; 25 | } 26 | 27 | public byte ByteCount 28 | { 29 | get { return MessageImpl.ByteCount.Value; } 30 | set { MessageImpl.ByteCount = value; } 31 | } 32 | 33 | public ushort NumberOfPoints 34 | { 35 | get 36 | { 37 | return MessageImpl.NumberOfPoints.Value; 38 | } 39 | 40 | set 41 | { 42 | if (value > Modbus.MaximumRegisterRequestResponseSize) 43 | { 44 | string msg = $"Maximum amount of data {Modbus.MaximumRegisterRequestResponseSize} registers."; 45 | throw new ArgumentOutOfRangeException(nameof(NumberOfPoints), msg); 46 | } 47 | 48 | MessageImpl.NumberOfPoints = value; 49 | } 50 | } 51 | 52 | public ushort StartAddress 53 | { 54 | get { return MessageImpl.StartAddress.Value; } 55 | set { MessageImpl.StartAddress = value; } 56 | } 57 | 58 | public override int MinimumFrameSize 59 | { 60 | get { return 7; } 61 | } 62 | 63 | public override string ToString() 64 | { 65 | string msg = $"Write {NumberOfPoints} holding registers starting at address {StartAddress}."; 66 | return msg; 67 | } 68 | 69 | public void ValidateResponse(IModbusMessage response) 70 | { 71 | var typedResponse = (WriteMultipleRegistersResponse)response; 72 | 73 | if (StartAddress != typedResponse.StartAddress) 74 | { 75 | string msg = $"Unexpected start address in response. Expected {StartAddress}, received {typedResponse.StartAddress}."; 76 | throw new IOException(msg); 77 | } 78 | 79 | if (NumberOfPoints != typedResponse.NumberOfPoints) 80 | { 81 | string msg = $"Unexpected number of points in response. Expected {NumberOfPoints}, received {typedResponse.NumberOfPoints}."; 82 | throw new IOException(msg); 83 | } 84 | } 85 | 86 | protected override void InitializeUnique(byte[] frame) 87 | { 88 | if (frame.Length < MinimumFrameSize + frame[6]) 89 | { 90 | throw new FormatException("Message frame does not contain enough bytes."); 91 | } 92 | 93 | StartAddress = (ushort)IPAddress.NetworkToHostOrder(BitConverter.ToInt16(frame, 2)); 94 | NumberOfPoints = (ushort)IPAddress.NetworkToHostOrder(BitConverter.ToInt16(frame, 4)); 95 | ByteCount = frame[6]; 96 | Data = new RegisterCollection(frame.Slice(7, ByteCount).ToArray()); 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /NModbus4/Message/WriteMultipleRegistersResponse.cs: -------------------------------------------------------------------------------- 1 | namespace Modbus.Message 2 | { 3 | using System; 4 | using System.Net; 5 | 6 | public class WriteMultipleRegistersResponse : AbstractModbusMessage, IModbusMessage 7 | { 8 | public WriteMultipleRegistersResponse() 9 | { 10 | } 11 | 12 | public WriteMultipleRegistersResponse(byte slaveAddress, ushort startAddress, ushort numberOfPoints) 13 | : base(slaveAddress, Modbus.WriteMultipleRegisters) 14 | { 15 | StartAddress = startAddress; 16 | NumberOfPoints = numberOfPoints; 17 | } 18 | 19 | public ushort NumberOfPoints 20 | { 21 | get 22 | { 23 | return MessageImpl.NumberOfPoints.Value; 24 | } 25 | 26 | set 27 | { 28 | if (value > Modbus.MaximumRegisterRequestResponseSize) 29 | { 30 | string msg = $"Maximum amount of data {Modbus.MaximumRegisterRequestResponseSize} registers."; 31 | throw new ArgumentOutOfRangeException(nameof(NumberOfPoints), msg); 32 | } 33 | 34 | MessageImpl.NumberOfPoints = value; 35 | } 36 | } 37 | 38 | public ushort StartAddress 39 | { 40 | get { return MessageImpl.StartAddress.Value; } 41 | set { MessageImpl.StartAddress = value; } 42 | } 43 | 44 | public override int MinimumFrameSize 45 | { 46 | get { return 6; } 47 | } 48 | 49 | public override string ToString() 50 | { 51 | string msg = $"Wrote {NumberOfPoints} holding registers starting at address {StartAddress}."; 52 | return msg; 53 | } 54 | 55 | protected override void InitializeUnique(byte[] frame) 56 | { 57 | StartAddress = (ushort)IPAddress.NetworkToHostOrder(BitConverter.ToInt16(frame, 2)); 58 | NumberOfPoints = (ushort)IPAddress.NetworkToHostOrder(BitConverter.ToInt16(frame, 4)); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /NModbus4/Message/WriteSingleCoilRequestResponse.cs: -------------------------------------------------------------------------------- 1 | namespace Modbus.Message 2 | { 3 | using System; 4 | using System.Diagnostics; 5 | using System.IO; 6 | using System.Linq; 7 | using System.Net; 8 | 9 | using Data; 10 | 11 | using Unme.Common; 12 | 13 | public class WriteSingleCoilRequestResponse : AbstractModbusMessageWithData, IModbusRequest 14 | { 15 | public WriteSingleCoilRequestResponse() 16 | { 17 | } 18 | 19 | public WriteSingleCoilRequestResponse(byte slaveAddress, ushort startAddress, bool coilState) 20 | : base(slaveAddress, Modbus.WriteSingleCoil) 21 | { 22 | StartAddress = startAddress; 23 | Data = new RegisterCollection(coilState ? Modbus.CoilOn : Modbus.CoilOff); 24 | } 25 | 26 | public override int MinimumFrameSize 27 | { 28 | get { return 6; } 29 | } 30 | 31 | public ushort StartAddress 32 | { 33 | get { return MessageImpl.StartAddress.Value; } 34 | set { MessageImpl.StartAddress = value; } 35 | } 36 | 37 | public override string ToString() 38 | { 39 | Debug.Assert(Data != null, "Argument Data cannot be null."); 40 | Debug.Assert(Data.Count() == 1, "Data should have a count of 1."); 41 | 42 | string msg = $"Write single coil {(Data.First() == Modbus.CoilOn ? 1 : 0)} at address {StartAddress}."; 43 | return msg; 44 | } 45 | 46 | public void ValidateResponse(IModbusMessage response) 47 | { 48 | var typedResponse = (WriteSingleCoilRequestResponse)response; 49 | 50 | if (StartAddress != typedResponse.StartAddress) 51 | { 52 | string msg = $"Unexpected start address in response. Expected {StartAddress}, received {typedResponse.StartAddress}."; 53 | throw new IOException(msg); 54 | } 55 | 56 | if (Data.First() != typedResponse.Data.First()) 57 | { 58 | string msg = $"Unexpected data in response. Expected {Data.First()}, received {typedResponse.Data.First()}."; 59 | throw new IOException(msg); 60 | } 61 | } 62 | 63 | protected override void InitializeUnique(byte[] frame) 64 | { 65 | StartAddress = (ushort)IPAddress.NetworkToHostOrder(BitConverter.ToInt16(frame, 2)); 66 | Data = new RegisterCollection(frame.Slice(4, 2).ToArray()); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /NModbus4/Message/WriteSingleRegisterRequestResponse.cs: -------------------------------------------------------------------------------- 1 | namespace Modbus.Message 2 | { 3 | using System; 4 | using System.Diagnostics; 5 | using System.IO; 6 | using System.Linq; 7 | using System.Net; 8 | 9 | using Data; 10 | 11 | public class WriteSingleRegisterRequestResponse : AbstractModbusMessageWithData, IModbusRequest 12 | { 13 | public WriteSingleRegisterRequestResponse() 14 | { 15 | } 16 | 17 | public WriteSingleRegisterRequestResponse(byte slaveAddress, ushort startAddress, ushort registerValue) 18 | : base(slaveAddress, Modbus.WriteSingleRegister) 19 | { 20 | StartAddress = startAddress; 21 | Data = new RegisterCollection(registerValue); 22 | } 23 | 24 | public override int MinimumFrameSize 25 | { 26 | get { return 6; } 27 | } 28 | 29 | public ushort StartAddress 30 | { 31 | get { return MessageImpl.StartAddress.Value; } 32 | set { MessageImpl.StartAddress = value; } 33 | } 34 | 35 | public override string ToString() 36 | { 37 | Debug.Assert(Data != null, "Argument Data cannot be null."); 38 | Debug.Assert(Data.Count() == 1, "Data should have a count of 1."); 39 | 40 | string msg = $"Write single holding register {Data[0]} at address {StartAddress}."; 41 | return msg; 42 | } 43 | 44 | public void ValidateResponse(IModbusMessage response) 45 | { 46 | var typedResponse = (WriteSingleRegisterRequestResponse)response; 47 | 48 | if (StartAddress != typedResponse.StartAddress) 49 | { 50 | string msg = $"Unexpected start address in response. Expected {StartAddress}, received {typedResponse.StartAddress}."; 51 | throw new IOException(msg); 52 | } 53 | 54 | if (Data.First() != typedResponse.Data.First()) 55 | { 56 | string msg = $"Unexpected data in response. Expected {Data.First()}, received {typedResponse.Data.First()}."; 57 | throw new IOException(msg); 58 | } 59 | } 60 | 61 | protected override void InitializeUnique(byte[] frame) 62 | { 63 | StartAddress = (ushort)IPAddress.NetworkToHostOrder(BitConverter.ToInt16(frame, 2)); 64 | Data = new RegisterCollection((ushort)IPAddress.NetworkToHostOrder(BitConverter.ToInt16(frame, 4))); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /NModbus4/Modbus.cs: -------------------------------------------------------------------------------- 1 | namespace Modbus 2 | { 3 | /// 4 | /// Defines constants related to the Modbus protocol. 5 | /// 6 | internal static class Modbus 7 | { 8 | // supported function codes 9 | public const byte ReadCoils = 1; 10 | public const byte ReadInputs = 2; 11 | public const byte ReadHoldingRegisters = 3; 12 | public const byte ReadInputRegisters = 4; 13 | public const byte WriteSingleCoil = 5; 14 | public const byte WriteSingleRegister = 6; 15 | public const byte Diagnostics = 8; 16 | public const ushort DiagnosticsReturnQueryData = 0; 17 | public const byte WriteMultipleCoils = 15; 18 | public const byte WriteMultipleRegisters = 16; 19 | public const byte ReadWriteMultipleRegisters = 23; 20 | 21 | public const int MaximumDiscreteRequestResponseSize = 2040; 22 | public const int MaximumRegisterRequestResponseSize = 127; 23 | 24 | // modbus slave exception offset that is added to the function code, to flag an exception 25 | public const byte ExceptionOffset = 128; 26 | 27 | // modbus slave exception codes 28 | public const byte IllegalFunction = 1; 29 | public const byte IllegalDataAddress = 2; 30 | public const byte Acknowledge = 5; 31 | public const byte SlaveDeviceBusy = 6; 32 | 33 | // default setting for number of retries for IO operations 34 | public const int DefaultRetries = 3; 35 | 36 | // default number of milliseconds to wait after encountering an ACKNOWLEGE or SLAVE DEVIC BUSY slave exception response. 37 | public const int DefaultWaitToRetryMilliseconds = 250; 38 | 39 | // default setting for IO timeouts in milliseconds 40 | public const int DefaultTimeout = 1000; 41 | 42 | // smallest supported message frame size (sans checksum) 43 | public const int MinimumFrameSize = 2; 44 | 45 | public const ushort CoilOn = 0xFF00; 46 | public const ushort CoilOff = 0x0000; 47 | 48 | // IP slaves should be addressed by IP 49 | public const byte DefaultIpSlaveUnitId = 0; 50 | 51 | // An existing connection was forcibly closed by the remote host 52 | public const int ConnectionResetByPeer = 10054; 53 | 54 | // Existing socket connection is being closed 55 | public const int WSACancelBlockingCall = 10004; 56 | 57 | // used by the ASCII tranport to indicate end of message 58 | public const string NewLine = "\r\n"; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /NModbus4/NModbus4.xproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 14.0 5 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 6 | 7 | 8 | 9 | 9b07ed72-3ad9-45ec-aae9-295849b309aa 10 | Modbus 11 | .\obj\ 12 | .\bin\ 13 | 14 | 15 | 2.0 16 | True 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /NModbus4/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using System.Resources; 4 | using System.Runtime.CompilerServices; 5 | using System.Runtime.InteropServices; 6 | 7 | [assembly: AssemblyTitle("NModbus4")] 8 | [assembly: AssemblyProduct("NModbus4")] 9 | [assembly: AssemblyCompany("")] 10 | [assembly: AssemblyCopyright("Copyright © 2006 Scott Alexander, 2015 Dmitry Turin")] 11 | [assembly: AssemblyDescription("NModbus4 is a C# implementation of the Modbus protocol. " + 12 | "Provides connectivity to Modbus slave compatible devices and applications. " + 13 | "Supports ASCII, RTU, TCP, and UDP protocols. " + 14 | "NModbus4 it's a fork of NModbus(https://code.google.com/p/nmodbus)")] 15 | 16 | [assembly: AssemblyTrademark("")] 17 | [assembly: AssemblyCulture("")] 18 | [assembly: CLSCompliant(false)] 19 | [assembly: NeutralResourcesLanguage("en-US")] 20 | [assembly: ComVisible(false)] 21 | [assembly: AssemblyVersion("3.0.0.0")] 22 | [assembly: AssemblyFileVersion("3.0.0.0")] 23 | [assembly: AssemblyInformationalVersion("3.0.0-dev")] 24 | 25 | #if !SIGNED 26 | [assembly: InternalsVisibleTo("NModbus4.UnitTests")] 27 | [assembly: InternalsVisibleTo("NModbus4.IntegrationTests")] 28 | [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] 29 | #endif 30 | -------------------------------------------------------------------------------- /NModbus4/Resources.cs: -------------------------------------------------------------------------------- 1 | namespace Modbus 2 | { 3 | internal static class Resources 4 | { 5 | public const string Acknowlege = "Specialized use in conjunction with programming commands.The server (or slave) has accepted the request and is processing it, but a long duration of time will be required to do so.This response is returned to prevent a timeout error from occurring in the client(or master). The client(or master) can next issue a Poll Program Complete message to determine if processing is completed."; 6 | 7 | public const string EmptyEndPoint = "Argument endPoint cannot be empty."; 8 | 9 | public const string GatewayPathUnavailable = "Specialized use in conjunction with gateways, indicates that the gateway was unable to allocate an internal communication path from the input port to the output port for processing the request.Usually means that the gateway is misconfigured or overloaded."; 10 | 11 | public const string GatewayTargetDeviceFailedToRespond = "Specialized use in conjunction with gateways, indicates that no response was obtained from the target device.Usually means that the device is not present on the network."; 12 | 13 | public const string HexCharacterCountNotEven = "Hex string must have even number of characters."; 14 | 15 | public const string IllegalDataAddress = "The data address received in the query is not an allowable address for the server (or slave). More specifically, the combination of reference number and transfer length is invalid.For a controller with 100 registers, the PDU addresses the first register as 0, and the last one as 99. If a request is submitted with a starting register address of 96 and a quantity of registers of 4, then this request will successfully operate(address-wise at least) on registers 96, 97, 98, 99. If a request is submitted with a starting register address of 96 and a quantity of registers of 5, then this request will fail with Exception Code 0x02 “Illegal Data Address” since it attempts to operate on registers 96, 97, 98, 99 and 100, and there is no register with address 100."; 16 | 17 | public const string IllegalDataValue = "A value contained in the query data field is not an allowable value for server(or slave). This indicates a fault in the structure of the remainder of a complex request, such as that the implied length is incorrect.It specifically does NOT mean that a data item submitted for storage in a register has a value outside the expectation of the application program, since the MODBUS protocol is unaware of the significance of any particular value of any particular register."; 18 | 19 | public const string IllegalFunction = "The function code received in the query is not an allowable action for the server (or slave). This may be because the function code is only applicable to newer devices, and was not implemented in the unit selected.It could also indicate that the server(or slave) is in the wrong state to process a request of this type, for example because it is unconfigured and is being asked to return register values."; 20 | 21 | public const string MemoryParityError = "Specialized use in conjunction with function codes 20 and 21 and reference type 6, to indicate that the extended file area failed to pass a consistency check."; 22 | 23 | public const string NetworkBytesNotEven = "Array networkBytes must contain an even number of bytes."; 24 | 25 | public const string SlaveDeviceBusy = "Specialized use in conjunction with programming commands. The server (or slave) is engaged in processing a long–duration program command.The client(or master) should retransmit the message later when the server(or slave) is free."; 26 | 27 | public const string SlaveDeviceFailure = "An unrecoverable error occurred while the server(or slave) was attempting to perform the requested action."; 28 | 29 | public const string SlaveExceptionResponseFormat = "Function Code: {1}{0}Exception Code: {2} - {3}"; 30 | 31 | public const string SlaveExceptionResponseInvalidFunctionCode = "Invalid function code value for SlaveExceptionResponse."; 32 | 33 | public const string TimeoutNotSupported = "The compact framework UDP client does not support timeouts."; 34 | 35 | public const string UdpClientNotConnected = "UdpClient must be bound to a default remote host. Call the Connect method."; 36 | 37 | public const string Unknown = "Unknown slave exception code."; 38 | 39 | public const string WaitRetryGreaterThanZero = "WaitToRetryMilliseconds must be greater than 0."; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /NModbus4/Unme.Common/DisposableUtility.cs: -------------------------------------------------------------------------------- 1 | namespace Modbus.Unme.Common 2 | { 3 | using System; 4 | 5 | internal static class DisposableUtility 6 | { 7 | public static void Dispose(ref T item) 8 | where T : class, IDisposable 9 | { 10 | if (item == null) 11 | { 12 | return; 13 | } 14 | 15 | item.Dispose(); 16 | item = default(T); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /NModbus4/Unme.Common/SequenceUtility.cs: -------------------------------------------------------------------------------- 1 | namespace Modbus.Unme.Common 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | 7 | internal static class SequenceUtility 8 | { 9 | public static IEnumerable Slice(this IEnumerable source, int startIndex, int size) 10 | { 11 | if (source == null) 12 | { 13 | throw new ArgumentNullException(nameof(source)); 14 | } 15 | 16 | var enumerable = source as T[] ?? source.ToArray(); 17 | int num = enumerable.Count(); 18 | 19 | if (startIndex < 0 || num < startIndex) 20 | { 21 | throw new ArgumentOutOfRangeException(nameof(startIndex)); 22 | } 23 | 24 | if (size < 0 || startIndex + size > num) 25 | { 26 | throw new ArgumentOutOfRangeException(nameof(size)); 27 | } 28 | 29 | return enumerable.Skip(startIndex).Take(size); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /NModbus4/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "3.0.0-alpha2", 3 | "description": "NModbus is a C# implementation of the Modbus protocol. Provides connectivity to Modbus slave compatible devices and applications. Supports TCP, and UDP protocols. NModbus4 it's a fork of NModbus(https://code.google.com/p/nmodbus)", 4 | "authors": [ "Dmitry Turin" ], 5 | 6 | "packOptions": { 7 | "tags": [ "modbus", "nmodbus", "tcp", "udp", "master", "slave", "coreclr" ], 8 | "projectUrl": "https://github.com/NModbus4/NModbus4", 9 | "licenseUrl": "http://opensource.org/licenses/MIT" 10 | }, 11 | 12 | "buildOptions": { 13 | "warningsAsErrors": true 14 | }, 15 | 16 | "frameworks": { 17 | "net45": {}, 18 | "netstandard1.3": { 19 | "dependencies": { 20 | "NETStandard.Library": "1.6.0" 21 | } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /NuGet.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | NModbus4 2 | ======= 3 | 4 | | |Build Status|Code Coverage| 5 | |-----------|:----------:|:-----------:| 6 | |**Mono**|[![Build Status](https://travis-ci.org/NModbus4/NModbus4.svg?branch=portable-3.0)](https://travis-ci.org/NModbus4/NModbus4)|| 7 | |**MS .NET**|[![Build status](https://ci.appveyor.com/api/projects/status/9irkluk69cr0f5ed?svg=true)](https://ci.appveyor.com/project/Maxwe11/nmodbus4-ss8e4)|[![codecov.io](https://codecov.io/github/NModbus4/NModbus4/coverage.svg?branch=portable-3.0)](https://codecov.io/github/NModbus4/NModbus4?branch=portable-3.0)| 8 | 9 | [![Join the chat at https://gitter.im/NModbus4/NModbus4](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/NModbus4/NModbus4?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 10 | 11 | NModbus is a C# implementation of the Modbus protocol. 12 | Provides connectivity to Modbus slave compatible devices and applications. 13 | Supports serial ASCII, serial RTU, TCP, and UDP protocols. 14 | NModbus4 it's a fork of NModbus(https://code.google.com/p/nmodbus). 15 | NModbus4 differs from original NModbus in following: 16 | 17 | 1. removed USB support(FtdAdapter.dll) 18 | 2. removed log4net dependency 19 | 3. removed Unme.Common.dll dependency 20 | 4. assembly renamed to NModbus4.dll 21 | 5. target framework changed to .NET 4 22 | 23 | Install 24 | ======= 25 | 26 | To install NModbus4, run the following command in the Package Manager Console 27 | 28 | PM> Install-Package NModbus4 29 | 30 | Documentation 31 | ======= 32 | Documentation is available in chm format (NModbus.chm) 33 | 34 | License 35 | ======= 36 | NModbus4 is licensed under the [MIT license](LICENSE.txt). 37 | -------------------------------------------------------------------------------- /Samples/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Samples")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Samples")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("24958647-c7e3-46c0-885d-89521c4811b8")] 24 | -------------------------------------------------------------------------------- /Samples/Samples.xproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 14.0 5 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 6 | 7 | 8 | 9 | 10 | 24958647-c7e3-46c0-885d-89521c4811b8 11 | Samples 12 | .\obj\ 13 | .\bin\ 14 | 15 | 16 | 17 | 2.0 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /Samples/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0-*", 3 | "description": "Samples Console Application", 4 | 5 | "buildOptions": { 6 | "emitEntryPoint": true, 7 | "warningsAsErrors": true 8 | }, 9 | 10 | "dependencies": { 11 | "NModbus4": { "target": "project" }, 12 | "NModbus4.Serial": { "target": "project" } 13 | }, 14 | 15 | "commands": { 16 | "Samples": "Samples" 17 | }, 18 | 19 | "frameworks": { 20 | "net45": { } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 3.0.{build} 2 | branches: 3 | only: 4 | - portable-3.0 5 | os: Visual Studio 2015 6 | platform: Any CPU 7 | shallow_clone: true 8 | init: 9 | - git config --global core.autocrlf true 10 | install: 11 | - cmd: >- 12 | dotnet restore 13 | build: 14 | project: NModbus4.sln 15 | verbosity: minimal 16 | test_script: 17 | - cd NModbus4.UnitTests 18 | - "SET PATH=C:/Users/appveyor/.nuget/packages/OpenCover/4.6.166/tools;C:/Python34;C:/Python34/Scripts;%PATH%" 19 | - OpenCover.Console.exe -register:user -target:"dotnet.exe" -targetargs:"test" -returntargetcode -filter:"+[NModbus4]*" -excludebyattribute:*.ExcludeFromCodeCoverage* -hideskipped:All -output:./opencover_report.xml 20 | - pip install codecov 21 | - codecov -f "./opencover_report.xml" 22 | -------------------------------------------------------------------------------- /global.json: -------------------------------------------------------------------------------- 1 | { 2 | "projects": [ "NModbus4", "NModbus4.Serial", "NModbus4.UnitTests", "Samples" ] 3 | } 4 | --------------------------------------------------------------------------------