EonaCat.Modbus
1.2.0
Prefix Reserved
dotnet add package EonaCat.Modbus --version 1.2.0
NuGet\Install-Package EonaCat.Modbus -Version 1.2.0
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="EonaCat.Modbus" Version="1.2.0" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add EonaCat.Modbus --version 1.2.0
The NuGet Team does not provide support for this client. Please contact its maintainers for support.
#r "nuget: EonaCat.Modbus, 1.2.0"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install EonaCat.Modbus as a Cake Addin
#addin nuget:?package=EonaCat.Modbus&version=1.2.0
// Install EonaCat.Modbus as a Cake Tool
#tool nuget:?package=EonaCat.Modbus&version=1.2.0
The NuGet Team does not provide support for this client. Please contact its maintainers for support.
EonaCat.Modbus
Below you can find a console example for a client and server: (Tcp and Serial (RS485))
// This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
using System.IO.Ports;
using System.Net;
using System.Text;
using EonaCat.Modbus.Extensions;
using EonaCat.Modbus.Interfaces;
using EonaCat.Modbus.Models;
using EonaCat.Modbus.Serial;
using SerialClient = EonaCat.Modbus.Serial.Client.ModbusClient;
using SerialOverTcpClient = EonaCat.Modbus.SerialOverTcp.Client.ModbusClient;
using TcpClient = EonaCat.Modbus.Tcp.Client.ModbusClient;
using SerialServer = EonaCat.Modbus.Serial.Server.ModbusServer;
using TcpServer = EonaCat.Modbus.Tcp.Server.ModbusServer;
namespace EonaCat.Modbus.Tester;
internal class Program
{
private static readonly string[] _trueList = { "y", "j", "yes", "ja" };
private static async Task<int> Main(string[] _)
{
var cancellationTokenSource = new CancellationTokenSource();
Console.CancelKeyPress += (sender, consoleCancelEventArgs) =>
{
cancellationTokenSource.Cancel();
consoleCancelEventArgs.Cancel = true;
};
Console.WriteLine("EonaCat Modbus Tester");
Console.WriteLine();
try
{
Console.Write("What do you want? [1] Client, [2] Server: ");
var type = Convert.ToInt32(Console.ReadLine().Trim());
return type switch
{
1 => await RunClientAsync(cancellationTokenSource.Token).ConfigureAwait(false),
2 => await RunServerAsync(cancellationTokenSource.Token).ConfigureAwait(false),
_ => throw new ArgumentException($"Unknown option: {type}")
};
}
catch (Exception ex)
{
Console.WriteLine($"App terminated unexpectedly: {ex.InnerException?.Message ?? ex.Message}");
return 1;
}
}
private static async Task<int> RunClientAsync(CancellationToken cancellationToken)
{
Console.Write("Connection Type [1] Tcp, [2] RS485 [3] SerialOverTcp");
var connectionType = Convert.ToInt32(Console.ReadLine().Trim());
IModbusClient client = null;
try
{
client = connectionType switch
{
1 => CreateTcpClient(),
2 => CreateSerialClient(),
3 => CreateSerialOverTcpClient(),
_ => throw new ArgumentException($"Unknown type: {connectionType}")
};
await ConnectClientAsync(client, cancellationToken).ConfigureAwait(false);
while (!cancellationToken.IsCancellationRequested)
{
var id = GetUserInput<byte>("Device ID");
var fn = GetUserInput<int>("Function [1] Read Register, [2] Write Register, [3] Device Info");
switch (fn)
{
case 1:
await ProcessReadRegisterAsync(client, id, cancellationToken).ConfigureAwait(false);
break;
case 2:
await ProcessWriteRegistersAsync(client, id).ConfigureAwait(false);
break;
case 3:
await ProcessDeviceInfoAsync(client, id).ConfigureAwait(false);
break;
default:
Console.WriteLine("Invalid function selection");
break;
}
Console.WriteLine("New Request? [y/N]: ");
var again = Console.ReadLine().Trim().ToLower();
if (!_trueList.Contains(again))
{
return 0;
}
}
return 0;
}
catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
{
return 0;
}
finally
{
Console.WriteLine("Disposing");
client?.Dispose();
Console.WriteLine("Disposed");
}
}
private static IModbusClient CreateSerialOverTcpClient()
{
Console.Write("Hostname: ");
var host = Console.ReadLine().Trim();
Console.Write("Port: ");
var port = Convert.ToInt32(Console.ReadLine().Trim());
return new SerialOverTcpClient(host, port);
}
private static TcpClient CreateTcpClient()
{
Console.Write("Hostname: ");
var host = Console.ReadLine().Trim();
Console.Write("Port: ");
var port = Convert.ToInt32(Console.ReadLine().Trim());
return new TcpClient(host, port);
}
private static SerialClient CreateSerialClient()
{
Console.Write("Interface: ");
var port = Console.ReadLine().Trim();
var baudRate = GetUserInput<int>("Baud");
var stopBits = GetUserInput<int>("Stop-Bits [0|1|2|3=1.5]");
var parity = GetUserInput<int>("Parity [0] None [1] Odd [2] Even [3] Mark [4] Space");
var handshake = GetUserInput<int>("Handshake [0] None [1] X-On/Off [2] RTS [3] RTS+X-On/Off");
var timeout = GetUserInput<int>("Timeout (ms)");
var setDriver = GetUserInput<int>("Set Driver to RS485 [0] No [1] Yes");
var client = new SerialClient(port)
{
BaudRate = (BaudRate)baudRate,
DataBits = 8,
StopBits = (StopBits)stopBits,
Parity = (Parity)parity,
Handshake = (Handshake)handshake,
SendTimeout = TimeSpan.FromMilliseconds(timeout),
ReceiveTimeout = TimeSpan.FromMilliseconds(timeout)
};
if (setDriver == 1)
{
client.DriverEnableRs485 = true;
}
return client;
}
private static async Task ConnectClientAsync(IModbusClient client, CancellationToken cancellationToken)
{
await Task.WhenAny(client.Connect(cancellationToken), Task.Delay(Timeout.Infinite, cancellationToken)).ConfigureAwait(false);
if (cancellationToken.IsCancellationRequested)
{
Environment.Exit(0);
}
}
private static async Task ProcessReadRegisterAsync(IModbusClient client, byte id,
CancellationToken cancellationToken = default)
{
ushort address = 0;
ushort count = 1;
Console.WriteLine();
Console.Write("Address : ");
address = GetUserInput<ushort>();
Console.Write("DataType: ");
var type = Console.ReadLine().Trim();
if (type == "string")
{
Console.Write("Register Count: ");
count = GetUserInput<ushort>();
}
Console.WriteLine();
Console.Write("Run as loop? [y/N]: ");
var loop = Console.ReadLine().Trim().ToLower();
var interval = _trueList.Contains(loop) ? GetUserInput<int>("Loop interval (milliseconds)") : 0;
Console.WriteLine();
do
{
try
{
Console.Write("Result : ");
var result = await ReadAndPrintResultAsync(client, id, type, address, count).ConfigureAwait(false);
}
catch
{
// Do nothing
}
await Task.Delay(TimeSpan.FromMilliseconds(interval), cancellationToken).ConfigureAwait(false);
} while (interval > 0 && !cancellationToken.IsCancellationRequested);
}
private static async Task ProcessDeviceInfoAsync(IModbusClient client, byte id)
{
Console.Write("[1] Basic, [2] Regular, [3] Extended: ");
var cat = GetUserInput<int>();
Dictionary<DeviceIdObject, string> info = null;
switch (cat)
{
case 1:
case 2:
case 3:
info = await client.ReadDeviceInformation(id, (DeviceIdCategory)cat,
cancellationToken: CancellationToken.None).ConfigureAwait(false);
break;
}
PrintDeviceInfo(info);
}
private static async Task ProcessWriteRegistersAsync(IModbusClient client, byte id)
{
Console.Write("Address: ");
var address = GetUserInput<ushort>();
Console.Write("Bytes (HEX): ");
var byteStr = Console.ReadLine().Trim().Replace(" ", "").ToLower();
var bytes = Enumerable.Range(0, byteStr.Length)
.Where(i => i % 2 == 0)
.Select(i => Convert.ToByte(byteStr.Substring(i, 2), 16))
.ToArray();
var registers = Enumerable.Range(0, bytes.Length / 2)
.Select(i => new Register
{
Type = ModbusObjectType.HoldingRegister,
Address = (ushort)(address + i),
HighByte = bytes[i * 2],
LowByte = bytes[i * 2 + 1]
})
.ToList();
if (client != null && !await client.WriteRegisters(id, registers, CancellationToken.None).ConfigureAwait(false))
{
throw new Exception($"Writing '{byteStr}' to address {address} failed");
}
}
private static async Task<object> ReadAndPrintResultAsync(IModbusClient client, byte id, string dataType,
ushort address, ushort count)
{
List<Register> result = null;
switch (dataType.Trim().ToLower())
{
case "byte":
result = await client.ReadHoldingRegisters(id, address, count, CancellationToken.None).ConfigureAwait(false);
break;
case "ushort":
result = await client.ReadHoldingRegisters(id, address, count, CancellationToken.None).ConfigureAwait(false);
break;
case "uint":
result = await client.ReadHoldingRegisters(id, address, 2, CancellationToken.None).ConfigureAwait(false);
break;
case "ulong":
result = await client.ReadHoldingRegisters(id, address, 4, CancellationToken.None).ConfigureAwait(false);
break;
case "sbyte":
result = await client.ReadHoldingRegisters(id, address, count, CancellationToken.None).ConfigureAwait(false);
break;
case "short":
result = await client.ReadHoldingRegisters(id, address, count, CancellationToken.None).ConfigureAwait(false);
break;
case "int":
result = await client.ReadHoldingRegisters(id, address, 2, CancellationToken.None).ConfigureAwait(false);
break;
case "long":
result = await client.ReadHoldingRegisters(id, address, 4, CancellationToken.None).ConfigureAwait(false);
break;
case "float":
result = await client.ReadHoldingRegisters(id, address, 2, CancellationToken.None).ConfigureAwait(false);
break;
case "double":
result = await client.ReadHoldingRegisters(id, address, 4, CancellationToken.None).ConfigureAwait(false);
break;
case "string":
var stringResult = await client.ReadHoldingRegisters(id, address, count, CancellationToken.None).ConfigureAwait(false);
Console.WriteLine();
Console.WriteLine("UTF8: " + stringResult?.GetString(count));
Console.WriteLine("Unicode: " + stringResult?.GetString(count, 0, Encoding.Unicode));
Console.WriteLine("BigEndianUnicode: " + stringResult?.GetString(count, 0, Encoding.BigEndianUnicode));
return stringResult;
default:
Console.WriteLine("DataType unknown");
return null;
}
Console.WriteLine(result?.GetValue(dataType));
return result;
}
private static void PrintDeviceInfo(Dictionary<DeviceIdObject, string> info)
{
if (info != null)
{
foreach (var kvp in info)
Console.WriteLine($"{kvp.Key}: {kvp.Value}");
}
}
private static T GetUserInput<T>(string prompt = null)
{
if (!string.IsNullOrEmpty(prompt))
{
Console.Write($"{prompt}: ");
}
var input = Console.ReadLine().Trim();
return (T)Convert.ChangeType(input, typeof(T));
}
private static void DisposeClient(IModbusClient client)
{
Console.WriteLine("Disposing");
client?.Dispose();
Console.WriteLine("Disposed");
}
private static async Task<int> RunServerAsync(CancellationToken cancellationToken)
{
Console.Write("Connection Type [1] Tcp, [2] RS485: ");
var cType = Convert.ToInt32(Console.ReadLine().Trim());
IModbusServer server = null;
try
{
server = cType switch
{
1 => CreateTcpServer(),
2 => CreateSerialServer(),
_ => throw new ArgumentException("Type unknown")
};
await InitializeServerAsync(server, cancellationToken).ConfigureAwait(false);
Console.WriteLine("EonaCat Modbus: Server is running... press CTRL+C to exit.");
await Task.Delay(Timeout.Infinite, cancellationToken).ConfigureAwait(false);
}
catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
{
// Do nothing
}
finally
{
DisposeServer(server);
}
return 0;
}
private static TcpServer CreateTcpServer()
{
Console.Write("Bind IP address: ");
var ip = IPAddress.Parse(Console.ReadLine().Trim());
Console.Write("Port: ");
var port = Convert.ToInt32(Console.ReadLine().Trim());
var tcp = new TcpServer(port, ip)
{
Timeout = TimeSpan.FromSeconds(3)
};
return tcp;
}
private static SerialServer CreateSerialServer()
{
Console.Write("Interface: ");
var port = Console.ReadLine().Trim();
var baudRate = GetUserInput<int>("Baud");
var stopBits = GetUserInput<int>("Stop-Bits [0|1|2|3=1.5]");
var parity = GetUserInput<int>("Parity [0] None [1] Odd [2] Even [3] Mark [4] Space");
var handshake = GetUserInput<int>("Handshake [0] None [1] X-On/Off [2] RTS [3] RTS+X-On/Off");
var timeout = GetUserInput<int>("Timeout (ms)");
var server = new SerialServer(port)
{
BaudRate = (BaudRate)baudRate,
DataBits = 8,
StopBits = (StopBits)stopBits,
Parity = (Parity)parity,
Handshake = (Handshake)handshake,
Timeout = TimeSpan.FromMilliseconds(timeout)
};
return server;
}
private static async Task InitializeServerAsync(IModbusServer server, CancellationToken cancellationToken)
{
// Add fake modbus devices
server.AddDevice(1);
server.AddDevice(2);
server.AddDevice(3);
// Create modbus registers
Register.Create(123.45f, 100).ForEach(register => server.SetHoldingRegister(1, register));
Register.Create(543.21f, 65534).ForEach(register => server.SetHoldingRegister(2, register));
Register.Create(999.99f, 102).ForEach(register => server.SetHoldingRegister(3, register));
await Task.Delay(0, cancellationToken).ConfigureAwait(false); // Ensure the method is async
}
private static void DisposeServer(IDisposable server)
{
server?.Dispose();
}
}
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net5.0 was computed. net5.0-windows was computed. net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 is compatible. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
.NET Core | netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
.NET Standard | netstandard2.1 is compatible. |
MonoAndroid | monoandroid was computed. |
MonoMac | monomac was computed. |
MonoTouch | monotouch was computed. |
Tizen | tizen60 was computed. |
Xamarin.iOS | xamarinios was computed. |
Xamarin.Mac | xamarinmac was computed. |
Xamarin.TVOS | xamarintvos was computed. |
Xamarin.WatchOS | xamarinwatchos was computed. |
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
-
.NETStandard 2.1
- Microsoft.Extensions.Logging (>= 8.0.0)
- System.IO.Ports (>= 8.0.0)
-
net6.0
- Microsoft.Extensions.Logging (>= 8.0.0)
- System.IO.Ports (>= 8.0.0)
-
net7.0
- Microsoft.Extensions.Logging (>= 8.0.0)
- System.IO.Ports (>= 8.0.0)
-
net8.0
- Microsoft.Extensions.Logging (>= 8.0.0)
- System.IO.Ports (>= 8.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated |
---|---|---|
1.2.0 | 98 | 2/23/2024 |