三菱通信串口协议

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;
    }
    
}

}

猜你喜欢

转载自blog.csdn.net/tel_1392/article/details/113785227