客户需要一个电子看板,类似一个电视大小的, 可以显示生产的型号,单号数量等信息. 电子看板是用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; } }