Files
TouchSocket/examples/Modbus/ModbusMasterConsoleApp/Program.cs
2025-05-07 17:14:23 +08:00

225 lines
7.6 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//------------------------------------------------------------------------------
// 此代码版权除特别声明或在XREF结尾的命名空间的代码归作者本人若汝棋茗所有
// 源代码使用协议遵循本仓库的开源协议及附加协议若本仓库没有设置则按MIT开源协议授权
// CSDN博客https://blog.csdn.net/qq_40374647
// 哔哩哔哩视频https://space.bilibili.com/94253567
// Gitee源代码仓库https://gitee.com/RRQM_Home
// Github源代码仓库https://github.com/RRQM
// API首页https://touchsocket.net/
// 交流QQ群234762506
// 感谢您的下载和使用
//------------------------------------------------------------------------------
using TouchSocket.Core;
using TouchSocket.Modbus;
using TouchSocket.SerialPorts;
using TouchSocket.Sockets;
namespace ModbusClientConsoleApp;
internal class Program
{
private static async Task Main(string[] args)
{
var master = await GetModbusTcpMasterAsync();
await ReadWriteHoldingRegisters(master);
Console.ReadKey();
}
/// <summary>
/// 要测试请打开Modbus Slave软件设置HoldingRegisters。至少30个长度。
/// </summary>
public static async Task ReadWriteHoldingRegisters(IModbusMaster master)
{
//写入单个寄存器
await master.WriteSingleRegisterAsync(1, 0, 1);//默认short ABCD端序
await master.WriteSingleRegisterAsync(1, 1, 1000);//默认short ABCD端序
using (var valueByteBlock = new ValueByteBlock(1024))
{
valueByteBlock.WriteUInt16(2, EndianType.Big);//ABCD端序
valueByteBlock.WriteUInt16(2000, EndianType.Little);//DCBA端序
valueByteBlock.WriteInt32(int.MaxValue, EndianType.BigSwap);//BADC端序
valueByteBlock.WriteInt64(long.MaxValue, EndianType.LittleSwap);//CDAB端序
//写入字符串会先用4字节表示字符串长度然后按utf8编码写入字符串
valueByteBlock.WriteString("Hello1");
//如果想要直接写入字符串可以使用WriteNormalString方法
//valueByteBlock.WriteNormalString("Hello1", System.Text.Encoding.UTF8);
//注意:写入字符串时,应当保证写入后的字节总数为双数。如果是单数,则会报错。
//写入到寄存器
await master.WriteMultipleRegistersAsync(1, 2, valueByteBlock.ToArray());
}
//读取寄存器
var response = await master.ReadHoldingRegistersAsync(1, 0, 30);
//创建一个读取器
var reader = response.CreateReader();
Console.WriteLine(reader.ReadInt16(EndianType.Big));
Console.WriteLine(reader.ReadInt16(EndianType.Big));
Console.WriteLine(reader.ReadInt16(EndianType.Big));
Console.WriteLine(reader.ReadInt16(EndianType.Little));
Console.WriteLine(reader.ReadInt32(EndianType.BigSwap));
Console.WriteLine(reader.ReadInt64(EndianType.LittleSwap));
Console.WriteLine(reader.ReadString());
}
/// <summary>
/// 读写线圈在测试时请选择对应的Modbus Slave类型且调到线圈操作至少5个长度
/// </summary>
/// <param name="master"></param>
public static async Task ReadWriteCoilsShouldBeOk(IModbusMaster master)
{
//写单个线圈
await master.WriteSingleCoilAsync(1, 0, true);
await master.WriteSingleCoilAsync(1, 1, false);
//写多个线圈
await master.WriteMultipleCoilsAsync(1, 2, new bool[] { true, false, true });
//读取线圈
var values = await master.ReadCoilsAsync(1, 0, 5);
foreach (var value in values)
{
Console.WriteLine(value);
}
}
/// <summary>
/// Tcp协议的主站
/// </summary>
/// <returns></returns>
public static async Task<IModbusMaster> GetModbusTcpMasterAsync()
{
var client = new ModbusTcpMaster();
await client.SetupAsync(new TouchSocketConfig()
.ConfigurePlugins(a =>
{
a.UseModbusTcpMasterReconnectionPlugin()
.UsePolling(TimeSpan.FromSeconds(1));
}));
await client.ConnectAsync("127.0.0.1:502");
return client;
}
/// <summary>
/// Udp协议的主站
/// </summary>
/// <returns></returns>
public static async Task<IModbusMaster> GetModbusUdpMaster()
{
var client = new ModbusUdpMaster();
await client.SetupAsync(new TouchSocketConfig()
.UseUdpReceive()
.SetRemoteIPHost("127.0.0.1:502"));
await client.StartAsync();
return client;
}
/// <summary>
/// 串口协议的主站
/// </summary>
/// <returns></returns>
public static async Task<IModbusMaster> GetModbusRtuMaster()
{
var client = new ModbusRtuMaster();
await client.SetupAsync(new TouchSocketConfig()
.SetSerialPortOption(new SerialPortOption()
{
BaudRate = 9600,
DataBits = 8,
Parity = System.IO.Ports.Parity.Even,
PortName = "COM2",
StopBits = System.IO.Ports.StopBits.One
}));
await client.ConnectAsync();
return client;
}
/// <summary>
/// 基于Tcp协议但使用Rtu的主站
/// </summary>
/// <returns></returns>
public static async Task<IModbusMaster> GetModbusRtuOverTcpMaster()
{
var client = new ModbusRtuOverTcpMaster();
await client.ConnectAsync("127.0.0.1:502");
return client;
}
/// <summary>
/// 基于Udp协议但使用Rtu的主站
/// </summary>
/// <returns></returns>
public static async Task<IModbusMaster> GetModbusRtuOverUdpMaster()
{
var client = new ModbusRtuOverUdpMaster();
await client.SetupAsync(new TouchSocketConfig()
.UseUdpReceive()
.SetRemoteIPHost("127.0.0.1:502"));
await client.StartAsync();
return client;
}
}
internal class MyClass
{
public int P1 { get; set; }
public int P2 { get; set; }
}
public static class MasterReconnectionPluginExtension
{
public static ReconnectionPlugin<IModbusTcpMaster> UseModbusTcpMasterReconnectionPlugin(this IPluginManager pluginManager)
{
ModbusTcpMasterReconnectionPlugin modbusTcpMasterReconnectionPlugin = new ModbusTcpMasterReconnectionPlugin();
pluginManager.Add(modbusTcpMasterReconnectionPlugin);
return modbusTcpMasterReconnectionPlugin;
}
}
internal sealed class ModbusTcpMasterReconnectionPlugin : ReconnectionPlugin<IModbusTcpMaster>, ITcpClosedPlugin
{
public override Func<IModbusTcpMaster, int, Task<bool?>> ActionForCheck { get; set; }
public ModbusTcpMasterReconnectionPlugin()
{
this.ActionForCheck = (c, i) => Task.FromResult<bool?>(c.Online);
}
public async Task OnTcpClosed(ITcpSession client, ClosedEventArgs e)
{
await e.InvokeNext().ConfigureAwait(EasyTask.ContinueOnCapturedContext);
if (client is not IModbusTcpMaster tClient)
{
return;
}
if (e.Manual)
{
return;
}
_ = Task.Run(async () =>
{
while (true)
{
if (this.DisposedValue)
{
return;
}
if (await this.ActionForConnect.Invoke(tClient).ConfigureAwait(EasyTask.ContinueOnCapturedContext))
{
return;
}
}
});
}
}