Modbus主站(Master)
定义
一、说明
Modbus
是OSI模型第7层上的应用层报文传输协议,它在连接至不同类型总线或网络的设备之间提供客户机/服务器通信。
自从 1979 年出现工业串行链路的事实标准以来,Modbus
使成千上万的自动化设备能够通信。目前,继续增加对简单而雅观的Modbus
结构支持。互联网组织能够使TCP/IP栈上的保留系统端口502 访问Modbus
。
所以总结来说Modbus
是一个请求/应答的总线协议。
所以我们开发了这个组件,方便大家使用。
二、特点
- 简单易用。
- 内存池支持
- 高性能
- 易扩展。
- 支持全数据类型的读写。
三、产品应用场景
- 所有
Modbus
使用场景:可跨平台使用。
四、可配置项
无单独配置项。
五、支持插件
无单独支持插件。
六、创建
目前TouchSocket.Modbus
支持Tcp
、Udp
、Rtu
、RtuOverTcp
、RtuOverUdp
等协议。下面会一一介绍创建过程。
6.1 创建ModbusTcpMaster
6.2 创建ModbusUdpMaster
6.3 创建ModbusRtuMaster
6.4 创建ModbusRtuOverTcpMaster
6.5 创建ModbusRtuOverUdpMaster
七、读写操作
7.1 原生接口操作
所有的Modbus
主站都支持以下原生接口操作:
以读线圈操作为例:
7.2 快捷扩展实现
因为Modbus
的操作一般比较固化,所以ModbusMaster
扩展了以下快捷操作:
7.2.1 读取线圈(FC1)。
7.2.2 读取离散输入(FC2)。
7.2.3 读取保持寄存器(FC3)。
7.2.4 读取输入寄存器(FC4)。
7.2.5 写入单个线圈(FC5)。
7.2.6 写入单个寄存器(FC6)。
7.2.7 写入多个线圈(FC15)。
7.2.8 写入多个寄存器(FC16)。
写入字符串时,应当保证写入后的字节总数为双数。如果是单数,则会报错。
以上扩展方法还有更多重载。例如:站号、超时时间、可取消令箭等参数。
八、更多写入与读取
线圈与离散输入的写入与读取比较单一,上述操作即可满足大部分需求。
下面介绍读写保持寄存器、读取输入寄存器的多元化方式。
读取寄存器到集合。如果该集合中的数据是一类数据,例如全是uint32
类型。那么可以使用下列方式:
该集合支持全部基础数据类型,和非托管类型,例如:DataTime
和TimeSpan
。
当寄存器的数据不规则时,可能需要依次读取。例如:
当写入下列数据时:
就需要依次读取:
读写任意类型:
配合序列化模块,可以任意读写任意类型。
上述所有类型可以任意组合使用,只需要读取的时候按序读取即可。
九、ModbusObject操作
ModbusObject的操作由于性能和其它设计缺陷问题,以后版本不再维护,也可能会废弃,请谨慎使用。
此处更为推荐使用Plc Modbus配合PlcObject
9.1 基本使用
一般的,我们使用Modbus
,都是通过Master直接Read
或Write
。所以有时候需要维护的代码会非常多,而且容易出错。
所以,我们提供了ModbusObject
,可以简化Modbus的读写操作。
ModbusObject
可以理解为一个实体类,我们只需要定义需要读写的属性,然后就可以直接读写了。
例如:我们需要读写线圈。
则声明一个新建类MyModbusObject
,继承ModbusObject
。
然后声明一个属性MyProperty1
。类型为bool
。并使用ModbusProperty
特性标记,同时指定站号、数据区、起始地址、超时时间等。
然后在属性实现中使用GetValue
和SetValue
方法。
class MyModbusObject : ModbusObject
{
/// <summary>
/// 声明一个来自线圈的bool属性。
/// <para>
/// 配置:站号、数据区、起始地址、超时时间
/// </para>
/// </summary>
[ModbusProperty(SlaveId = 1, Partition = Partition.Coils, StartAddress = 0, Timeout = 1000)]
public bool MyProperty1
{
get { return this.GetValue<bool>(); }
set { this.SetValue(value); }
}
}
然后我们可以通过IModbusMaster
的扩展方法来创建该对象。
然后可以像访问属性那样的访问Modbus
。
var master = GetModbusTcpMaster();
var myModbusObject = master.CreateModbusObject<MyModbusObject>();
myModbusObject.MyProperty1 = true;//直接赋值线圈
Console.WriteLine(myModbusObject.MyProperty1.ToJsonString());//读取,然后以json格式化
9.2 读写寄存器
对于寄存器,我们也可以以属性的方式直接读写基础类型。
例如下列,我们可以直接读写short
类型。
在配置时,除了可配置站号、数据区、起始地址、超时时间外,还可以配置端序。
class MyModbusObject : ModbusObject
{
/// <summary>
/// 声明一个来自保持寄存器的short属性。
/// <para>
/// 配置:站号、数据区、起始地址、超时时间、端序
/// </para>
/// </summary>
[ModbusProperty(SlaveId = 1, Partition = Partition.HoldingRegisters, StartAddress = 0, Timeout = 1000, EndianType = TouchSocket.Core.EndianType.Big)]
public short MyProperty3
{
get { return this.GetValue<short>(); }
set { this.SetValue(value); }
}
/// <summary>
/// 声明一个来自输入寄存器的short属性。
/// <para>
/// 配置:站号、数据区、起始地址、超时时间、端序
/// </para>
/// </summary>
[ModbusProperty(SlaveId = 1, Partition = Partition.InputRegisters, StartAddress = 0, Timeout = 1000, EndianType = TouchSocket.Core.EndianType.Big)]
public short MyProperty4
{
get { return this.GetValue<short>(); }
}
}
常用的数据类型,基本都支持,例如:int16、uint16、int32、uint32、int64、uint64、float、double、char等。
9.3 读写数组
当操作的数据是数组时,也可以直接读写。但是需要使用GetValueArray
和SetValueArray
方法。
使用数组时,需要指定数组的长度。也就是Quantity
。
在线圈和离散输入中,该值就是读取的数量。
在寄存器中,该值是读取时的数组长度,并非寄存器个数。例如:当读取int32数组时,如果该值是5,那就是需要读取10个寄存器。
class MyModbusObject : ModbusObject
{
/// <summary>
/// 声明一个来自线圈的bool数组属性。
/// <para>
/// 配置:站号、数据区、起始地址、超时时间、数量
/// </para>
/// </summary>
[ModbusProperty(SlaveId = 1, Partition = Partition.Coils, StartAddress = 1, Timeout = 1000, Quantity = 9)]
public bool[] MyProperty11
{
get { return this.GetValueArray<bool>(); }
set { this.SetValueArray(value); }
}
/// <summary>
/// 声明一个来自离散输入的bool数组属性。
/// <para>
/// 配置:站号、数据区、起始地址、超时时间、数量
/// </para>
/// </summary>
[ModbusProperty(SlaveId = 1, Partition = Partition.DiscreteInputs, StartAddress = 1, Timeout = 1000, Quantity = 9)]
public bool MyProperty22
{
get { return this.GetValue<bool>(); }
}
/// <summary>
/// 声明一个来自保持寄存器的short数组属性。
/// <para>
/// 配置:站号、数据区、起始地址、超时时间、端序、数组长度
/// </para>
/// </summary>
[ModbusProperty(SlaveId = 1, Partition = Partition.HoldingRegisters, StartAddress = 1, Timeout = 1000, EndianType = TouchSocket.Core.EndianType.Big, Quantity = 9)]
public short[] MyProperty33
{
get { return this.GetValueArray<short>(); }
set { this.SetValueArray(value); }
}
/// <summary>
/// 声明一个来自输入寄存器的short数组属性。
/// <para>
/// 配置:站号、数据区、起始地址、超时时间、端序、数组长度
/// </para>
/// </summary>
[ModbusProperty(SlaveId = 1, Partition = Partition.InputRegisters, StartAddress = 0, Timeout = 1000, EndianType = TouchSocket.Core.EndianType.Big, Quantity = 10)]
public short[] MyProperty44
{
get { return this.GetValueArray<short>(); }
}
}