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() { }
|
|
/// <summary>
|
/// 获取帧结构
|
/// </summary>
|
/// <param name="dNA">目标(PLC)网络号</param>
|
/// <param name="dA1">目标(PLC)节点</param>
|
/// <param name="dA2">目标(PLC)单元号</param>
|
/// <param name="sNA">源(PC)网络号</param>
|
/// <param name="sA1">源(PC)节点</param>
|
/// <param name="sA2">源(PC)单元号</param>
|
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<byte> textRange = BytesFrame(sid);
|
textRange.AddRange(GetTextRange(item));
|
|
return GetFrames(textRange);
|
}
|
|
private byte[] GetFrames(List<byte> textRange)
|
{
|
if (IsUdp)
|
{
|
return textRange.ToArray();
|
}
|
else
|
{
|
List<byte> data = new List<byte>()
|
{
|
0x46,0x49,0x4E,0x53,
|
0x00,0x00,
|
};
|
|
int length = 4 + 4 + textRange.Count;
|
|
data.AddRange(length.IntToBytes());
|
|
data.AddRange(new List<byte>()
|
{
|
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<byte> 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<byte> 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<string, string> oldItemValue = new Dictionary<string, string>();
|
//ConcurrentDictionary<string, string> oldItemValue = new ConcurrentDictionary<string, string>();
|
|
/// <summary>
|
///
|
/// </summary>
|
/// <param name="bytes"></param>
|
/// <param name="item"></param>
|
/// <returns>true:数值有变化 false:数值无变化</returns>
|
//public bool CheckReceivedItems(byte[] bytes, PLC_ITEM item)
|
//{
|
// if (bytes == null || bytes.Length == 0 || bytes.All(b => b == 0))
|
// return false;
|
|
// List<byte> byteList = bytes.ToList();
|
|
// //List<int> beginIndexList = new List<int>();
|
// 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<byte> singleBytes;
|
// // if (takeCount == 0)
|
// // {
|
// // singleBytes = bytes.Skip(beginIndexList[i]).ToList();
|
// // }
|
// // else
|
// // {
|
// // singleBytes = bytes.Skip(beginIndexList[i]).Take(takeCount).ToList();
|
// // }
|
// //}
|
|
// List<byte> 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<int> AnalyseReceivedItems(byte[] bytes, PLC_ITEM item)
|
{
|
if (!IsUdp)
|
{
|
List<byte> errorCode = bytes.Skip(4 + 4 + 4).Take(4).ToList();
|
if (errorCode.Any(u => u != 0x00))
|
{
|
throw new ProcessException($"返回异常,错误码{string.Join("", errorCode)}");
|
}
|
|
bytes = bytes.Skip(4 + 4 + 4 + 4).ToArray();
|
}
|
|
List<int> list = new List<int>();
|
|
if (bytes == null || bytes.Length == 0 || bytes.All(b => b == 0))
|
return null;
|
|
List<byte> 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通讯帧错误");
|
}
|
|
List<byte> 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<byte> GetTextRange(PLC_ITEM item)
|
{
|
List<byte> textRange = new List<byte>();
|
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监听地址设置错误");
|
}
|
|
//字地址
|
//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;
|
}
|
|
/// <summary>
|
/// 将数字转换为字节数组
|
/// </summary>
|
/// <param name="sourceInt">需要转换的数字</param>
|
/// <param name="dataLength">转换数据长度。 如果是位操作 结果长度和数据长度一致;如果是字操作 结果长度是数据长度两倍</param>
|
/// <param name="bitLength">1:表示是位操作 2:表示是字操作</param>
|
/// <returns></returns>
|
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);
|
}
|
}
|
}
|
}
|