using System;
|
using System.Collections.Generic;
|
using System.Linq;
|
using System.Text;
|
using System.Threading.Tasks;
|
using Bro.Common.Helper;
|
using System.Reflection;
|
using static Bro.Common.Helper.EnumHelper;
|
using Bro.Common.Model;
|
|
namespace SchneiderPLC_ModbusTcp
|
{
|
#region DataFrame
|
public interface IModbusDataFrame
|
{
|
ModbusOrder Order { get; set; }
|
|
int Address { get; set; }
|
int Length { get; set; }
|
List<int> 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<int> sendValue;
|
public List<int> 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<int> 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<FrameAttribue>();
|
if (attr != null)
|
{
|
Frame = attr.GetSendFrame(Address, Length, OpValue);
|
}
|
}
|
}
|
|
public string GetFrameString()
|
{
|
return string.Format("Order:{0};Addr:{1};Length:{2},Data:{3}", Order.ToString(), Address, Length, string.Join(",", OpValue ?? new List<int>()));
|
}
|
}
|
|
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<int> opValue;
|
public List<int> 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<FrameAttribue>();
|
if (attr != null)
|
{
|
List<int> valueList = new List<int>();
|
int lengthResult = 0;
|
int addressResult = 0;
|
|
bool flag = attr.AnalyzeReceiveFrame(Frame, out valueList, out addressResult, out 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("Order:{0};Addr:{1};Length:{2},Data:{3}", Order.ToString(), Address, Length, string.Join(",", OpValue ?? new List<int>()));
|
}
|
}
|
#endregion
|
|
#region Frame
|
public interface IModbusFrame
|
{
|
#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 : IModbusFrame
|
{
|
#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<byte> all = new List<byte>();
|
|
#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() { }
|
|
public ModbusFrame_Send(ModbusOrder _order, int _address, int _length, List<int> valueList, int _headId = 1, int _unitNum = 1)
|
{
|
Head = _headId;
|
UnitNum = _unitNum;
|
|
BodyFrame = new ModbusDataFrame_Send(_order, _address, _length, valueList);
|
}
|
|
public ModbusFrame_Send(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<int> valueList = new List<int>();
|
|
//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:{0}\r\n", string.Join(" ", Array.ConvertAll(Frame, b => b.ToString("X2"))));
|
desc += string.Format("DataFrame:{0}\r\n", BodyFrame?.GetFrameString());
|
return desc;
|
}
|
}
|
|
public class ModbusFrame_Receive : IModbusFrame
|
{
|
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() { }
|
|
public ModbusFrame_Receive(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:{0}\r\n", string.Join(" ", Array.ConvertAll(Frame, b => b.ToString("X2"))));
|
desc += string.Format("DataFrame:{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,
|
//[FrameAttribue(0x17, true, false, true)]
|
//RW_MultipleRegister = 0x17,
|
}
|
|
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<int> valueList)
|
{
|
List<byte> all = new List<byte>();
|
all.Add(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
|
{
|
//int divide = valueList.Count / 8;
|
//int remainder = length % 8;
|
//if (remainder > 0)
|
//{
|
// divide++;
|
//}
|
//all.AddRange(divide.IntToBytes(1));
|
////all.AddRange(valueList.IntToBytes(divide));
|
|
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<int> opValue, out int address, out int length)
|
{
|
opValue = new List<int>();
|
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 SendFrameAttribue : FrameAttribue
|
//{
|
// public SendFrameAttribue(byte code, bool isCoil, bool isMultiple, bool isRead = true) : base(code, isCoil, isMultiple, isRead)
|
// {
|
// }
|
|
// public byte[] GetSendFrame(int address, int length, List<int> valueList)
|
// {
|
// List<byte> all = new List<byte>();
|
// all.Add(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
|
// {
|
// //int divide = valueList.Count / 8;
|
// //int remainder = length % 8;
|
// //if (remainder > 0)
|
// //{
|
// // divide++;
|
// //}
|
// //all.AddRange(divide.IntToBytes(1));
|
// ////all.AddRange(valueList.IntToBytes(divide));
|
|
// all.AddRange(valueList.Count.IntToBytes(1));//寄存器数量
|
|
// 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 class ReceiveFrameAttribue : FrameAttribue
|
//{
|
// public ReceiveFrameAttribue(byte code, bool isCoil, bool isMultiple, bool isRead = true) : base(code, isCoil, isMultiple, isRead)
|
// {
|
// }
|
|
// public bool GetReceiveFrame(byte[] data, out List<int> readValue, out int address, out int writeLength, out int writeValue)
|
// {
|
// readValue = new List<int>();
|
// address = -1;
|
// writeLength = 1;
|
// writeValue = -1;
|
|
// if (data[0] != Code)
|
// {
|
// return false;
|
// }
|
|
// if (IsRead)
|
// {
|
// int dataLength = data[1];
|
// if (IsCoil)
|
// {
|
// for (int i = 0; i < dataLength; i++)
|
// {
|
// int index = i + 2;
|
|
// if (index > data.Length)
|
// {
|
// return false;
|
// }
|
|
// int value8 = data[index];
|
|
// for (int j = 0; j < 8; j++)
|
// {
|
// readValue.Add((value8 >> j) & 0x01);
|
// }
|
// }
|
// }
|
// else
|
// {
|
// for (int i = 0; i < dataLength; i++)
|
// {
|
// byte[] res = new byte[2];
|
// Array.Copy(data, i * 2 + 2, res, 0, 2);
|
|
// readValue.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);
|
|
// writeLength = writeLengthBytes.BytesToInt();
|
// }
|
// else
|
// {
|
// byte[] writeValueBytes = new byte[2];
|
// Array.Copy(data, 3, writeValueBytes, 0, 2);
|
|
// writeValue = writeValueBytes.BytesToInt();
|
// }
|
// }
|
|
// return true;
|
// }
|
//}
|
}
|