天津康师傅调度系统
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

501 lines
17 KiB

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
/// <summary>
/// 返回 FINS 握手需要的数据
/// </summary>
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;
}
/// <summary>
/// 读取 DM 内存时所需的信息。由上层定时调用
/// </summary>
/// <param name="startAdress">要读取的起始地址</param>
/// <param name="lenth">要读的长度</param>
/// <param name="isbit">是否按位读取</param>
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;
}
}
/// <summary>
/// 写入 DM 内存时所需的信息。由上层定时调用.(lenth好像不太对,目前只保证写16位的变量时没问题)
/// </summary>
/// <param name="startAdress">要写入的起始地址</param>
/// <param name="lenth">要写的长度</param>
/// <param name="data">要写数据</param>
/// <param name="isbit">是否按位写入</param>
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; //理论上不会报错
}
}
/// <summary>
/// 地址格式转换.PLC的内存地址(区域.地址.字长度 如:WR.10.4)
/// </summary>
/// <param name="memory">内存区域</param>
/// <param name="startaddr">起始地址</param>
/// <param name="len">读写长度</param>
/// <param name="isBit">数据类型为位</param>
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<byte> 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<byte> 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;
}
}
}
}