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 ModbusAsciiServer: IModbusServer { 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 const byte MB_ERR_FORMAT = 0x06;//ASCII 翻译出错 enum ModbusAsciiStatus { MBA_STATUS_3A = 0, MBA_STATUS_0A = 2 } ModbusAsciiStatus status; ModbusRegReadWriteHandler regRead; ModbusRegReadWriteHandler regWrite; Action<IEnumerable<byte>> sendMsg; //接收缓冲 List<byte> recBuf = new List<byte>(); //上一次接收数据的时间 DateTime lastRecMsgTime; public void Init(ModbusRegReadWriteHandler regRead, ModbusRegReadWriteHandler regWrite, Action<IEnumerable<byte>> sendMsg, out Action<IEnumerable<byte>> recMsg) { this.regRead = regRead; this.regWrite = regWrite; this.sendMsg = sendMsg; recMsg = RecMsg; //每个包间隔最大1s。大于1s,清空 this.status = ModbusAsciiStatus.MBA_STATUS_3A; } private void T_Elapsed(object sender, System.Timers.ElapsedEventArgs e) { Modbus_IntervalCb(); } /// <summary> /// Read REGs /// </summary> /// <param name="buf">传入通讯字元</param> /// <param name="startIndex"></param> /// <param name="len">buf长度</param> void Modbus_Func03(IEnumerable<byte> buf, int startIndex, int len) { //请求 //0. ':' 0x3A //1. slave address : 2bytes ascii //2. Function : 2bytes ascii //3. Starting Address Hi : 2bytes ascii //4. Starting Address Lo : 2bytes ascii //5. Number of Register Hi : 2bytes ascii //6. Number of Register Lo : 2bytes ascii //7. LRC : 2bytes ascii //8. 0x0D 0x0A //响应 //0. ':' 0x3A //1. slave address : 2bytes ascii //2. Function : 2bytes ascii //3. Number * 2 : 2bytes ascii //下面重复 Number 次 //4. Data Hi : 2bytes ascii //5. Data Lo : 2bytes ascii //6. LRC : 2bytes ascii //7. 0x0D 0x0A //example: 下面为 ASCII //请求 //: 01 03 00 01 00 01 ?? (0X0D) (0X0A) //响应 //: 01 03 02 12 34 ?? (0X0D) (0X0A) //buf 已经是 ascii转hex 后结果。 从 slave address 开始,没有LRC if (len < 6) { Modbus_DefaultCallback(buf, startIndex, MB_ERR_LEN);//指令异常 return; } byte deviceNo = buf.ElementAt(startIndex); UInt16 startAddr = buf.ToUInt16_Big_Endian(startIndex + 2); UInt16 number = buf.ToUInt16_Big_Endian(startIndex + 4); if (number == 0) { Modbus_DefaultCallback(buf, startIndex, MB_ERR_NUM_0);//指令异常 return; } if (number > 50)//限制数量,因为缓存区不够大 number = 50; List<byte> midBuf = new List<byte>(); 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) { //读取失败,没有设备,不用回复 return; } for (int i = 0; i < number; i++) { midBuf.AddRange(regValues[i].GetBytes_Big_endian()); } Modbus_AddLrcAndSend(midBuf); } /// <summary> /// Write Single REG /// </summary> /// <param name="buf"></param> /// <param name="startIndex"></param> /// <param name="len"></param> void Modbus_Func06(IEnumerable<byte> buf, int startIndex, int len) { //请求 //0. ':' 0x3A //1. slave address : 2bytes ascii //2. Function : 2bytes ascii //3. Starting Address Hi : 2bytes ascii //4. Starting Address Lo : 2bytes ascii //5. Data Hi : 2bytes ascii //6. Data Lo : 2bytes ascii //7. LRC : 2bytes ascii //8. 0x0D 0x0A //响应 //0. ':' 0x3A //1. slave address : 2bytes ascii //2. Function : 2bytes ascii //3. Starting Address Hi : 2bytes ascii //4. Starting Address Lo : 2bytes ascii //5. Data Hi : 2bytes ascii //6. Data Lo : 2bytes ascii //7. LRC : 2bytes ascii //8. 0x0D 0x0A //example: 下面为 ASCII //请求 //: 01 06 00 01 00 01 ?? (0X0D) (0X0A) //响应 //: 01 06 00 01 00 01 ?? (0X0D) (0X0A) //buf 已经是 ascii转hex 后结果。 从 slave address 开始,没有LRC if (len < 6) { Modbus_DefaultCallback(buf, startIndex, MB_ERR_LEN);//指令异常 return; } byte deviceNo = buf.ElementAt(startIndex); 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) { //写入失败,没有设备,不用回复 return; } //完成 List<byte> midBuf = new List<byte>(); midBuf.AddRange(buf.Skip(startIndex).Take(6)); Modbus_AddLrcAndSend(midBuf); } /// <summary> /// Write REGs /// </summary> /// <param name="buf"></param> /// <param name="startIndex"></param> /// <param name="len"></param> void Modbus_Func10(IEnumerable<byte> buf, int startIndex, int len) { //请求 //0. ':' 0x3A //1. slave address : 2bytes ascii //2. Function : 2bytes ascii //3. Starting Address Hi : 2bytes ascii //4. Starting Address Lo : 2bytes ascii //5. Number of Register Hi : 2bytes ascii //6. Number of Register Lo : 2bytes ascii //7. Byte Count : 2bytes ascii //8. Data Hi : 2bytes ascii //9. Data Lo : 2bytes ascii //10. Data Hi : 2bytes ascii //11. Data Lo : 2bytes ascii //12. Data Hi : 2bytes ascii //13. Data Lo : 2bytes ascii //14. LRC : 2bytes ascii //15. 0x0D 0x0A //响应 //0. ':' 0x3A //1. slave address : 2bytes ascii //2. Function : 2bytes ascii //3. Starting Address Hi : 2bytes ascii //4. Starting Address Lo : 2bytes ascii //5. Number of Register Hi : 2bytes ascii //6. Number of Register Lo : 2bytes ascii //7. LRC : 2bytes ascii //8. 0x0D 0x0A //example: 下面为 ASCII //请求 //: 01 10 00 01 00 01 02 12 34 ?? (0X0D) (0X0A) //响应 //: 01 10 00 01 00 01 ?? (0X0D) (0X0A) //buf 已经是 ascii转hex 后结果。 从 slave address 开始,没有LRC if (len < 7) { //指令异常,指令字节数不足 Modbus_DefaultCallback(buf, startIndex, MB_ERR_LEN); return; } byte deviceNo = buf.ElementAt(startIndex); UInt16 startAddr = buf.ToUInt16_Big_Endian(startIndex + 2); UInt16 number = buf.ToUInt16_Big_Endian(startIndex + 4); byte cnt = buf.ElementAt(startIndex + 6); if (number == 0) { //指令异常, 居然没有任何寄存器 Modbus_DefaultCallback(buf, startIndex, MB_ERR_NUM_0); return; } if (cnt != number * 2) { //指令异常,字节数!=寄存器数*2 Modbus_DefaultCallback(buf, startIndex, MB_ERR_CNT); return; } if (len < (7 + cnt)) { //指令异常, 接受的字符不足够 Modbus_DefaultCallback(buf, startIndex, MB_ERR_LEN); return; } 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) { //写入失败,没有设备,不用回复 return; } //完成 List<byte> midBuf = new List<byte>(); midBuf.AddRange(buf.Skip(startIndex).Take(6)); Modbus_AddLrcAndSend(midBuf); } void Modbus_DefaultCallback(IEnumerable<byte> buf, int startIndex, byte err) { //格式 //Field Name Example(Hex) //Slave Address 01 //Function 81 //Exception Code 02 List<byte> midBuf = new List<byte>(); midBuf.Add(buf.ElementAt(startIndex + 0)); midBuf.Add((byte)(buf.ElementAt(startIndex + 1) | 0x80)); midBuf.Add(err); Modbus_AddLrcAndSend(midBuf); } int last_index = -1; void RecMsg(IEnumerable<byte> buf) { if (lastRecMsgTime != DateTime.MinValue) { if (DateTime.Now - lastRecMsgTime > TimeSpan.FromSeconds(1)) { //异常,清空数据 recBuf.Clear(); } } lastRecMsgTime = DateTime.Now; recBuf.AddRange(buf); bool ret = false; while (true) { int index = last_index + 1; if (index >= recBuf.Count()) break; ret = Modbus_SingleRec(recBuf, index); if (ret == true) { last_index = -1; break; } else { last_index = index; } } if (ret) recBuf.Clear(); } void ClearRecMsg() { recBuf.Clear(); last_index = -1; } /// <summary> /// 每个字符判断, 处理完成,返回true, buf全部清除 /// </summary> /// <param name="buf"></param> /// <param name="startIndex"></param> /// <param name="len"></param> /// <returns></returns> bool Modbus_SingleRec(IEnumerable<byte> buf, int index) { if (index == 0) { if (buf.ElementAt(index) != (byte)':') { //包头错,可以删除 return true; } this.status = ModbusAsciiStatus.MBA_STATUS_0A; } else { if (index >= 3) { if (buf.ElementAt(index) == 0x0A) { if (buf.ElementAt(index - 1) == 0x0D) { //可以解析了 Modbus_Handle(buf, 0, index+1); this.status = ModbusAsciiStatus.MBA_STATUS_3A; return true; } else { //结尾出错 return true; } } } } return false; } void Modbus_IntervalCb() { if (this.status == ModbusAsciiStatus.MBA_STATUS_3A) return; //长时间没收到更多数据,清除数据缓存 this.status = ModbusAsciiStatus.MBA_STATUS_3A; ClearRecMsg(); } void Modbus_Handle(IEnumerable<byte> buf, int startIndex, int len) { //0x3A(':') //address :2bytes ascii //function :2bytes ascii //data :2*n bytes ascii //LRC :2bytes ascii //0x0D 0X0A // //DEBUG_printf("Modbus_Check_RTU(buf,%d)\r\n",len); //for(i=0;i<len;i++) // DEBUG_printf("0x%x ",buf[i]); //DEBUG_printf("\r\n"); if (len < 11)//最短是11个bytes return; if (len % 2 != 1)//必须是奇数 return; int adu_len = (len - 1 - 2 - 2) / 2; //ascii 转为 hex List<byte> midBuf = new List<byte>(); int index = 1; byte? b; for (int i = 0; i < adu_len; i++) { b = buf.Ascii2Hex(startIndex + index); if (b == null) { Modbus_DefaultCallback(midBuf, 0, MB_ERR_FORMAT);//指令异常 return ; } midBuf.Add((byte)b); index += 2; } //# ifndef MB_DEBUG_NO_LRC b= buf.Ascii2Hex(startIndex + index); if (b == null) { Modbus_DefaultCallback(midBuf, 0, MB_ERR_FORMAT);//指令异常 return; } byte lrc = (byte)b; byte lrcCheck = midBuf.LRC8(0, midBuf.Count()); if (lrc != lrcCheck) { Modbus_DefaultCallback(midBuf, 0, MB_ERR_LRC);//指令异常 return; } //#endif byte func = midBuf[1]; switch (func) { //case 0x01: // Modbus_Func01(buf, adu_len); // break; case 0x03: Modbus_Func03(midBuf, 0, adu_len); break; //case 0x05: // Modbus_Func05(buf, adu_len); // break; case 0x06: Modbus_Func06(midBuf, 0, adu_len); break; //case 0x0F: // Modbus_Func0F(buf, adu_len); // break; case 0x10: Modbus_Func10(midBuf, 0, adu_len); break; default: Modbus_DefaultCallback(midBuf, 0, MB_ERR_NOFUNC);//没此func break; } } void Modbus_AddLrcAndSend(IEnumerable<byte> buf) { int startIndex = 0; int len = buf.Count(); //添加 : , LRC, 0x0D 0x0A byte lrc = buf.LRC8(startIndex, len); List<byte> sendBuf = new List<byte>(); sendBuf.Add((byte)':'); sendBuf.AddRange(buf.HexToAscii(startIndex, len)); sendBuf.AddRange(lrc.HexToAscii()); sendBuf.Add(0x0D); sendBuf.Add(0x0A); sendMsg?.Invoke(sendBuf); } } }