using NLog; using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Linq; using System.Text; using System.Threading.Tasks; namespace GeneralGommunication { public class ModbusRtuServer : IModbusServer { public Logger logger = NLog.LogManager.GetCurrentClassLogger(); const byte MB_ERR_LRC = 0x04;//LRC校验出错 const byte MB_ERR_LEN = 0x01;//指令字节数不足 const byte MB_ERR_NUM_0 = 0x02;//寄存器数量=0 const byte MB_ERR_CNT = 0x03;//字节数!=寄存器数量*2 const byte MB_ERR_NOFUNC = 0x05;//没有这个func ModbusRegReadWriteHandler regRead; ModbusRegReadWriteHandler regWrite; Action> sendMsg; //接收缓冲 List recBuf = new List(); //System.Timers.Timer timer3d5t;//通讯包的 3.5T 时间间隔。 实际只是 15ms定时而已 System.Threading.Timer timer3d5t;//通讯包的 3.5T 时间间隔。 实际只是 15ms定时而已 public void Init(ModbusRegReadWriteHandler regRead, ModbusRegReadWriteHandler regWrite, Action> sendMsg, out Action> recMsg) { this.regRead = regRead; this.regWrite = regWrite; this.sendMsg = sendMsg; recMsg = RecMsg; //timer3d5t = new System.Timers.Timer(); //timer3d5t.Interval = 30;//设得再小也没有用。 它只是用系统的15ms定时器触发而已。 //timer3d5t.AutoReset = false;//触发一次 //timer3d5t.Elapsed += Timer3d5t_Elapsed; timer3d5t = new System.Threading.Timer(new System.Threading.TimerCallback(Timer3d5t_Elapsed), null, System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite); } private void Timer3d5t_Elapsed(object state) { ParsePack(); } //private void Timer3d5t_Elapsed(object sender, System.Timers.ElapsedEventArgs e) //{ // ParsePack(); //} /// /// 读寄存器, 返回已经处理的长度 /// /// 接收缓冲 /// 数据开始序号 /// 数据长度 /// int Modbus_Func03(IEnumerable buf, int startIndex, int len, out byte[] sendBuf) { //请求 //1. slave address : 1byte //2. Function : 1byte //3. Starting Address Hi : 1byte //4. Starting Address Lo : 1byte //5. Number of Register Hi : 1byte //6. Number of Register Lo : 1byte //7. CRC16 : 2bytes //响应 //1. slave address : 1byte //2. Function : 1byte //3. Number * 2 : 1byte //下面重复 Number 次 //4. Data Hi : 1byte //5. Data Lo : 1byte //6. CRC16 : 2bytes //example: //请求 //01 03 00 01 00 01 ?? ?? //响应 //01 03 02 12 34 ?? ?? int req_len = 8; //buf 从 slave address 开始 if (len < req_len) { logger.Error($"REQ 指令字节数不足"); sendBuf = Modbus_DefaultCallback(buf, startIndex, MB_ERR_LEN);//指令异常 return len; } //CRC int adu_len = req_len - 2;//去除crc后的数据包长 UInt16 crc = buf.CRC16(startIndex, req_len - 2); UInt16 rec_crc = buf.ToUInt16_Little_Endian(startIndex + req_len - 2);//CRC 是 小端模式 if (crc != rec_crc) { logger.Error($"REQ CRC 校验出错 接收:{rec_crc:X4} 计算:{crc:X4}"); sendBuf = Modbus_DefaultCallback(buf, startIndex, MB_ERR_LRC);//指令异常 return len;//指令异常,应该全部删除 } byte deviceNo = recBuf[0]; UInt16 startAddr = buf.ToUInt16_Big_Endian(startIndex + 2); UInt16 number = buf.ToUInt16_Big_Endian(startIndex + 4); if (number == 0) { logger.Error($"REQ 没有任何寄存器"); sendBuf = Modbus_DefaultCallback(buf, startIndex, MB_ERR_NUM_0);//指令异常 return req_len; } if (number > 50)//限制数量,因为缓存区不够大 number = 50; List midBuf = new List(); midBuf.Add(deviceNo); midBuf.Add(0x03); midBuf.Add((byte)(number * 2)); UInt16[] regValues = new UInt16[number]; bool ret = this.regRead(deviceNo, startAddr, regValues); if (!ret) { //读取失败,没有设备,不用回复 logger.Debug($"REQ 读取失败,没有设备,不用回复"); sendBuf = null; return req_len; } for (int i = 0; i < number; i++) { midBuf.AddRange(regValues[i].GetBytes_Big_endian()); } sendBuf = midBuf.ToArray(); return req_len; //Modbus_AddCrcAndSend(midBuf); } /// /// Write Single REG /// /// /// /// int Modbus_Func06(IEnumerable buf, int startIndex, int len, out byte[] sendBuf) { //协议为 //1. slave address //2. Function //3. Starting Address Hi //4. Starting Address Lo //5. Data Hi : //6. Data Lo : //7. CRC16 : 2bytes //响应 //1. slave address //2. Function //3. Starting Address Hi //4. Starting Address Lo //5. Data Hi : //6. Data Lo : //7. CRC16 : 2bytes //example: 下面为 ASCII //请求 //01 06 00 01 12 34 ?? ?? //响应 //01 06 00 01 12 34 ?? ?? //buf 从 slave address 开始 int req_len = 8; if (len < req_len) { logger.Error($"REQ 指令字节数不足"); sendBuf = Modbus_DefaultCallback(buf, startIndex, MB_ERR_LEN);//指令异常 return len; } //CRC int adu_len = req_len - 2;//去除crc后的数据包长 UInt16 crc = buf.CRC16(startIndex, req_len - 2); UInt16 rec_crc = buf.ToUInt16_Little_Endian(startIndex + req_len - 2); if (crc != rec_crc) { logger.Error($"REQ CRC 校验出错 接收:{rec_crc:X4} 计算:{crc:X4}"); sendBuf = Modbus_DefaultCallback(buf, startIndex, MB_ERR_LRC);//指令异常 return len;//指令异常,应该全部删除 } byte deviceNo = recBuf[0]; UInt16 startAddr = buf.ToUInt16_Big_Endian(startIndex+2); UInt16 regValue = buf.ToUInt16_Big_Endian(startIndex+4); UInt16[] regValues = new UInt16[] { regValue }; bool ret = this.regWrite(deviceNo, startAddr, regValues); if (!ret) { logger.Debug($"REQ 写入失败,没有设备,不用回复"); sendBuf = null; return req_len; } //完成 List midBuf = new List(); midBuf.AddRange(buf.Skip(startIndex).Take(6)); sendBuf = midBuf.ToArray(); return req_len; //Modbus_AddCrcAndSend(midBuf); } /// /// Write REGs /// /// /// /// int Modbus_Func10(IEnumerable buf, int startIndex, int len, out byte[] sendBuf) { //协议为 //1. slave address //2. Function //3. Starting Address Hi //4. Starting Address Lo //5. Number of Register Hi //6. Number of Register Lo //7. Byte Count //8. Data Hi //9. Data Lo //10. Data Hi //11. Data Lo //12. Data Hi //13. Data Lo //14. CRC16 : 2bytes //响应 //1. slave address //2. Function //3. Starting Address Hi //4. Starting Address Lo //5. Number of Register Hi //6. Number of Register Lo //7. CRC16 : 2bytes //example: 下面为 ASCII //请求 //: 01 10 00 01 00 01 02 12 34 ?? ?? //响应 //: 01 10 00 01 00 01 ?? ?? //buf 已经是 ascii转hex 后结果。 从 slave address 开始 int req_len = 9; if (len < req_len) { logger.Error($"REQ 指令字节数不足"); sendBuf = Modbus_DefaultCallback(buf, startIndex, MB_ERR_LEN);//指令异常 return len; } byte deviceNo = recBuf[0]; UInt16 startAddr = buf.ToUInt16_Big_Endian(startIndex + 2); UInt16 number = buf.ToUInt16_Big_Endian(startIndex + 4); byte cnt = buf.ElementAt(startIndex + 6); req_len = 9 + cnt; if (len < req_len) { logger.Error($"REQ 指令字节数不足"); sendBuf = Modbus_DefaultCallback(buf, startIndex, MB_ERR_LEN);//指令异常 return len; } //CRC int adu_len = req_len - 2;//去除crc后的数据包长 UInt16 crc = buf.CRC16(startIndex, req_len - 2); UInt16 rec_crc = buf.ToUInt16_Little_Endian(startIndex + req_len - 2);//CRC 是 小端模式 if (crc != rec_crc) { logger.Error($"REQ CRC 校验出错 接收:{rec_crc:X4} 计算:{crc:X4}"); sendBuf = Modbus_DefaultCallback(buf, startIndex, MB_ERR_LRC);//指令异常 return len;//指令异常,应该全部删除 } if (number == 0) { //指令异常, 居然没有任何寄存器 logger.Error($"REQ 没有任何寄存器"); sendBuf = Modbus_DefaultCallback(buf, startIndex, MB_ERR_NUM_0); return req_len; } if (cnt != number * 2) { //指令异常,字节数!=寄存器数*2 logger.Error($"REQ 字节数!=寄存器数*2"); sendBuf = Modbus_DefaultCallback(buf, startIndex, MB_ERR_CNT); return req_len; } int index = 7; UInt16[] regValues = new UInt16[number]; for (int i = 0; i < number; i++) { UInt16 regValue = buf.ToUInt16_Big_Endian(startIndex + index); index += 2; regValues[i] = regValue; } bool ret = this.regWrite(deviceNo, startAddr, regValues); if (!ret) { logger.Debug($"REQ 写入失败,没有设备,不用回复"); sendBuf = null; return req_len; } //完成 List midBuf = new List(); midBuf.AddRange(buf.Skip(startIndex).Take(6)); sendBuf = midBuf.ToArray(); return req_len; //Modbus_AddCrcAndSend(midBuf); } byte[] Modbus_DefaultCallback(IEnumerable buf, int startIndex, byte err) { //格式 //Field Name Example(Hex) //Slave Address 01 //Function 81 //Exception Code 02 List midBuf = new List(); midBuf.Add(buf.ElementAt(startIndex + 0)); midBuf.Add((byte)(buf.ElementAt(startIndex + 1) | 0x80)); midBuf.Add(err); return midBuf.ToArray(); //Modbus_AddCrcAndSend(midBuf); } void RecMsg(IEnumerable buf) { //15ms后再处理。 //timer3d5t.Stop(); timer3d5t.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite); recBuf.AddRange(buf); if (logger.IsDebugEnabled) { string msg = recBuf.ToString_Bytes2Hex(); logger.Debug($"REQ {msg}"); } ParsePack(); //timer3d5t.Start(); timer3d5t.Change(30, System.Threading.Timeout.Infinite); } void ParsePack() { //只处理最后一条 //address :1byte //function :1byte //data :Nbyte //CRC16 :2bytes //收到了完成一段数据再判断的 if (recBuf.Count() < 5) { //异常 数据太少 recBuf.Clear(); return; } while (true) { byte func = recBuf[1]; byte[] sendBuf; int len; switch (func) { //case 0x01: // Modbus_Func01(buf, adu_len); // break; case 0x03: len = Modbus_Func03(recBuf, 0, recBuf.Count(), out sendBuf); break; //case 0x05: // Modbus_Func05(buf, adu_len); // break; case 0x06: len = Modbus_Func06(recBuf, 0, recBuf.Count(), out sendBuf); break; //case 0x0F: // Modbus_Func0F(buf, adu_len); // break; case 0x10: len = Modbus_Func10(recBuf, 0, recBuf.Count(), out sendBuf); break; default: { logger.Error($"REQ 不支持功能号"); //全删除 sendBuf = Modbus_DefaultCallback(recBuf, 0, MB_ERR_NOFUNC);//没此func len = recBuf.Count(); } break; } recBuf.RemoveRange(0, len); if (recBuf.Count == 0) { if (sendBuf != null) { //把最后一条发送出去 Modbus_AddCrcAndSend(sendBuf); } return; } } } void Modbus_AddCrcAndSend(IEnumerable buf) { //添加 CRC16 UInt16 crc = buf.CRC16(0, buf.Count()); List sendBuf = new List(); sendBuf.AddRange(buf); sendBuf.AddRange(crc.GetBytes_Little_Endian()); if (logger.IsDebugEnabled) { string msg = sendBuf.ToString_Bytes2Hex(); logger.Debug($"ACK {msg}"); } sendMsg?.Invoke(sendBuf); } } }