using Bro.Common.Helper; using Bro.Common.Model; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using static Bro.Common.Helper.EnumHelper; namespace Bro.Device.OmronFins { public class FinsFrame : FinsFrameBase { public bool IsUdp { get; set; } = true; public FinsFrame() { } /// /// 获取帧结构 /// /// 目标(PLC)网络号 /// 目标(PLC)节点 /// 目标(PLC)单元号 /// 源(PC)网络号 /// 源(PC)节点 /// 源(PC)单元号 public FinsFrame(byte dNA, byte dA1, byte dA2, byte sNA, byte sA1, byte sA2, bool isUdp = true) { DNA = dNA; DA1 = dA1; DA2 = dA2; SNA = sNA; SA1 = sA1; SA2 = sA2; IsUdp = isUdp; } public byte[] GetSendReadFrameBytes(PLC_ITEM item, int sid) { ICF = 0x80; SRC = 0x01; List textRange = BytesFrame(sid); textRange.AddRange(GetTextRange(item)); return GetFrames(textRange); } private byte[] GetFrames(List textRange) { if (IsUdp) { return textRange.ToArray(); } else { List data = new List() { 0x46,0x49,0x4E,0x53, 0x00,0x00, }; int length = 4 + 4 + textRange.Count; data.AddRange(length.IntToBytes()); data.AddRange(new List() { 0x00,0x00,0x00,0x02, 0x00,0x00,0x00,0x00, }); data.AddRange(textRange); return data.ToArray(); } } public byte[] GetSendWriteFrameBytes(PLC_ITEM item, int sid) { ICF = 0x80; SRC = 0x02; List textRange = BytesFrame(sid); textRange.AddRange(GetTextRange(item)); #region 添加写入内容 byte[] writeData; switch (item.ITEM_VALUE_TYPE) { case (int)PLCItemType.Bool: { writeData = ConvertIntToByte(int.Parse(item.ITEM_VALUE), item.ITEM_LENGTH, 1); textRange.AddRange(writeData); } break; case (int)PLCItemType.Integer: { writeData = ConvertIntToByte(int.Parse(item.ITEM_VALUE), item.ITEM_LENGTH, 2, true); textRange.AddRange(writeData); } break; case (int)PLCItemType.String: { byte[] bs = System.Text.Encoding.Default.GetBytes(item.ITEM_VALUE); writeData = new byte[item.ITEM_LENGTH * 2]; bs.CopyTo(writeData, 0); textRange.AddRange(writeData); } break; default: break; } #endregion return GetFrames(textRange); } public byte[] GetTcpRequestFrame(int nodeNum = 0) { byte[] data = new byte[] { 0x46,0x49,0x4E,0x53, 0x00,0x00,0x00,0x0c, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,(byte)nodeNum }; return data; } public int AnalyseTcpRequestRespond(byte[] data, out string msg) { msg = ""; if (data.Length != 20) { msg = "返回帧长度错误"; return -1; } if (data[0] != 0x46 || data[1] != 0x49 || data[2] != 0x4E || data[3] != 0x53) { msg = "返回帧头标识错误"; return -1; } List errorCode = data.Skip(4 + 4 + 4).Take(4).ToList(); if (errorCode.Any(u => u != 0x00)) { msg = string.Join("", errorCode); return -1; } return data.Skip(4 + 4 + 4 + 4 + 3).Take(1).ElementAt(0); } //Dictionary oldItemValue = new Dictionary(); //ConcurrentDictionary oldItemValue = new ConcurrentDictionary(); /// /// /// /// /// /// true:数值有变化 false:数值无变化 //public bool CheckReceivedItems(byte[] bytes, PLC_ITEM item) //{ // if (bytes == null || bytes.Length == 0 || bytes.All(b => b == 0)) // return false; // List byteList = bytes.ToList(); // //List beginIndexList = new List(); // int beginIndex = -1; // for (int i = 0; i < bytes.Length - 5; i++) // { // if (bytes[i] == 0xC0 // && bytes[i + 1] == 0x00 // && bytes[i + 2] == 0x02 // && bytes[i + 3] == SNA // && bytes[i + 4] == SA1 // && bytes[i + 5] == SA2) // { // //beginIndexList.Add(i); // beginIndex = i; // break; // } // } // //if (beginIndexList.Count < items.Count) // //{ // // throw new UserException("欧姆龙PLC通讯可能出现丢帧现象", LogType.Exception_Error); // //} // if (beginIndex == -1) // { // throw new ProcessException("欧姆龙PLC通讯帧错误", null); // } // //for (int i = 0; i < beginIndexList.Count; i++) // //{ // // int takeCount = 0; // // if (i < beginIndexList.Count - 1) // // { // // takeCount = beginIndexList[i + 1] - beginIndexList[i]; // // } // // List singleBytes; // // if (takeCount == 0) // // { // // singleBytes = bytes.Skip(beginIndexList[i]).ToList(); // // } // // else // // { // // singleBytes = bytes.Skip(beginIndexList[i]).Take(takeCount).ToList(); // // } // //} // List singleBytes = bytes.Skip(beginIndex).ToList(); // item.OpTimeStamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); // #region 如果该帧是写入指令的回复帧 // if (singleBytes[11] == 0x02) // { // //MRES SRES检查 // if (!(singleBytes[12] == 0x00 && singleBytes[13] == 0x00)) // { // //throw new UserException("欧姆龙PLC写入错误", LogType.Exception_Error); // } // return true; // } // #endregion // #region 如果该帧是读取指令的回复帧 // else if (singleBytes[11] == 0x01) // { // byte[] data; // //string oldValue = item.ITEM_VALUE; // //if (!oldItemValue.Keys.Contains(item.ID)) // //{ // // oldItemValue[item.ID] = item.ITEM_VALUE; // //} // oldItemValue.GetOrAdd(item.ID, item.ITEM_VALUE); // string newValue = null; // if (item.ITEM_VALUE_TYPE == (int)PLCItemType.Bool) // { // data = new byte[item.ITEM_LENGTH]; // singleBytes.CopyTo(14, data, 0, data.Length); // newValue = ConvertByteToInt(data, 1).ToString(); // } // else if (item.ITEM_VALUE_TYPE == (int)PLCItemType.Integer) // { // data = new byte[item.ITEM_LENGTH * 2]; // singleBytes.CopyTo(14, data, 0, data.Length); // newValue = ConvertByteToInt(data, 2, true).ToString(); // } // else if (item.ITEM_VALUE_TYPE == (int)PLCItemType.String) // { // data = new byte[item.ITEM_LENGTH * 2]; // singleBytes.CopyTo(14, data, 0, data.Length); // newValue = System.Text.Encoding.Default.GetString(data); // } // if (oldItemValue[item.ID] != newValue) // { // //oldItemValue[item.ID] = item.ITEM_VALUE = newValue; // oldItemValue.AddOrUpdate(item.ID, newValue, (a, b) => { return newValue; }); // item.ITEM_VALUE = newValue; // //Task.Run(() => item.OnPLCItemDataChanged?.Invoke(DateTime.Now, oldValue, newValue)); // return true; // } // //return oldValue != newValue; // } // return false; // #endregion //} public List AnalyseReceivedItems(byte[] bytes, PLC_ITEM item) { if (!IsUdp) { List errorCode = bytes.Skip(4 + 4 + 4).Take(4).ToList(); if (errorCode.Any(u => u != 0x00)) { throw new ProcessException($"返回异常,错误码{string.Join("", errorCode)}", null); } bytes = bytes.Skip(4 + 4 + 4 + 4).ToArray(); } List list = new List(); if (bytes == null || bytes.Length == 0 || bytes.All(b => b == 0)) return null; List byteList = bytes.ToList(); int beginIndex = -1; for (int i = 0; i < bytes.Length - 5; i++) { if (bytes[i] == 0xC0 && bytes[i + 1] == 0x00 && bytes[i + 2] == 0x02 && bytes[i + 3] == SNA && bytes[i + 4] == SA1 && bytes[i + 5] == SA2) { //beginIndexList.Add(i); beginIndex = i; break; } } if (beginIndex == -1) { throw new ProcessException("欧姆龙PLC通讯帧错误", null); } List singleBytes = bytes.Skip(beginIndex).ToList(); #region 如果该帧是写入指令的回复帧 if (singleBytes[11] == 0x02) { //MRES SRES检查 if (!(singleBytes[12] == 0x00 && singleBytes[13] == 0x00)) { //throw new UserException("欧姆龙PLC写入错误", LogType.Exception_Error); } return list; } #endregion #region 如果该帧是读取指令的回复帧 else if (singleBytes[11] == 0x01) { if (item.ITEM_VALUE_TYPE == (int)PLCItemType.Integer) { byte[] data = new byte[item.ITEM_LENGTH * 2]; singleBytes.CopyTo(14, data, 0, data.Length); for (int i = 0; i < data.Length; i += 2) { byte[] res = new byte[2]; Array.Copy(data, i, res, 0, 2); list.Add(res.BytesToInt()); } return list; } else { return null; } } #endregion return null; } protected List GetTextRange(PLC_ITEM item) { List textRange = new List(); int wordAddress = 0; int byteAddress = 0; //针对CJ和CS系列 if (item.ADDRESS.ToUpper().StartsWith("DM") || item.ADDRESS.ToUpper().StartsWith("D")) { //AreaCode = 0x82; textRange.Add(0x82); wordAddress = Convert.ToInt16(item.ADDRESS.Replace("D", "").Replace("M", "")); } else if (item.ADDRESS.ToUpper().StartsWith("W")) { if (item.ITEM_VALUE_TYPE == (int)PLCItemType.Bool) { //AreaCode = 0x31; textRange.Add(0x31); string wholeAddress = item.ADDRESS.Replace("W", ""); int dotIndex = wholeAddress.IndexOf("."); wordAddress = Convert.ToInt16(wholeAddress.Substring(0, dotIndex)); byteAddress = Convert.ToInt16(wholeAddress.Substring(dotIndex + 1)); } else { //AreaCode = 0xB1; textRange.Add(0xB1); wordAddress = Convert.ToInt16(item.ADDRESS.Replace("W", "")); } } else if (item.ADDRESS.ToUpper().StartsWith("CIO")) { //AreaCode = 0x30; textRange.Add(0x30); string wholeAddress = item.ADDRESS.Replace("CIO", ""); int dotIndex = wholeAddress.IndexOf("."); wordAddress = Convert.ToInt16(wholeAddress.Substring(0, dotIndex)); byteAddress = Convert.ToInt16(wholeAddress.Substring(dotIndex + 1)); } if (wordAddress == 0 && byteAddress == 0) { throw new ProcessException("欧姆龙PLC监听地址设置错误", null); } //字地址 //string high2 = wordAddress.ToString("X4").Substring(0, 2); //string low2 = wordAddress.ToString("X4").Substring(2, 2); //textRange.Add((byte)Convert.ToInt16(high2, 16)); //textRange.Add((byte)Convert.ToInt16(low2, 16)); textRange.AddRange(ConvertIntToByte(wordAddress, 1, 2)); //位地址 textRange.Add((byte)byteAddress); //读取项目个数 //int opItemNum = 1; //if (item.ITEM_VALUE_TYPE == (int)PLCItemType.String) //{ // opItemNum = item.ITEM_VALUE.Length; //} //textRange.AddRange(ConvertIntTo2Byte(opItemNum)); textRange.AddRange(ConvertIntToByte(item.ITEM_LENGTH, 1, 2)); return textRange; } /// /// 将数字转换为字节数组 /// /// 需要转换的数字 /// 转换数据长度。 如果是位操作 结果长度和数据长度一致;如果是字操作 结果长度是数据长度两倍 /// 1:表示是位操作 2:表示是字操作 /// protected byte[] ConvertIntToByte(int sourceInt, int dataLength = 1, int bitLength = 1, bool inverseHL = false) { byte[] bs; //位操作 if (bitLength == 1) { bs = new byte[dataLength]; for (int i = 0; i < bs.Length; i++) { bs[i] = (byte)((sourceInt >> ((bs.Length - i - 1) * 2)) & 3); //取末两位 } } else //字操作 { string formatStr = "X" + dataLength * 4; string sourceXStr = sourceInt.ToString(formatStr); bs = new byte[dataLength * 2]; if (inverseHL) { sourceXStr = ReverseStr4(sourceXStr); } for (int i = 0; i < bs.Length; i++) { bs[i] = (byte)Convert.ToInt32(sourceXStr.Substring(i * 2, 2), 16); } } return bs; } private string ReverseStr4(string sourceXStr) { string result = ""; for (int i = 0; i < sourceXStr.Length - 3; i = i + 4) { result = result.Insert(0, sourceXStr.Substring(i, 4)); } return result; } protected int ConvertByteToInt(byte[] bytes, int length = 1, bool inverseHL = false) { if (length == 1) { int result = 0; for (int i = 0; i < bytes.Length; i++) { result += (bytes[i] << ((bytes.Length - i - 1) * 2)); } return result; } else { string xStr = ""; Array.ForEach(bytes, b => { xStr += b.ToString("X2"); }); if (inverseHL) { xStr = ReverseStr4(xStr); } return Convert.ToInt32(xStr, 16); } } } }