Commit b812db29 authored by Andreas Müller's avatar Andreas Müller

Implemented Modbus serial connection to work (with diagslave from ModbusDRIVER.com)

Tests with real devices will follow.
parent cba58e5f
Pipeline #21 passed with stage
in 42 seconds
image: microsoft/dotnet
image: microsoft/dotnet:sdk
stages:
- build
......
......@@ -10,6 +10,8 @@ using AMWD.Modbus.Common.Interfaces;
using AMWD.Modbus.Common.Structures;
using AMWD.Modbus.Common.Util;
using AMWD.Modbus.Common;
using AMWD.Modbus.Serial;
using System.IO.Ports;
namespace ConsoleDemo
{
......@@ -56,7 +58,30 @@ namespace ConsoleDemo
{
Console.Write("Interface: ");
var port = Console.ReadLine().Trim();
client = new SerialClient(port);
Console.Write("Baud: ");
var baud = Convert.ToInt32(Console.ReadLine().Trim());
Console.Write("Data-Bits [7|8]: ");
var dataBits = Convert.ToInt32(Console.ReadLine().Trim());
Console.Write("Stop-Bits [0|1|2|3=1.5]: ");
var stopBits = Convert.ToInt32(Console.ReadLine().Trim());
Console.Write("Parity [0] None [1] Odd [2] Even [3] Mark [4] Space: ");
var parity = Convert.ToInt32(Console.ReadLine().Trim());
Console.Write("Handshake [0] None [1] X-On/Off [2] RTS [3] RTS+X-On/Off: ");
var handshake = Convert.ToInt32(Console.ReadLine().Trim());
client = new SerialClient(port)
{
BaudRate = (BaudRate)baud,
DataBits = dataBits,
StopBits = (StopBits)stopBits,
Parity = (Parity)parity,
Handshake = (Handshake)handshake
};
}
break;
default:
......
......@@ -17,9 +17,14 @@
#region Protocol limitations
/// <summary>
/// The lowest accepted device id.
/// The lowest accepted device id on TCP protocol.
/// </summary>
public const byte MinDeviceId = 0x00;
public const byte MinDeviceIdTcp = 0x00;
/// <summary>
/// The lowest accepted device id on RTU protocol.
/// </summary>
public const byte MinDeviceIdRtu = 0x01;
/// <summary>
/// The highest accepted device id.
......
The MIT License
Copyright (c) 2018 Andreas Müller
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
\ No newline at end of file
......@@ -11,8 +11,6 @@
<Copyright>Copyright (c) 2018 Andreas Müller</Copyright>
<RepositoryUrl>https://github.com/AndreasAmMueller/Modbus.git</RepositoryUrl>
<PackageTags>Modbus</PackageTags>
<PackageLicenseFile>LICENSE.txt</PackageLicenseFile>
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
<PackageId>AMWD.Modbus.Common</PackageId>
<AssemblyName>AMWD.Modbus.Common</AssemblyName>
<RootNamespace>AMWD.Modbus.Common</RootNamespace>
......
This diff is collapsed.
The MIT License
Copyright (c) 2018 Andreas Müller
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
\ No newline at end of file
......@@ -11,8 +11,6 @@
<Copyright>Copyright (c) 2018 Andreas Müller</Copyright>
<RepositoryUrl>https://github.com/AndreasAmMueller/Modbus.git</RepositoryUrl>
<PackageTags>Modbus, Serial, RTU</PackageTags>
<PackageLicenseFile>LICENSE.txt</PackageLicenseFile>
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
<PackageId>AMWD.Modbus.Serial</PackageId>
<AssemblyName>AMWD.Modbus.Serial</AssemblyName>
<RootNamespace>AMWD.Modbus.Serial</RootNamespace>
......
......@@ -210,7 +210,7 @@ namespace AMWD.Modbus.Serial.Protocol
var buffer = new DataBuffer(bytes);
var crcBuff = buffer.GetBytes(buffer.Length - 3, 2);
var crcBuff = buffer.GetBytes(buffer.Length - 2, 2);
var crcCalc = Checksum.CRC16(bytes, 0, bytes.Length - 2);
if (crcBuff[0] != crcCalc[0] || crcBuff[1] != crcCalc[1])
......@@ -237,7 +237,7 @@ namespace AMWD.Modbus.Serial.Protocol
case FunctionCode.ReadHoldingRegisters:
case FunctionCode.ReadInputRegisters:
var len = buffer.GetByte(2);
if (buffer.Length != len + 3)
if (buffer.Length != len + 3 + 2) // following bytes + 3 byte head + 2 byte CRC
{
throw new ArgumentException("Response incomplete");
}
......@@ -254,19 +254,19 @@ namespace AMWD.Modbus.Serial.Protocol
Data = new DataBuffer(buffer.GetBytes(4, buffer.Length - 6));
break;
case FunctionCode.EncapsulatedInterface:
MEIType = (MEIType)buffer.GetByte(8);
MEIType = (MEIType)buffer.GetByte(2);
switch (MEIType)
{
case MEIType.CANOpenGeneralReference:
Data = new DataBuffer(buffer.Buffer.Skip(9).ToArray());
Data = new DataBuffer(buffer.Buffer.Skip(3).ToArray());
break;
case MEIType.ReadDeviceInformation:
MEICategory = (DeviceIDCategory)buffer.GetByte(9);
ConformityLevel = buffer.GetByte(10);
MoreRequestsNeeded = buffer.GetByte(11) > 0;
NextObjectId = buffer.GetByte(12);
ObjectCount = buffer.GetByte(13);
Data = new DataBuffer(buffer.Buffer.Skip(14).ToArray());
MEICategory = (DeviceIDCategory)buffer.GetByte(3);
ConformityLevel = buffer.GetByte(4);
MoreRequestsNeeded = buffer.GetByte(5) > 0;
NextObjectId = buffer.GetByte(6);
ObjectCount = buffer.GetByte(7);
Data = new DataBuffer(buffer.Buffer.Skip(8).ToArray());
break;
default:
throw new NotImplementedException();
......
......@@ -225,12 +225,13 @@ namespace AMWD.Modbus.Tcp.Client
/// <returns>A list of coils or null on error.</returns>
public async Task<List<Coil>> ReadCoils(byte deviceId, ushort startAddress, ushort count)
{
logger?.LogTrace($"ModbusClient.ReadCoils({deviceId}, {startAddress}, {count})");
if (isDisposed)
{
throw new ObjectDisposedException(GetType().FullName);
}
if (deviceId < Consts.MinDeviceId || Consts.MaxDeviceId < deviceId)
if (deviceId < Consts.MinDeviceIdTcp || Consts.MaxDeviceId < deviceId)
{
throw new ArgumentOutOfRangeException(nameof(deviceId));
}
......@@ -308,12 +309,13 @@ namespace AMWD.Modbus.Tcp.Client
/// <returns>A list of discrete inputs or null on error.</returns>
public async Task<List<DiscreteInput>> ReadDiscreteInputs(byte deviceId, ushort startAddress, ushort count)
{
logger?.LogTrace($"ModbusClient.ReadDiscreteInputs({deviceId}, {startAddress}, {count})");
if (isDisposed)
{
throw new ObjectDisposedException(GetType().FullName);
}
if (deviceId < Consts.MinDeviceId || Consts.MaxDeviceId < deviceId)
if (deviceId < Consts.MinDeviceIdTcp || Consts.MaxDeviceId < deviceId)
{
throw new ArgumentOutOfRangeException(nameof(deviceId));
}
......@@ -391,12 +393,13 @@ namespace AMWD.Modbus.Tcp.Client
/// <returns>A list of registers or null on error.</returns>
public async Task<List<Register>> ReadHoldingRegisters(byte deviceId, ushort startAddress, ushort count)
{
logger?.LogTrace($"ModbusClient.ReadHoldingRegisters({deviceId}, {startAddress}, {count})");
if (isDisposed)
{
throw new ObjectDisposedException(GetType().FullName);
}
if (deviceId < Consts.MinDeviceId || Consts.MaxDeviceId < deviceId)
if (deviceId < Consts.MinDeviceIdTcp || Consts.MaxDeviceId < deviceId)
{
throw new ArgumentOutOfRangeException(nameof(deviceId));
}
......@@ -470,12 +473,13 @@ namespace AMWD.Modbus.Tcp.Client
/// <returns>A list of registers or null on error.</returns>
public async Task<List<Register>> ReadInputRegisters(byte deviceId, ushort startAddress, ushort count)
{
logger?.LogTrace($"ModbusClient.ReadInputRegisters({deviceId}, {startAddress}, {count})");
if (isDisposed)
{
throw new ObjectDisposedException(GetType().FullName);
}
if (deviceId < Consts.MinDeviceId || Consts.MaxDeviceId < deviceId)
if (deviceId < Consts.MinDeviceIdTcp || Consts.MaxDeviceId < deviceId)
{
throw new ArgumentOutOfRangeException(nameof(deviceId));
}
......@@ -572,12 +576,13 @@ namespace AMWD.Modbus.Tcp.Client
/// <returns>A map of device information and their content as raw bytes.</returns>>
public async Task<Dictionary<byte, byte[]>> ReadDeviceInformationRaw(byte deviceId, DeviceIDCategory categoryId, DeviceIDObject objectId = DeviceIDObject.VendorName)
{
logger?.LogTrace($"ModbusClient.ReadDeviceInformation({deviceId}, {categoryId}, {objectId})");
if (isDisposed)
{
throw new ObjectDisposedException(GetType().FullName);
}
if (deviceId < Consts.MinDeviceId || Consts.MaxDeviceId < deviceId)
if (deviceId < Consts.MinDeviceIdTcp || Consts.MaxDeviceId < deviceId)
{
throw new ArgumentOutOfRangeException(nameof(deviceId));
}
......@@ -659,6 +664,7 @@ namespace AMWD.Modbus.Tcp.Client
/// <returns>true on success, otherwise false.</returns>
public async Task<bool> WriteSingleCoil(byte deviceId, Coil coil)
{
logger?.LogTrace($"ModbusClient.WriteSingleCoil({deviceId}, {coil})");
if (isDisposed)
{
throw new ObjectDisposedException(GetType().FullName);
......@@ -668,7 +674,7 @@ namespace AMWD.Modbus.Tcp.Client
{
throw new ArgumentNullException(nameof(coil));
}
if (deviceId < Consts.MinDeviceId || Consts.MaxDeviceId < deviceId)
if (deviceId < Consts.MinDeviceIdTcp || Consts.MaxDeviceId < deviceId)
{
throw new ArgumentOutOfRangeException(nameof(deviceId));
}
......@@ -733,6 +739,7 @@ namespace AMWD.Modbus.Tcp.Client
/// <returns>true on success, otherwise false.</returns>
public async Task<bool> WriteSingleRegister(byte deviceId, Register register)
{
logger?.LogTrace($"ModbusClient.WriteSingleRegister({deviceId}, {register})");
if (isDisposed)
{
throw new ObjectDisposedException(GetType().FullName);
......@@ -742,7 +749,7 @@ namespace AMWD.Modbus.Tcp.Client
{
throw new ArgumentNullException(nameof(register));
}
if (deviceId < Consts.MinDeviceId || Consts.MaxDeviceId < deviceId)
if (deviceId < Consts.MinDeviceIdTcp || Consts.MaxDeviceId < deviceId)
{
throw new ArgumentOutOfRangeException(nameof(deviceId));
}
......@@ -805,6 +812,7 @@ namespace AMWD.Modbus.Tcp.Client
/// <returns>true on success, otherwise false.</returns>
public async Task<bool> WriteCoils(byte deviceId, IEnumerable<Coil> coils)
{
logger?.LogTrace($"ModbusClient.WriteCoils({deviceId}, Length: {coils.Count()})");
if (isDisposed)
{
throw new ObjectDisposedException(GetType().FullName);
......@@ -814,7 +822,7 @@ namespace AMWD.Modbus.Tcp.Client
{
throw new ArgumentNullException(nameof(coils));
}
if (deviceId < Consts.MinDeviceId || Consts.MaxDeviceId < deviceId)
if (deviceId < Consts.MinDeviceIdTcp || Consts.MaxDeviceId < deviceId)
{
throw new ArgumentOutOfRangeException(nameof(deviceId));
}
......@@ -904,6 +912,7 @@ namespace AMWD.Modbus.Tcp.Client
/// <returns>true on success, otherwise false.</returns>
public async Task<bool> WriteRegisters(byte deviceId, IEnumerable<Register> registers)
{
logger?.LogTrace($"ModbusClient.WriteRegisters({deviceId}, Length: {registers.Count()})");
if (isDisposed)
{
throw new ObjectDisposedException(GetType().FullName);
......@@ -913,7 +922,7 @@ namespace AMWD.Modbus.Tcp.Client
{
throw new ArgumentNullException(nameof(registers));
}
if (deviceId < Consts.MinDeviceId || Consts.MaxDeviceId < deviceId)
if (deviceId < Consts.MinDeviceIdTcp || Consts.MaxDeviceId < deviceId)
{
throw new ArgumentOutOfRangeException(nameof(deviceId));
}
......
The MIT License
Copyright (c) 2018 Andreas Müller
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
\ No newline at end of file
......@@ -11,8 +11,6 @@
<Copyright>Copyright (c) 2018 Andreas Müller</Copyright>
<RepositoryUrl>https://github.com/AndreasAmMueller/Modbus.git</RepositoryUrl>
<PackageTags>Modbus, TCP</PackageTags>
<PackageLicenseFile>LICENSE.txt</PackageLicenseFile>
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
<PackageId>AMWD.Modbus.Tcp</PackageId>
<AssemblyName>AMWD.Modbus.Tcp</AssemblyName>
<RootNamespace>AMWD.Modbus.Tcp</RootNamespace>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment