using Bro.Common.Helper; using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using static Bro.Common.Helper.EnumHelper; namespace Bro.Common.Model { #region DataFrame public interface IModbusDataFrame { ModbusOrder Order { get; set; } int Address { get; set; } int Length { get; set; } List OpValue { get; set; } byte[] Frame { get; set; } string GetFrameString(); } public class ModbusDataFrame_Send : IModbusDataFrame { public ModbusOrder Order { get; set; } private int address = -1; public int Address { get { return address; } set { if (value != address) { address = value; SetFrame(); } } } private int length = -1; public int Length { get { return length; } set { if (value != length) { length = value; SetFrame(); } } } private List sendValue; public List OpValue { get { return sendValue; } set { if (value != sendValue) { sendValue = value; SetFrame(); } } } public byte[] Frame { get; set; } public ModbusDataFrame_Send() { } public ModbusDataFrame_Send(ModbusOrder order, int address, int length, List value) { Order = order; Address = address; Length = length; OpValue = value; } public void SetFrame(bool enforce = false) { if (enforce || (Address != -1 && Length != -1 && OpValue != null)) { FrameAttribue attr = Order.GetType().GetField(Order.ToString()).GetCustomAttribute(); if (attr != null) { Frame = attr.GetSendFrame(Address, Length, OpValue); } } } public string GetFrameString() { return string.Format("{0}\tAddr:{1}\tLength:{2}\tData:{3}", Order.ToString(), Address, Length, string.Join(",", OpValue ?? new List())); } } public class ModbusDataFrame_Receive : IModbusDataFrame { //private int address = -1; public int Address { get; set; } //{ // get // { // if (address == -1) // { // byte[] addressBytes = new byte[2]; // Array.Copy(Frame, 1, addressBytes, 0, 2); // address = addressBytes.BytesToInt(); // } // return address; // } // set // { // //throw new NotImplementedException(); // } //} private byte[] frame = null; public byte[] Frame { get { return frame; } set { if (value != null) { frame = value; AnalyzeReceivedData(); } } } //private int length = 0; public int Length { get; set; } //{ // get // { // if (length == 0) // { // } // return length; // } // set // { // //throw new NotImplementedException(); // } //} //private ModbusOrder order = ModbusOrder.Read_Coil; public ModbusOrder Order { get; set; } //private List opValue; public List OpValue { get; set; } //{ // get // { // if (opValue == -1) // { // } // return opValue; // } // set // { // //throw new NotImplementedException(); // } //} public ModbusDataFrame_Receive() { } public ModbusDataFrame_Receive(byte[] datas) { Frame = datas; } public void AnalyzeReceivedData() { Order = (ModbusOrder)Frame[0]; FrameAttribue attr = Order.GetType().GetField(Order.ToString()).GetCustomAttribute(); if (attr != null) { bool flag = attr.AnalyzeReceiveFrame(Frame, out List valueList, out int addressResult, out int lengthResult); if (!flag) { //throw new Common.Helper.UserException("Modbus返回数据解析失败"); throw new Exception("Modbus返回数据解析失败"); } OpValue = valueList; Address = addressResult; Length = lengthResult; } else { //throw new Common.Helper.UserException("Modbus返回数据不在解析范围中"); throw new Exception("Modbus返回数据不在解析范围中"); } } public string GetFrameString() { return string.Format("{0}\tAddr:{1}\tLength:{2}\tData:{3}", Order.ToString(), Address, Length, string.Join(",", OpValue ?? new List())); } } #endregion #region Tcp Frame public interface IModbusFrame_TCP { #region MBAP Head byte[] ModbusFlag { get; set; } int Head { get; set; } int Length { get; set; } int UnitNum { get; set; } #endregion #region Modbus Body IModbusDataFrame BodyFrame { get; set; } #endregion byte[] Frame { get; set; } string GetFrameString(); } public class ModbusFrame_Send_TCP : IModbusFrame_TCP { #region MBAP Head public byte[] ModbusFlag { get; set; } = { 0x00, 0x00 }; private int head = 0; public int Head { get { return head; } set { if (head != value) { head = value; SetFrame(); } } } private int length = 0; public int Length { get { return length; } set { if (length != value) { length = value; SetFrame(); } } } private int unitNum = 1; public int UnitNum { get { return unitNum; } set { if (unitNum != value) { unitNum = value; SetFrame(); } } } #endregion #region Modbus Body private IModbusDataFrame bodyFrame = null; public IModbusDataFrame BodyFrame { get { return bodyFrame; } set { if (bodyFrame != value) { bodyFrame = value; SetFrame(); } } } #endregion public byte[] Frame { get; set; } public void SetFrame() { if (BodyFrame == null || BodyFrame.Frame == null || BodyFrame.Frame.Length == 0) { return; } List all = new List(); #region MBAP all.AddRange(Head.IntToBytes()); all.AddRange(ModbusFlag); all.AddRange(new byte[] { 0x00, 0x00 }); //预设长度 all.AddRange(UnitNum.IntToBytes(1)); #endregion all.AddRange(BodyFrame.Frame); Length = BodyFrame.Frame.Length + 1; byte[] lengthBytes = Length.IntToBytes(); all[2 + 2 + 0] = lengthBytes[0]; all[2 + 2 + 1] = lengthBytes[1]; Frame = all.ToArray(); } public ModbusFrame_Send_TCP() { } public ModbusFrame_Send_TCP(ModbusOrder _order, int _address, int _length, List valueList, int _headId = 1, int _unitNum = 1) { Head = _headId; UnitNum = _unitNum; BodyFrame = new ModbusDataFrame_Send(_order, _address, _length, valueList); } public ModbusFrame_Send_TCP(PLCItem item, int _unitNum = 1, int _headId = 1) { ModbusOrder order = ModbusOrder.Read_Coil; if (((int)item.PLCOpType & (int)PLCOpType.Read) == (int)PLCOpType.Read) { switch (item.Address[0]) { case '0': order = ModbusOrder.Read_Coil; break; case '1': order = ModbusOrder.Read_Input; break; case '3': order = ModbusOrder.Read_InputRegister; break; case '4': order = ModbusOrder.Read_HoldingRegister; break; } } else { switch (item.Address[0]) { case '1': if (item.ItemLength > 1) { order = ModbusOrder.Write_MultipleOutputs; } else { order = ModbusOrder.Write_SingleOutput; } break; case '4': if (item.ItemLength > 1) { order = ModbusOrder.Write_MultipleRegister; } else { order = ModbusOrder.Write_SingleRegister; } break; } } //int address = Convert.ToInt16(item.ADDRESS.Substring(1), 16); int address = int.Parse(item.Address.Substring(1)); //address = address - 1; //PLC地址比Modbus地址少一位。例如PLC40001读取Modbus40000 Head = _headId; UnitNum = _unitNum; //List valueList = new List(); //if (!string.IsNullOrWhiteSpace(item.ItemValues)) //{ // valueList = item.ItemValues.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList().ConvertAll(s => int.Parse(s)).ToList(); //} BodyFrame = new ModbusDataFrame_Send(order, address, item.ItemLength, item.ItemValues); } public string GetFrameString() { string desc = string.Format("Send\t{0}\r\n", string.Join(" ", Array.ConvertAll(Frame, b => b.ToString("X2")))); desc += string.Format("{0}\r\n", BodyFrame?.GetFrameString()); return desc; } } public class ModbusFrame_Receive_TCP : IModbusFrame_TCP { private byte[] frame = null; public byte[] Frame { get { return frame; } set { if (value != null) { frame = value; AnalyzeFrame(); } } } //private int head = 0; public int Head { get; set; } //{ // get // { // if (head == 0) // { // byte[] headBytes = new byte[2]; // Array.Copy(Frame, 0, headBytes, 0, 2); // head = headBytes.BytesToInt(); // } // return head; // } // set // { // //throw new NotImplementedException(); // } //} //private int length = 0; public int Length { get; set; } //{ // get // { // if (length == 0) // { // byte[] lengthBytes = new byte[2]; // Array.Copy(Frame, 4, lengthBytes, 0, 2); // length = lengthBytes.BytesToInt(); // } // return length; // } // set // { // //throw new NotImplementedException(); // } //} public byte[] ModbusFlag { get; set; } = { 0x00, 0x00 }; //private int unitNum = -1; public int UnitNum { get; set; } //{ // get // { // if (unitNum == -1) // { // unitNum = Frame[6]; // } // return unitNum; // } // set // { // //throw new NotImplementedException(); // } //} //private IModbusDataFrame bodyFrame = null; public IModbusDataFrame BodyFrame { get; set; } public ModbusFrame_Receive_TCP() { } public ModbusFrame_Receive_TCP(byte[] datas) { Frame = datas; } public void AnalyzeFrame() { byte[] headBytes = new byte[2]; Array.Copy(Frame, 0, headBytes, 0, 2); Head = headBytes.BytesToInt(); byte[] lengthBytes = new byte[2]; Array.Copy(Frame, 4, lengthBytes, 0, 2); Length = lengthBytes.BytesToInt(); UnitNum = Frame[6]; BodyFrame = new ModbusDataFrame_Receive(Frame.Skip(2 + 2 + 2 + 1).ToArray()); } public string GetFrameString() { string desc = string.Format("Rec\t{0}\r\n", string.Join(" ", Array.ConvertAll(Frame, b => b.ToString("X2")))); desc += string.Format("{0}\r\n", BodyFrame?.GetFrameString()); return desc; } } #endregion #region RTU Frame public interface IModbusFrame_RTU { int UnitNum { get; set; } IModbusDataFrame BodyFrame { get; set; } byte[] CRCBytes { get; set; } byte[] Frame { get; set; } string GetFrameString(); } public class ModbusFrame_Send_RTU : IModbusFrame_RTU { private int unitNum = 1; public int UnitNum { get { return unitNum; } set { if (unitNum != value) { unitNum = value; SetFrame(); } } } private IModbusDataFrame bodyFrame = null; public IModbusDataFrame BodyFrame { get { return bodyFrame; } set { if (bodyFrame != value) { bodyFrame = value; SetFrame(); } } } public byte[] CRCBytes { get; set; } public byte[] Frame { get; set; } public void SetFrame() { if (BodyFrame == null || BodyFrame.Frame == null || BodyFrame.Frame.Length == 0) { return; } List all = new List(); all.AddRange(UnitNum.IntToBytes(1)); all.AddRange(BodyFrame.Frame); byte[] temp = all.ToArray(); CRCBytes = CRCHelper.Crc16InBytes(temp, temp.Length); all.AddRange(CRCBytes); Frame = all.ToArray(); } public ModbusFrame_Send_RTU() { } public ModbusFrame_Send_RTU(ModbusOrder _order, int _address, int _length, List valueList, int _unitNum = 1) { UnitNum = _unitNum; BodyFrame = new ModbusDataFrame_Send(_order, _address, _length, valueList); } public string GetFrameString() { string desc = string.Format("Send\t{0}\r\n", string.Join(" ", Array.ConvertAll(Frame, b => b.ToString("X2")))); desc += string.Format("{0}\r\n", BodyFrame?.GetFrameString()); return desc; } } public class ModbusFrame_Receive_RTU : IModbusFrame_RTU { private byte[] frame = null; public byte[] Frame { get { return frame; } set { if (value != null) { frame = value; AnalyzeFrame(); } } } public byte[] ModbusFlag { get; set; } = { 0x00, 0x00 }; public int UnitNum { get; set; } public byte[] CRCBytes { get; set; } public IModbusDataFrame BodyFrame { get; set; } public ModbusFrame_Receive_RTU() { } public ModbusFrame_Receive_RTU(byte[] datas) { Frame = datas; } public void AnalyzeFrame() { UnitNum = Frame[0]; CRCBytes = Frame.Skip(Frame.Length - 2).ToArray(); BodyFrame = new ModbusDataFrame_Receive(Frame.Skip(1).Take(Frame.Length - 1 - 2).ToArray()); } public string GetFrameString() { string desc = string.Format("Rec\t{0}\r\n", string.Join(" ", Array.ConvertAll(Frame, b => b.ToString("X2")))); desc += string.Format("{0}\r\n", BodyFrame?.GetFrameString()); return desc; } } #endregion public enum ModbusOrder { [FrameAttribue(0x01, true, false, true)] Read_Coil = 0x01, [FrameAttribue(0x02, false, true, true)] Read_Input = 0x02, [FrameAttribue(0x03, false, true, true)] Read_HoldingRegister = 0x03, [FrameAttribue(0x04, false, true, true)] Read_InputRegister = 0x04, [FrameAttribue(0x05, true, false, false)] Write_SingleOutput = 0x05, [FrameAttribue(0x06, false, false, false)] Write_SingleRegister = 0x06, [FrameAttribue(0x0f, true, true, false)] Write_MultipleOutputs = 0x0f, [FrameAttribue(0x10, false, true, false)] Write_MultipleRegister = 0x10, } public class FrameAttribue : Attribute { public byte Code { get; set; } public bool IsRead { get; set; } public bool IsMultiple { get; set; } public bool IsCoil { get; set; } public FrameAttribue(byte code, bool isCoil, bool isMultiple, bool isRead = true) { Code = code; IsRead = isRead; IsMultiple = isMultiple; IsCoil = isCoil; } public byte[] GetSendFrame(int address, int length, List valueList) { List all = new List { Code }; all.AddRange(address.IntToBytes()); if (IsRead) { all.AddRange(length.IntToBytes()); } else { if (!IsMultiple) { if (IsCoil) { all.AddRange(valueList[0] == 1 ? new byte[] { 0xff, 0x00 } : new byte[] { 0x00, 0x00 }); } else { all.AddRange(valueList[0].IntToBytes()); } } else { all.AddRange(valueList.Count.IntToBytes(2));//寄存器数量 if (IsCoil) { int divide = valueList.Count / 8; int remainder = length % 8; if (remainder > 0) { divide++; } all.AddRange(divide.IntToBytes(1)); //字节长度 for (int i = 0; i < divide; i++) { string s = ""; for (int j = 0; j < 8; j++) { int index = j + i * 8; if (valueList.Count > index) { s = valueList[index].ToString() + s; } else { s = "0" + s; } } all.Add((byte)Convert.ToInt16(s, 2)); } } else { all.Add((byte)(valueList.Count * 2));//字节长度 valueList.ForEach(u => { all.AddRange(u.IntToBytes()); }); } } } return all.ToArray(); } public bool AnalyzeReceiveFrame(byte[] data, out List opValue, out int address, out int length) { opValue = new List(); address = -1; length = 0; if (IsRead) { if (IsCoil) { length = data[1]; for (int i = 0; i < length; i++) { int index = i + 2; //头两位是Code和Length if (index > data.Length) { return false; } int value8 = data[index]; for (int j = 0; j < 8; j++) { opValue.Add((value8 >> j) & 0x01); } } } else { length = data[1] / 2; for (int i = 0; i < length; i++) { byte[] res = new byte[2]; Array.Copy(data, i * 2 + 2, res, 0, 2); opValue.Add(res.BytesToInt()); } } } else { if (data.Length < 5) { return false; } byte[] addbytes = new byte[2]; Array.Copy(data, 1, addbytes, 0, 2); address = addbytes.BytesToInt(); if (IsMultiple) { byte[] writeLengthBytes = new byte[2]; Array.Copy(data, 3, writeLengthBytes, 0, 2); length = writeLengthBytes.BytesToInt(); } else { byte[] writeValueBytes = new byte[2]; Array.Copy(data, 3, writeValueBytes, 0, 2); opValue.Add(writeValueBytes.BytesToInt()); } } return true; } } public class CRCHelper { private static readonly byte[] aucCRCHi = { 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 }; private static readonly byte[] aucCRCLo = { 0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3, 0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26, 0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5, 0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C, 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C, 0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83, 0x41, 0x81, 0x80, 0x40 }; /// /// CRC效验 /// /// 效验数据 /// 数据长度 /// 效验结果 public static int Crc16(byte[] pucFrame, int usLen) { int i = 0; byte ucCRCHi = 0xFF; byte ucCRCLo = 0xFF; while (usLen-- > 0) { ushort iIndex = (UInt16)(ucCRCLo ^ pucFrame[i++]); ucCRCLo = (byte)(ucCRCHi ^ aucCRCHi[iIndex]); ucCRCHi = aucCRCLo[iIndex]; } return (ucCRCHi << 8 | ucCRCLo); } public static byte[] Crc16InBytes(byte[] pucFrame, int usLen) { int i = 0; byte ucCRCHi = 0xFF; byte ucCRCLo = 0xFF; while (usLen-- > 0) { ushort iIndex = (UInt16)(ucCRCLo ^ pucFrame[i++]); ucCRCLo = (byte)(ucCRCHi ^ aucCRCHi[iIndex]); ucCRCHi = aucCRCLo[iIndex]; } return new byte[2] { ucCRCLo, ucCRCHi }; } } }