using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
//静态类《PLC_FX_编程口协议》,不用实例化
namespace 串口编程
{
static class PLC_FX_编程口协议
{
static PLC_FX_编程口协议()
{
}
static byte[] Buf_PLC_Recive = new byte[1];//PL返回的串口数据
public static int Len_PLC_Recive = 0;//PLC返回的串口数据长度
static byte[] Hex2Asc = new byte[16] { 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0X41, 0X42, 0X43, 0X44, 0X45, 0X46 };
static byte[] Asc2Hex = new byte[23] { 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0, 0, 0, 0, 0, 0, 0, 10, 11, 12, 13, 14, 15 };
static byte[] U8toBin = new byte[8] { 0x01, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80 };
public static string string_Now_Send_PLC_FX_Order = "NULL";//当前发发送给PLC的指令("NULL"表示没有给PLC发送串口指令)
//PLC寄存器
public static Boolean[] PLC_FX_M = new Boolean[1024]; //M0 to M1023
public static Boolean[] PLC_FX_S = new Boolean[1000]; //S0 to S999
public static int[] PLC_FX_D = new int[1024]; //D0 to D1023
public static Boolean[] PLC_FX_Y = new Boolean[16]; //Y0 to Y17
//寄存器地址表
private const int PLC_D_Base_AddRess = 0x1000;
private const int PLC_D_Special_Base_AddRess = 3584;
private const int PLC_Y_Group_Base_AddRess = 0xA0;
private const int PLC_PY_Group_Base_AddRess = 672;
private const int PLC_T_Group_Base_AddRess = 192;//0xC0
private const int PLC_OT_Group_Base_AddRess = 704;
private const int PLC_RT_Group_Base_AddRess = 1216;
private const int PLC_M_SINGLE_Base_AddRess = 2048;//'(命令为7或8时)
private const int PLC_M_Group_Base_AddRess = 256;
private const int PLC_PM_Group_Base_AddRess = 768;
private const int PLC_S_Group_Base_AddRess = 0;
private const int PLC_X_Group_Base_AddRess = 0x80;
private const int PLC_C_Group_Base_AddRess = 448;//0x1C0
private const int PLC_OC_Group_Base_AddRess = 960;
private const int PLC_RC_Group_Base_AddRess = 1472;
private const int PLC_TV_Group_Base_AddRess = 2048;
private const int PLC_CV16_Group_Base_AddRess = 2560;
private const int PLC_CV32_Group_Base_AddRess = 3072;
/*************************************************************************
* 以下程序是根据要求,生成读写PLC的指令
*
* PLC读写指令(共4条):
* 1、读元件,命令码“0”,目标设备:X,Y,M,S,T,C,D。注:最多可读64byte数据
* 2、写元件,命令码“1”,目标设备:X,Y,M,S,T,C,D
* 3、置位 ,命令码“7”,目标设备:X,Y,M,S,T,C
* 4、复位 ,命令码“8”,目标设备:X,Y,M,S,T,C
*
* ENQ , 0x05,请求
* ACK ,0x06,PLC正确响应(对了)
* NAK , 0x15,PLC错误响应(错了)
* STX , 0x02,报文开始
* ETX , 0x03,报文结束
*
* 命令码的响应:
* 命令码“1”、“7”、“8”均响应0x06或0x15
* 命令码“1”的响应格式为:0x02,Dada1_H,Data1_L,0x03,Sum_H,Sum_L
* *************************************************************************/
/// <summary>
/// 三菱PLC和校验
/// 和校验,不包含起始符,但是包含结束符
/// </summary>
private static byte FX_Checksum(byte[] input_buf, byte input_Len)
{
int sum = 0;
for (int i = 1; i < input_Len; i++)
{
sum = sum + input_buf[i];
}
sum = sum % 0x100;//只取后两位
return (byte)sum;
}
/// <summary>
/// 1、读元件,命令码“0”,目标设备:X,Y,M,S,T,C,D。注:最多可读64byte数据
/// string Adress格式: X0000 Y0000 M0000 S0000 T0000 C0000 D0000
/// 例如:读D200,FX_Read_0("D0200",2)
/// </summary>
public static Boolean FX_read(string NameAndAdress, byte byteLen, ref byte[] PLCSendbuf, ref string str_Order)
{
Boolean Flag_Boolean = true;
int read_Address=0;
int k;
int j;
int Typebit_8Jinzhi;
int Typebit_10Jinzhi;
char yuanjian_Type = NameAndAdress[0];//提取字符串中的第一个字符,C#中字符串变量也就是字符串数组
string str_Num = System.Text.RegularExpressions.Regex.Replace(NameAndAdress, @"[^0-9]+", "");//提取字符中的数字
int yuanjian_Num = Convert.ToInt32(str_Num);
str_Order = "Read" + NameAndAdress;
string str_1 = byteLen.ToString();
str_Order = str_Order + "_" + str_1.PadLeft(3, '0');//检查字符串长度是否超过3,不超过的左侧加0补足到3的长度
{//元件编号为8进制,如XY
k = yuanjian_Num / 10;
j = k / 10;
Typebit_8Jinzhi = k + j * 8;
}
{//元件编号为10进制的bit类型元件,如MSCT
Typebit_10Jinzhi = yuanjian_Num / 8;
}
if (NameAndAdress.StartsWith("X"))
{//X元件编号是8进制的,范围:【X0000-X0177】
read_Address = Typebit_8Jinzhi + PLC_X_Group_Base_AddRess;
}
else if (NameAndAdress.StartsWith("Y"))
{
read_Address = Typebit_8Jinzhi + PLC_Y_Group_Base_AddRess;
}
else if (NameAndAdress.StartsWith("M"))
{
read_Address = Typebit_10Jinzhi + PLC_M_Group_Base_AddRess;
}
else if (NameAndAdress.StartsWith("S"))
{
read_Address = Typebit_10Jinzhi + PLC_S_Group_Base_AddRess;
}
else if (NameAndAdress.StartsWith("T"))
{
read_Address = Typebit_10Jinzhi + PLC_T_Group_Base_AddRess;
}
else if (NameAndAdress.StartsWith("C"))
{
read_Address = Typebit_10Jinzhi + PLC_C_Group_Base_AddRess;
}
else if (NameAndAdress.StartsWith("D"))
{
read_Address = (yuanjian_Num * 2) + PLC_D_Base_AddRess;
}
else
Flag_Boolean = false;
System.Array.Resize(ref PLCSendbuf, 11);//改变数组长度
PLCSendbuf[0] = 0x02;//起始符
PLCSendbuf[1] = 0x30;//命令字
PLCSendbuf[2] = Hex2Asc[(read_Address / 0x1000) % 0x10];
PLCSendbuf[3] = Hex2Asc[(read_Address / 0x100) % 0x10];
PLCSendbuf[4] = Hex2Asc[(read_Address / 0x10) % 0x10];
PLCSendbuf[5] = Hex2Asc[(read_Address / 0x1) % 0x10];
PLCSendbuf[6] = Hex2Asc[byteLen / 0x10];
PLCSendbuf[7] = Hex2Asc[byteLen % 0x10];
PLCSendbuf[8] = 0x03;//结束符
int sum = FX_Checksum(PLCSendbuf, 9);
PLCSendbuf[9] = Hex2Asc[sum / 0x10];
PLCSendbuf[10] = Hex2Asc[sum % 0x10];
return Flag_Boolean;
}
/// <summary>
/// 2、写元件,命令码“1”,目标设备:X,Y,M,S,T,C,D
/// 例如:写D200=1234,Write_D_16bit("D0200",1234)
/// </summary>
/// <param name="M_Nuber"></param>
public static Boolean Write_D_16bit(string D_Adress, int D_Value, ref byte[] PLCSendbuf, ref string str_Order)
{
Boolean Flag_Boolean ;
if (D_Adress.StartsWith("D"))
Flag_Boolean = true;
else
Flag_Boolean = false;
string str_Num = System.Text.RegularExpressions.Regex.Replace(D_Adress, @"[^0-9]+", "");//提取字符中的数字
int yuanjian_Num = Convert.ToInt32(str_Num);
str_Order = "Writ" + D_Adress;
string str_1 = D_Value.ToString();
str_Order = str_Order + "_" + (str_1).PadLeft(5, '0');//检查字符串长度是否超过5,不超过的左侧加0补足到5的长度
System.Array.Resize(ref PLCSendbuf, 15);//改变数组长度
PLCSendbuf[0] = 0x02;//起始符
PLCSendbuf[1] = 0x31;//命令字
int NumberAdress = (yuanjian_Num * 2) + PLC_D_Base_AddRess;
PLCSendbuf[2] = Hex2Asc[NumberAdress / 0x1000 % 0x10];
PLCSendbuf[3] = Hex2Asc[NumberAdress / 0x100 % 0x10];
PLCSendbuf[4] = Hex2Asc[NumberAdress / 0x10 % 0x10];
PLCSendbuf[5] = Hex2Asc[NumberAdress % 0x10];
PLCSendbuf[6] = 0x30;//要写入的字节数
PLCSendbuf[7] = 0x32;//要写入的字节数
PLCSendbuf[10] = Hex2Asc[D_Value / 0x1000 % 0x10];
PLCSendbuf[11] = Hex2Asc[D_Value / 0x100 % 0x10];
PLCSendbuf[8] = Hex2Asc[D_Value / 0x10 % 0x10];
PLCSendbuf[9] = Hex2Asc[D_Value % 0x10];
PLCSendbuf[12] = 0x03;
int sum = FX_Checksum(PLCSendbuf, 13);
PLCSendbuf[13] = Hex2Asc[sum / 0x10];
PLCSendbuf[14] = Hex2Asc[sum % 0x10];
return Flag_Boolean;
//PLC返回
//ACK (06H) 接受正确
//NAK (15H) 接受错误
}
/// <summary>
/// 3、置位 ,命令码“7”,目标设备:X,Y,M,S,T,C
/// 例如:置位M200,FX_SET_bit("M0200",ref PLCSendbuf,ref str_Order)
/// 参数:string NameAndNumber,元件类型及编号
/// ref byte[] PLCSendbuf,按址传输的数组
/// ref string str_Order,按址传输的字符串
/// </summary>
/// <param name="M_Nuber"></param>
public static Boolean FX_SET_bit(string NameAndNumber, ref byte[] PLCSendbuf, ref string str_Order)
{
//private byte[] buf_Set_M252 = new byte[9] { 0x02, 0x37, 0x46, 0x43, 0x30, 0x38, 0x03, 0x32, 0x42 };
//#2 7 FC08 #3 CRC CRC
Boolean Flag_Boolean = true;
string str_Num = System.Text.RegularExpressions.Regex.Replace(NameAndNumber, @"[^0-9]+", "");//提取字符中的数字
int yuanjian_Num = Convert.ToInt32(str_Num);
int read_Address = 0;
str_Order = "SET_" + NameAndNumber;
if (NameAndNumber.StartsWith("S"))
read_Address = yuanjian_Num + PLC_S_Group_Base_AddRess;
else if (NameAndNumber.StartsWith("Y"))
{//元件编号为8进制,如XY
int k = yuanjian_Num / 10;
int j = k / 10;
int Typebit_8Jinzhi = k + j * 8;
read_Address = Typebit_8Jinzhi + PLC_Y_Group_Base_AddRess;
}
else if (NameAndNumber.StartsWith("M"))
read_Address = yuanjian_Num + PLC_M_SINGLE_Base_AddRess;
else if (NameAndNumber.StartsWith("T"))
read_Address = yuanjian_Num + PLC_T_Group_Base_AddRess;
else
Flag_Boolean = false;
System.Array.Resize(ref PLCSendbuf, 9);//改变数组长度
PLCSendbuf[0] = 0x02;//起始符
PLCSendbuf[1] = 0x37;//命令字
PLCSendbuf[4] = Hex2Asc[(read_Address / 0x1000) % 0x10];
PLCSendbuf[5] = Hex2Asc[(read_Address / 0x100) % 0x10];
PLCSendbuf[2] = Hex2Asc[(read_Address / 0x10) % 0x10];
PLCSendbuf[3] = Hex2Asc[(read_Address / 0x1) % 0x10];
PLCSendbuf[6] = 0x03;
int sum = FX_Checksum(PLCSendbuf, 7);
PLCSendbuf[7] = Hex2Asc[sum / 0x10];
PLCSendbuf[8] = Hex2Asc[sum % 0x10];
return Flag_Boolean;
}
/// <summary>
/// 4、复位 ,命令码“8”,目标设备:X,Y,M,S,T,C
/// 例如:置位M200,FX_RST_bit("M0200")
/// </summary>
/// <param name="M_Nuber"></param>
public static Boolean FX_RST_bit(string NameAndNumber, ref byte[] PLCSendbuf, ref string str_Order)
{
//private byte[] buf_Set_M252 = new byte[9] { 0x02, 0x37, 0x46, 0x43, 0x30, 0x38, 0x03, 0x32, 0x42 };
//#2 7 FC08 #3 CRC CRC
Boolean Flag_Boolean = true;
string str_Num = System.Text.RegularExpressions.Regex.Replace(NameAndNumber, @"[^0-9]+", "");//提取字符中的数字
int yuanjian_Num = Convert.ToInt32(str_Num);
int read_Address = 0;
str_Order = "RST_" + NameAndNumber;
if (NameAndNumber.StartsWith("S"))
read_Address = yuanjian_Num + PLC_S_Group_Base_AddRess;
else if (NameAndNumber.StartsWith("Y"))
{//元件编号为8进制,如XY
int k = yuanjian_Num / 10;
int j = k / 10;
int Typebit_8Jinzhi = k + j * 8;
read_Address = Typebit_8Jinzhi + PLC_Y_Group_Base_AddRess;
}
else if (NameAndNumber.StartsWith("M"))
read_Address = yuanjian_Num + PLC_M_SINGLE_Base_AddRess;
else if (NameAndNumber.StartsWith("T"))
read_Address = yuanjian_Num + PLC_T_Group_Base_AddRess;
else
Flag_Boolean = false;
System.Array.Resize(ref PLCSendbuf, 9);//改变数组长度
PLCSendbuf[0] = 0x02;//起始符
PLCSendbuf[1] = 0x38;//命令字
PLCSendbuf[4] = Hex2Asc[(read_Address / 0x1000) % 0x10];
PLCSendbuf[5] = Hex2Asc[(read_Address / 0x100) % 0x10];
PLCSendbuf[2] = Hex2Asc[(read_Address / 0x10) % 0x10];
PLCSendbuf[3] = Hex2Asc[(read_Address / 0x1) % 0x10];
PLCSendbuf[6] = 0x03;
int sum = FX_Checksum(PLCSendbuf, 7);
PLCSendbuf[7] = Hex2Asc[sum / 0x10];
PLCSendbuf[8] = Hex2Asc[sum % 0x10];
return Flag_Boolean;
}
/***************************************************
* 以下程序是接收到串口的应答数据处理
*
* 处理流程:
* 1、检查PLC响应是否完成
* 2、PLC响应完成后,检查和校验是否正确
* 3、解析PLC响应数据
* ************************************************/
/// <summary>
/// 1、检查PLC响应是否完成
/// </summary>
/// <param name="readbuf"></param>
/// <returns></returns>
public static Boolean PLC_recive_Over(byte[] readbuf)
{
Boolean Flag_Boolean = true;
//判断首字符
if (readbuf[0] == 0x06)//ACK (06H) 接受正确
Len_PLC_Recive = 0;//接收数据字节数,清零
else if (readbuf[0] == 0x15)//NAK (15H) 接受错误
Len_PLC_Recive = 0;//接收数据字节数,清零
else if (readbuf[0] == 0x02)//起始符
{
Len_PLC_Recive = readbuf.Length;//接收数据字节数
System.Array.Resize(ref Buf_PLC_Recive, Len_PLC_Recive);//改变数组长度
for (int i = 0; i < Len_PLC_Recive; i++)
Buf_PLC_Recive[i] = readbuf[i];
if (Len_PLC_Recive >= 6)//最短一帧信息4byte:起始符、2byte数据、结束符、2byte和校验
Flag_Boolean = Check_End_Char();//检查是否接收到结束符
else
Flag_Boolean = false;
}
else if (Len_PLC_Recive > 100)//不可能接收到100byte,所以将错误接收清空
Len_PLC_Recive = 0;
else if (Len_PLC_Recive > 0)//PLC返回的串口数据长度
{
int temp_Len = readbuf.Length;//接收数据字节数
System.Array.Resize(ref Buf_PLC_Recive, Len_PLC_Recive + temp_Len);//改变数组长度
for (int i = Len_PLC_Recive; i < (Len_PLC_Recive + temp_Len); i++)
Buf_PLC_Recive[i] = readbuf[i - Len_PLC_Recive];
Len_PLC_Recive = Len_PLC_Recive + temp_Len;
if (Len_PLC_Recive >= 6)//最短一帧信息6byte:起始符、2byte数据、结束符、2byte和校验
Flag_Boolean = Check_End_Char();//检查是否接收到结束符
else//接收的数据字节数<6
Flag_Boolean = false;
}
else//Len_PLC_Recive,不可能小于0
Len_PLC_Recive = 0;
return Flag_Boolean;
}
/// <summary>
/// 检查是否收到结束符
/// </summary>
/// <returns></returns>
private static Boolean Check_End_Char()
{
if (Buf_PLC_Recive[Len_PLC_Recive - 3] == 0x03)
return true;
else
return false;
}
/// <summary>
/// 2、PLC响应完成后,检查和校验是否正确
/// </summary>
public static Boolean ReadBuf_SumCRC()
{
byte sum_len;
byte sum;
sum_len = (byte)(Len_PLC_Recive - 2);
sum = FX_Checksum(Buf_PLC_Recive, sum_len);
if ((Buf_PLC_Recive[Len_PLC_Recive - 2] == Hex2Asc[sum / 0x10]) && (Buf_PLC_Recive[Len_PLC_Recive - 1] == Hex2Asc[sum % 0x10]))
return true;
else
return false;
}
/// <summary>
/// 3、解析PLC响应数据
/// </summary>
/// <returns></returns>
public static Boolean PLC_Recive_Data_解析()
{
//只有命令"0"才会有数据返回
//命令"0"的目标设备X,Y,M,S,T,C,D
//str_Order="Read"+NameAndAdress;//ReadD0123
Boolean FLAG = false;
string str_4 = "NULL";
if (string_Now_Send_PLC_FX_Order.Length > 3)
str_4 = string_Now_Send_PLC_FX_Order.Substring(0, 4);
if ((string_Now_Send_PLC_FX_Order.Length >= 9) && (str_4 == "Read"))
{
string str_原件名称 = string_Now_Send_PLC_FX_Order.Substring(4, 1);
string str_元件编号 = string_Now_Send_PLC_FX_Order.Substring(5, 4);
int int_元件编号 = Convert.ToInt16(str_元件编号);
int yuanjian_Num;
FLAG = true;
//PLC返回的数据提取
int ByteCount = (Buf_PLC_Recive.Length - 4) / 2;//起始符1,结束符1,和校验码2
byte[] data = new byte[ByteCount];
for (int i = 0; i < ByteCount; i++)
data[i] = (byte)(Asc2Hex[Buf_PLC_Recive[1 + i * 2] - 0x30] * 0x10 + Asc2Hex[Buf_PLC_Recive[2 + i * 2] - 0x30]);
switch (str_原件名称)
{
case "X":
break;
case "Y":
{//元件编号为8进制,如XY
int k = int_元件编号 / 10;
int j = k / 10;
yuanjian_Num = k + j * 8;
}
for (int i = 0; i < ByteCount; i++)
{
for (byte j = 0; j < 8; j++)
{
if ((data[i] & U8toBin[j]) > 0)
PLC_FX_Y[yuanjian_Num] = true;
else
PLC_FX_Y[yuanjian_Num] = false;
yuanjian_Num++;
}
}
break;
case "M"://M0 to M1023
yuanjian_Num = int_元件编号 - int_元件编号 % 8;
for (int i = 0; i < ByteCount; i++)
{
for (byte j = 0; j < 8; j++)
{
if ((data[i] & U8toBin[j]) > 0)
PLC_FX_M[yuanjian_Num] = true;
else
PLC_FX_M[yuanjian_Num] = false;
yuanjian_Num++;
}
}
break;
case "S"://S0 to S999
yuanjian_Num = int_元件编号 - int_元件编号 % 8;
for (int i = 0; i < ByteCount; i++)
{
for (byte j = 0; j < 8; j++)
{
if ((data[i] & U8toBin[j]) > 0)
PLC_FX_S[yuanjian_Num] = true;
else
PLC_FX_S[yuanjian_Num] = false;
yuanjian_Num++;
}
}
break;
case "T":
break;
case "C":
break;
case "D"://D0 to D1023
int data16Count = ByteCount / 2;
for (int i = 0; i < data16Count; i++)
{
PLC_FX_D[int_元件编号] = data[0 + i * 2] + data[1 + i * 2] * 0x100;
int_元件编号++;
}
break;
default: /* 可选的 */
FLAG = false;
break;
}
}
Len_PLC_Recive = 0;
return FLAG;
}
}
}