EonaCat.Modbus
1.2.0
Prefix Reserved
.NET 6.0
This package targets .NET 6.0. The package is compatible with this framework or higher.
.NET Standard 2.1
This package targets .NET Standard 2.1. The package is compatible with this framework or higher.
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. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.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 | 170 | 2/23/2024 |