Modbus主站(Master)
定义
命名空间:TouchSocket.Modbus
程序集:TouchSocket.Modbus.dll
一、说明
Modbus
是OSI模型第7层上的应用层报文传输协议,它在连接至不同类型总线或网络的设备之间提供客户机/服务器通信。
自从 1979 年出现工业串行链路的事实标准以来,Modbus
使成千上万的自动化设备能够通信。目前,继续增加对简单而雅观的Modbus
结构支持。互联网组织能够使TCP/IP栈上的保留系统端口502 访问Modbus
。
所以总结来说Modbus
是一个请求/应答的总线协议。
所以我们开发了这个组件,方便大家使用。
二、特点
- 简单易用。
- 内存池支持
- 高性能
- 易扩展。
- 支持全数据类型的读写。
三、产品应用场景
- 所有
Modbus
使用场景:可跨平台使用。
四、可配置项
无单独配置项。
五、支持插件
无单独支持插件。
六、创建
目前TouchSokcet.Modbus
支持Tcp
、Udp
、Rtu
、RtuOverTcp
、RtuOverUdp
等协议。下面会一一介绍创建过程。
6.1 创建ModbusTcpMaster
public IModbusTcpMaster GetMaster()
{
var master = new ModbusTcpMaster();
master.Connect("127.0.0.1:502");
return master;
}
6.2 创建ModbusUdpMaster
public IModbusMaster GetMaster()
{
var master = new ModbusUdpMaster();
master.Setup(new TouchSocketConfig()
.UseUdpReceive()
.SetRemoteIPHost("127.0.0.1:502"));
master.Start();
return master;
}
6.3 创建ModbusRtuMaster
public IModbusMaster GetMaster()
{
var master = new ModbusRtuMaster();
master.Setup(new TouchSocketConfig()
.SetSerialPortOption(new SerialPortOption()
{
BaudRate = 9600,
DataBits = 8,
Parity = System.IO.Ports.Parity.Even,
PortName = "COM2",
StopBits = System.IO.Ports.StopBits.One
}));
master.Connect();
return master;
}
6.4 创建ModbusRtuOverTcpMaster
public IModbusMaster GetMaster()
{
var master = new ModbusRtuOverTcpMaster();
master.Connect("127.0.0.1:502");
return master;
}
6.5 创建ModbusRtuOverUdpMaster
public IModbusMaster GetMaster()
{
var master = new ModbusRtuOverUdpMaster();
master.Setup(new TouchSocketConfig()
.UseUdpReceive()
.SetRemoteIPHost("127.0.0.1:502"));
master.Start();
return master;
}
七、读写操作
7.1 原生接口操作
所有的Modbus主站都支持以下两种原生接口操作:
//同步发送Modbus请求,并等待响应
IModbusResponse SendModbusRequest(ModbusRequest request, int millisecondsTimeout, CancellationToken token);
//异步发送Modbus请求,并等待响应
Task<IModbusResponse> SendModbusRequestAsync(ModbusRequest request, int millisecondsTimeout, CancellationToken token);
以读线圈操作为例:
ModbusRequest modbusRequest = new ModbusRequest(FunctionCode.ReadCoils);
modbusRequest.SetSlaveId(1);//设置站号。如果是Tcp可以不设置
modbusRequest.SetStartingAddress(0);//设置起始
modbusRequest.SetQuantity(1);//设置数量
//modbusRequest.SetValue(false);//如果是写入类操作,可以直接设定值
var response = master.SendModbusRequest(modbusRequest, 1000, CancellationToken.None);
bool[] bools = response.CreateReader().ToBoolensFromBit().ToArray();
7.2 快捷扩展实现
因为Modbus
的操作一般比较固化,所以ModbusMaster
扩展了以下快捷操作:
读取线圈(FC1)。
bool[] bools = master.ReadCoils(0, 1);
读取离散输入(FC2)。
bool[] bools = master.ReadDiscreteInputs(0, 1);
读取保持寄存器(FC3)。
var response = master.ReadHoldingRegisters(0, 1);
var reader = response.CreateReader();
var value=reader.ReadInt16();
读取输入寄存器(FC4)。
var response = master.ReadInputRegisters(0, 1);
var reader = response.CreateReader();
var value=reader.ReadInt16();
写入单个线圈(FC5)。
master.WriteSingleCoil(0, true);
写入单个寄存器(FC6)。
master.WriteSingleRegister(0, (short)100);
写入多个线圈(FC15)。
master.WriteMultipleCoils(0, new bool[] { true, false, true });
写入多个寄存器(FC16)。
using (var writer = new ValueByteBlock(1024))
{
writer.Write(short.MaxValue, EndianType.Big);//ABCD端序 2字节
writer.Write(int.MaxValue, EndianType.Little);//DCBA端序 4字节
writer.Write(long.MaxValue, EndianType.BigSwap);//BADC端序 8字节
writer.Write(double.MaxValue, EndianType.LittleSwap);//CDAB端序 8字节
master.WriteMultipleRegisters(0, writer.ToArray());
}
提示
以上扩展方法还有更多重载。例如:站号、超时时间、可取消令箭等参数。
八、更多写入与读取
线圈与离散输入的写入与读取比较单一,上述操作即可满足大部分需求。下面介绍读写保持寄存器、读取输入寄存器的多元化方式。
读取寄存器到集合。如果该集合中的数据是一类数据,例如全是uint32类型。那么可以使用下列方式:
//读取寄存器
var response = master.ReadHoldingRegisters(1, 0, 10);//站点1,从0开始读取10个寄存器
//创建一个读取器
var reader = response.CreateReader();
//将数据全部读为无符号32为,且使用大端序,即ABCD
uint[] values=reader.ToUInt32s(EndianType.Big).ToArray();
提示
该集合支持全部基础数据类型,以及DataTiem和TimeSpan。
当寄存器的数据不规则时,可能需要依次读取。例如:
当写入下列数据时:
using (var valueByteBlock = new ValueByteBlock(1024))
{
valueByteBlock.Write((ushort)2, EndianType.Big);//ABCD端序
valueByteBlock.Write((ushort)2000, EndianType.Little);//DCBA端序
valueByteBlock.Write(int.MaxValue, EndianType.BigSwap);//BADC端序
valueByteBlock.Write(long.MaxValue, EndianType.LittleSwap);//CDAB端序
//写入到寄存器
master.WriteMultipleRegisters(1, 0, valueByteBlock.ToArray());
}
就需要依次读取:
//读取寄存器
var response = master.ReadHoldingRegisters(1, 0, 1 + 1 + 2 + 4);
//创建一个读取器
var reader = response.CreateReader();
//依次读取
Console.WriteLine(reader.ReadInt16(EndianType.Big));
Console.WriteLine(reader.ReadInt16(EndianType.Little));
Console.WriteLine(reader.ReadInt32(EndianType.BigSwap));
Console.WriteLine(reader.ReadInt64(EndianType.LittleSwap));