using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace FINSTCPIP { //用来解析FINS包的数据和将数据打包 public class FINSParse { public FINSParse () { _socketConnected = false; _finsConnected = false; _ifIniting = true; } readonly byte[] HEADER_FINS = new byte[4] { FINS_F, FINS_I, FINS_N, FINS_S }; //FINS 的 ASCII public byte[] _clientaddr = new byte[4]; //客户端IP第四节(程序) public byte[] _serveraddr = new byte[4]; //服务端IP第四节(PLC) public bool _socketConnected; //socket连接 public bool _finsConnected; //FINS连接 public bool _ifIniting; //正在初始化 public int connectTimes; string _errname = string.Empty; public string CommLayerError { get { return _errname; } set { _errname = value; } } public const byte SID = 255; #region 『FINS/TCP Header 』 // Header public const byte FINS_F = 0x46; public const byte FINS_I = 0x49; public const byte FINS_N = 0x4e; public const byte FINS_S = 0x53; // Length 报文中的字段长度,从 Command 开始,到报文结束(不包括Header的长度和Length的长度) public const byte SHAKEHAND_SEND_LENGTH = 12; //FINS 握手请求报文中 Length 的值 public const byte SHAKEHAND_RECV_LENGTH = 16; //FINS 握手回复报文中 Length 的值 public const byte FRAM_NODATA_SEND_LENGTH = 26; //FINS 发送读报文中无数据的 Length 的值 public const byte FRAM_NODATA_RECV_LENGTH = 22; //FINS 收到读报文中无数据的 Length 的值 // Command public const byte CLIENT_SERVER = 0; public const byte SERVER_CLIENT = 1; public const byte FINS_FRAME = 2; public const byte ERROR_FRAME = 3; public const byte CONNECT_RIGHT = 6; // ErrorCode // 0 正常 // 01 Header不正确 // 02 数据过长 // 03 命令Command错误 // 20 连接/通道被占用 #endregion /// /// 返回 FINS 握手需要的数据 /// public byte[] GetConnectInfo(byte clientAddr = 0) { byte[] SendByte = new byte[8 + SHAKEHAND_SEND_LENGTH]; SendByte[0] = HEADER_FINS[0]; //header SendByte[1] = HEADER_FINS[1]; SendByte[2] = HEADER_FINS[2]; SendByte[3] = HEADER_FINS[3]; SendByte[4] = 0; //cmd length SendByte[5] = 0; SendByte[6] = 0; SendByte[7] = SHAKEHAND_SEND_LENGTH; SendByte[8] = 0; //frame command SendByte[9] = 0; SendByte[10] = 0; SendByte[11] = CLIENT_SERVER; SendByte[12] = 0;//err SendByte[13] = 0; SendByte[14] = 0; SendByte[15] = 0; SendByte[16] = 0;//FINS Frame (本机节点)(0为自动,PLC返回) SendByte[17] = 0; SendByte[18] = 0; SendByte[19] = clientAddr; return SendByte; } /// /// 读取 DM 内存时所需的信息。由上层定时调用 /// /// 要读取的起始地址 /// 要读的长度 /// 是否按位读取 public byte[] GetInfo_DMRead(ushort startAdress, ushort lenth, bool isbit = false) { try { byte[] SendByte = new byte[FRAM_NODATA_SEND_LENGTH + 8]; SendByte[0] = HEADER_FINS[0]; //header SendByte[1] = HEADER_FINS[1]; SendByte[2] = HEADER_FINS[2]; SendByte[3] = HEADER_FINS[3]; SendByte[4] = 0; //cmd length SendByte[5] = 0; SendByte[6] = 0; SendByte[7] = FRAM_NODATA_SEND_LENGTH; SendByte[8] = 0;//frame command SendByte[9] = 0; SendByte[10] = 0; SendByte[11] = FINS_FRAME; SendByte[12] = 0;//err SendByte[13] = 0; SendByte[14] = 0; SendByte[15] = 0; //command frame header SendByte[16] = 0x80;//ICF SendByte[17] = 0x00;//RSV SendByte[18] = 0x02;//GCT, less than 8 network layers SendByte[19] = 0x00;//DNA, local network SendByte[20] = _serveraddr[3];//DA1 SendByte[21] = 0x00;//DA2, CPU unit SendByte[22] = 0x00;//SNA, local network SendByte[23] = _clientaddr[3];//SA1 SendByte[24] = 0x00;//SA2, CPU unit SendByte[25] = Convert.ToByte(SID);//SID SendByte[26] = 0x01; //0101 Read SendByte[27] = 0x01; byte[] head = AddrToByte("DM", startAdress, lenth, isbit); SendByte[28] = head[0]; SendByte[29] = head[1]; SendByte[30] = head[2]; SendByte[31] = head[3]; SendByte[32] = head[4]; SendByte[33] = head[5]; return SendByte; } catch (Exception ex) { throw ex; } } /// /// 写入 DM 内存时所需的信息。由上层定时调用.(lenth好像不太对,目前只保证写16位的变量时没问题) /// /// 要写入的起始地址 /// 要写的长度 /// 要写数据 /// 是否按位写入 public byte[] GetInfo_DMWrite(ushort startAdress, ushort lenth, byte[] data, bool isbit = false) { try { byte[] SendByte = new byte[8 + data.Length + FRAM_NODATA_SEND_LENGTH]; SendByte[0] = HEADER_FINS[0]; SendByte[1] = HEADER_FINS[1]; SendByte[2] = HEADER_FINS[2]; SendByte[3] = HEADER_FINS[3]; SendByte[4] = 0;//cmd length SendByte[5] = 0; SendByte[6] = 0; SendByte[7] = Convert.ToByte(FRAM_NODATA_SEND_LENGTH + data.Length); SendByte[8] = 0;//frame command SendByte[9] = 0; SendByte[10] = 0; SendByte[11] = FINS_FRAME; SendByte[12] = 0;//err SendByte[13] = 0; SendByte[14] = 0; SendByte[15] = 0; //command frame header SendByte[16] = 0x80;//ICF SendByte[17] = 0x00;//RSV SendByte[18] = 0x02;//GCT, less than 8 network layers SendByte[19] = 0x00;//DNA, local network SendByte[20] = _serveraddr[3];//DA1 SendByte[21] = 0x00;//DA2, CPU unit SendByte[22] = 0x00;//SNA, local network SendByte[23] = _clientaddr[3];//SA1 SendByte[24] = 0x00;//SA2, CPU unit SendByte[25] = Convert.ToByte(SID);//SID SendByte[26] = 0x01; //0102 Write SendByte[27] = 0x02; byte[] head = AddrToByte("DM", startAdress, lenth, isbit); SendByte[28] = head[0]; SendByte[29] = head[1]; SendByte[30] = head[2]; SendByte[31] = head[3]; SendByte[32] = head[4]; SendByte[33] = head[5]; Array.Copy(data, 0, SendByte, 34, data.Length); return SendByte; } catch (Exception ex) { throw ex; //理论上不会报错 } } /// /// 地址格式转换.PLC的内存地址(区域.地址.字长度 如:WR.10.4) /// /// 内存区域 /// 起始地址 /// 读写长度 /// 数据类型为位 private byte[] AddrToByte(string memory, int startaddr, int len, bool isBit) { byte[] CH = BitConverter.GetBytes(startaddr); byte[] Count = BitConverter.GetBytes(len); byte[] Addrs = new byte[6]; if (!isBit) //字处理 { switch (memory) { case "CIO": Addrs[0] = 0xB0; break; case "WR": Addrs[0] = 0xB1; break; case "DM": Addrs[0] = 0x82; break; case "HR": Addrs[0] = 0xB2; break; case "TIM": Addrs[0] = 0x89; break; case "AR": Addrs[0] = 0xB3; break; case "CNT": Addrs[0] = 0x89; break; default: Addrs[0] = 0x00; break; } Addrs[1] = CH[1]; Addrs[2] = CH[0]; Addrs[3] = 0x00; Addrs[4] = Count[1]; Addrs[5] = Count[0];//读写字的长度 } else //位处理 { switch (memory) { case "CIO": Addrs[0] = 0x30; break; case "WR": Addrs[0] = 0x31; break; case "DM": Addrs[0] = 0x02; break; case "HR": Addrs[0] = 0x32; break; case "TIM": Addrs[0] = 0x09; break; case "AR": Addrs[0] = 0x33; break; case "CNT": Addrs[0] = 0x09; break; default: Addrs[0] = 0x00; break; } Addrs[1] = CH[1]; Addrs[2] = CH[0]; Addrs[3] = Count[0]; Addrs[4] = 0x00; Addrs[5] = 0x01;//每次读写一位 } return Addrs; } //前四位是 'FINS' public bool IsHeader(byte[] recv) { if (recv.Length < HEADER_FINS.Length) { CommLayerError = "Header is too short, not enough four!"; return false; } if (recv[0] != HEADER_FINS[0] || recv[1] != HEADER_FINS[1] || recv[2] != HEADER_FINS[2] || recv[3] != HEADER_FINS[3]) { CommLayerError = "Header is not 'FINS'!"; return false; } return true; } //根据接收到的包,来判断FINS连接是否成功,并更新_clientaddr和_serveraddr public bool IsConnectRight(List recv) { if (recv.Count < SHAKEHAND_RECV_LENGTH + 8) { CommLayerError = "建立连接时,收到的数据长度不够。"; return false; } int err = 0; if (recv[11] == ERROR_FRAME || recv[14] != 0 || recv[15] != 0) { switch (recv[15]) { case 0x01: err = 2; CommLayerError = "ERROR CODE:2——header is not 'FINS'"; break;//the head is not 'FINS' case 0x02: err = 3; CommLayerError = "ERROR CODE:4——the data length is too long"; break;//the data length is too long case 0x03: err = 4; CommLayerError = "ERROR CODE:8——the command is not supported"; break;//the command is not supported case 0x20: err = 32; CommLayerError = "ERROR CODE:32——PLC 拒绝访问,请重启 PLC!"; break;//the command is not supported default: err = 1; break; } } if (recv[7] == SHAKEHAND_RECV_LENGTH && recv[11] == SERVER_CLIENT && recv[15] == 0) { _clientaddr[0] = recv[16]; _clientaddr[1] = recv[17]; _clientaddr[2] = recv[18]; _clientaddr[3] = recv[19]; _serveraddr[0] = recv[20]; _serveraddr[1] = recv[21]; _serveraddr[2] = recv[22]; _serveraddr[3] = recv[23]; } if (err == 0) { return true; } else { return false; } } //需要返回错误类型(未完成) private bool IsFrameHeaderRight(byte[] recv) { int err = 0; if (recv[0] != 128 && recv[0] != 192) { err = 1;// ICF错误 } if (recv[1] != 0) { err = 1; } if (recv[2] != 2) { err = 1; } if (recv[4] != _clientaddr[3]) { err = 1; } if (recv[7] != _serveraddr[3]) { err = 1; } if (recv[9] != SID) { //err = 1; } if (err == 0) return true; else return false; } // private bool IsFrameCommandRight(byte[] recv) { int err = -1; if (recv[0] == 1 && recv[1] == 1 && recv[2] == 0 && recv[3] == 0) {//读内存 err = 0; } if (recv[0] == 1 && recv[1] == 2 && recv[2] == 0 && recv[3] == 0) {//写内存 err = 0; } if (err == 0) return true; else return false; } // public bool IsRecvRight(List recv) { if (recv.Count < FRAM_NODATA_RECV_LENGTH + 8) { CommLayerError = "数据长度不够。"; return false; } int err = 0; if (recv[11] == ERROR_FRAME) { switch (recv[15]) { case 0x01: err = 2; CommLayerError = "ERROR CODE:2——the head is not 'FINS'。"; break;//the head is not 'FINS' case 0x02: err = 3; CommLayerError = "ERROR CODE:4——the data length is too long。"; break;//the data length is too long case 0x03: err = 4; CommLayerError = "ERROR CODE:8——the command is not supported。"; break;//the command is not supported } } if (recv[7] >= FRAM_NODATA_RECV_LENGTH && recv[11] == FINS_FRAME && recv[15] == 0) { byte[] header = new byte[10]; recv.CopyTo(SHAKEHAND_RECV_LENGTH, header, 0, 10); if (IsFrameHeaderRight(header) == false) { err = 1; CommLayerError = "FrameHeader不正确。"; } byte[] commad = new byte[4]; recv.CopyTo(FRAM_NODATA_SEND_LENGTH, commad, 0, 4); if (IsFrameCommandRight(commad) == false) { err = 1; CommLayerError = "FrameCommand不正确。"; } } if (err == 0) { return true; } else { return false; } } } }