客户需要一个电子看板,类似一个电视大小的, 可以显示生产的型号,单号数量等信息. 电子看板是用Modbus TCP通讯的. 生产线每完成一件产品的测试,扫码打包后, 实际产量要增加1, 所以要和生产数据库连接起来
下载一个开源的C# Modbus的工具
https://github.com/stephan1827/modbusTCP-DotNET
里面关键代码就是构建Modbus TCP的Header
报文头 报文的序列号2字节, 00 00表示ModbusTCP协议,数据长度2字节,设备地址1字节,
功能码为1字节,寄存器地址2字节,读取长度2字节
Modbus的操作对象有四种:线圈、离散输入、保持寄存器、输入寄存器。
对象
Coil线圈 可读可写
DiscreteInputs离散量 只读
InputRegister输入寄存器 只读
HoldingRegiste保持寄存器 可读可写
//功能码
private const byte fctReadCoil = 1;
private const byte fctReadDiscreteInputs = 2;
private const byte fctReadHoldingRegister = 3;
private const byte fctReadInputRegister = 4;
private const byte fctWriteSingleCoil = 5;
private const byte fctWriteSingleRegister = 6;
private const byte fctWriteMultipleCoils = 15;
private const byte fctWriteMultipleRegister = 16;
private const byte fctReadWriteMultipleRegister = 23;
// ------------------------------------------------------------------------
// Create modbus header for read action
private byte[] CreateReadHeader(ushort id, byte unit, ushort startAddress, ushort length, byte function)
{
byte[] data = new byte[12];
byte[] _id = BitConverter.GetBytes((short)id);
data[0] = _id[1]; // Slave id high byte
data[1] = _id[0]; // Slave id low byte
data[5] = 6; // Message size ,数据长度
data[6] = unit; // Slave address 设备地址
data[7] = function; // Function code 功能码
byte[] _adr = BitConverter.GetBytes((short)IPAddress.HostToNetworkOrder((short)startAddress));
data[8] = _adr[0]; // Start address 寄存器地址
data[9] = _adr[1]; // Start address
byte[] _length = BitConverter.GetBytes((short)IPAddress.HostToNetworkOrder((short)length));
data[10] = _length[0]; // Number of data to read 读取长度
data[11] = _length[1]; // Number of data to read
return data;
}
// ------------------------------------------------------------------------
// Create modbus header for write action
private byte[] CreateWriteHeader(ushort id, byte unit, ushort startAddress, ushort numData, ushort numBytes, byte function)
{
byte[] data = new byte[numBytes + 11];
byte[] _id = BitConverter.GetBytes((short)id);
data[0] = _id[1]; // Slave id high byte
data[1] = _id[0]; // Slave id low byte
byte[] _size = BitConverter.GetBytes((short)IPAddress.HostToNetworkOrder((short)(5 + numBytes)));
data[4] = _size[0]; // Complete message size in bytes
data[5] = _size[1]; // Complete message size in bytes
data[6] = unit; // Slave address
data[7] = function; // Function code
byte[] _adr = BitConverter.GetBytes((short)IPAddress.HostToNetworkOrder((short)startAddress));
data[8] = _adr[0]; // Start address
data[9] = _adr[1]; // Start address
if (function >= fctWriteMultipleCoils)
{
byte[] _cnt = BitConverter.GetBytes((short)IPAddress.HostToNetworkOrder((short)numData));
data[10] = _cnt[0]; // Number of bytes
data[11] = _cnt[1]; // Number of bytes
data[12] = (byte)(numBytes - 2);
}
return data;
}
了解了ModBus TCP协议后,我们看看怎么来读写寄存器的例子
try
{
// Create new modbus master and add event functions
MBmaster = new Master("192.168.8.1", 502, true);
MBmaster.OnResponseData += new ModbusTCP.Master.ResponseData(MBmaster_OnResponseData);
MBmaster.OnException += new ModbusTCP.Master.ExceptionData(MBmaster_OnException);
////取得工人人数等信息
ReadHoldingRegister();
lblMsg.Text = "连接成功";
}
catch (SystemException error)
{
MessageBox.Show(error.Message);
lblMsg.Text = error.Message;
}
// ------------------------------------------------------------------------
// read holding register
// 工人人数,计划数量,实际产量,已耗时长,已耗时长显示保留小数位数 存在地址1到6
//
// ------------------------------------------------------------------------
private void ReadHoldingRegister()
{
ushort ID = 3; //自己定义的序号
byte unit = Convert.ToByte("1"); //Modus 地址1
ushort StartAddress = 0; //开始地址
UInt16 Length =6; //长度
if (MBmaster != null)
{
MBmaster.ReadHoldingRegister(ID, unit, StartAddress, Length); //异步方法
}
}
private void MBmaster_OnResponseData(ushort ID, byte unit, byte function, byte[] values)
{
// ------------------------------------------------------------------
// Seperate calling threads
if (this.InvokeRequired)
{
this.BeginInvoke(new Master.ResponseData(MBmaster_OnResponseData), new object[] { ID, unit, function, values });
return;
}
// ------------------------------------------------------------------------
// Identify requested data
switch (ID)
{
case 3:
//"Read holding register";
//根据返回值数组大小,来判断哪个数据
int[] word = ConvertBytesToWords(values);
if(word.Length==6)
{
Console.WriteLine("return ActualCnt=" + ActualCnt);
WorkerCnt = word[0];
PlanCnt = word[1];
ActualCnt = word[2];
txtWorkerCnt.Text = WorkerCnt.ToString();
txtActual.Text = ActualCnt.ToString();
txtPlanCnt.Text = PlanCnt.ToString();
txtLeftCnt.Text = (PlanCnt - ActualCnt).ToString();
}
break;
case 8:
//grpData.Text = "Write multiple register";
break;
}
}