当前位置: 首页 > article >正文

C#使用TCP-S7协议读写西门子PLC(三)

接上篇

C#使用TCP-S7协议读写西门子PLC(二)-CSDN博客

这里我们进行封装读写西门子PLC的S7协议命令以及连接西门子PLC并两次握手

新建部分类文件SiemensS7ProtocolUtil.ReadWrite.cs

主要方法:

连接西门子PLC并发送两次握手。两次握手成功后,才真正连接到PLC

public OperateResult ConnectPlcAndHandshake(SiemensPlcCategory siemensPlcCategory, IPEndPoint endPoint, int timeout = 3000)

生成一个写入字节数据的指令

public static OperateResult<byte[]> BuildWriteByteCommand(OperateResult<byte, int, ushort> analysis, byte[] data)

生成一个读取字数据指令头的通用方法【一个字Word占用两个字节Byte】

public static OperateResult<byte[]> BuildReadCommand(OperateResult<byte, int, ushort>[] address, ushort[] length)

SiemensS7ProtocolUtil.ReadWrite.cs源程序如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace PlcSiemesS7Demo
{
    /// <summary>
    /// 西门子S7协议,封装读写命令
    /// 关键方法:连接PLC并发送两次握手、生成写PLC命令,生成读PLC命令
    /// (1).public OperateResult ConnectPlcAndHandshake(SiemensPlcCategory siemensPlcCategory, IPEndPoint endPoint, int timeout = 3000)
    /// (2).public static OperateResult&lt;byte[]&gt; BuildWriteByteCommand(OperateResult&lt;byte, int, ushort&gt; analysis, byte[] data)
    /// (3).BuildReadCommand(OperateResult&lt;byte, int, ushort&gt;[] address, ushort[] length)
    /// </summary>
    public partial class SiemensS7ProtocolUtil
    {
        #region 西门子S7协议握手【两次握手】命令,固定.不同型号的PLC握手命令有少许不同
        private byte[] plcHead1 = new byte[22]
        {
            0x03,0x00,0x00,0x16,0x11,0xE0,0x00,0x00,0x00,0x01,0x00,0xC0,0x01,0x0A,0xC1,0x02,
            0x01,0x02,0xC2,0x02,0x01,0x00
        };
        private byte[] plcHead2 = new byte[25]
        {
            0x03,0x00,0x00,0x19,0x02,0xF0,0x80,0x32,0x01,0x00,0x00,0x04,0x00,0x00,0x08,0x00,
            0x00,0xF0,0x00,0x00,0x01,0x00,0x01,0x01,0xE0
        };
        private byte[] plcHead1_200smart = new byte[22]
        {
            0x03,0x00,0x00,0x16,0x11,0xE0,0x00,0x00,0x00,0x01,0x00,0xC1,0x02,0x10,0x00,0xC2,
            0x02,0x03,0x00,0xC0,0x01,0x0A
        };
        private byte[] plcHead2_200smart = new byte[25]
        {
            0x03,0x00,0x00,0x19,0x02,0xF0,0x80,0x32,0x01,0x00,0x00,0xCC,0xC1,0x00,0x08,0x00,
            0x00,0xF0,0x00,0x00,0x01,0x00,0x01,0x03,0xC0
        };
        #endregion

        /// <summary>
        /// 连接西门子PLC并发送两次握手。两次握手成功后,才真正连接到PLC
        /// 使用网络终结点和PLC型号枚举进行连接
        /// </summary>
        /// <param name="siemensPlcCategory"></param>
        /// <param name="endPoint"></param>
        /// <param name="timeout"></param>
        /// <returns></returns>
        public OperateResult ConnectPlcAndHandshake(SiemensPlcCategory siemensPlcCategory, IPEndPoint endPoint, int timeout = 3000)
        {
            isConnected = false;
            this.SiemensPlcCategory = siemensPlcCategory;
            switch (siemensPlcCategory)
            {
                case SiemensPlcCategory.S1200:
                case SiemensPlcCategory.S1500:
                    plcHead1[21] = 0;
                    break;
                case SiemensPlcCategory.S300:
                    plcHead1[21] = 2;
                    break;
                case SiemensPlcCategory.S400:
                    plcHead1[21] = 3;
                    plcHead1[17] = 0x00;
                    break;
                case SiemensPlcCategory.S200Smart:
                    plcHead1 = plcHead1_200smart;
                    plcHead2 = plcHead2_200smart;
                    break;
                default:
                    plcHead1[18] = 0;
                    break;
            }
            // 重新连接之前,先将旧的数据进行清空
            CoreSocket?.Close();
            OperateResult<Socket> result = ConnectPlc(endPoint, timeout);
            if (result.IsSuccess)
            {
                // 第一次握手 -> First handshake
                OperateResult<byte[]> read_first = SendDataAndWaitResult(result.Content, plcHead1);
                if (!read_first.IsSuccess)
                {
                    RecordLogEvent?.Invoke($"第一次握手出错:{read_first.Message}");
                    return read_first;
                }

                // 第二次握手 -> Second handshake
                OperateResult<byte[]> read_second = SendDataAndWaitResult(result.Content, plcHead2);
                if (!read_second.IsSuccess)
                {
                    RecordLogEvent?.Invoke($"第二次握手出错:{read_second.Message}");
                    return read_second;
                }

                // 返回成功的信号 
                CoreSocket = result.Content;
                result.IsSuccess = true;
                isConnected = true;
                RecordLogEvent?.Invoke($"连接PLC【{endPoint}】成功并且两次握手成功");
            }
            else
            {
                result.Content?.Close();
                CoreSocket = null;
                result.IsSuccess = false;
            }
            return result;
        }

        /// <summary>
        /// 连接西门子PLC并发送两次握手。两次握手成功后,才真正连接到PLC
        /// 使用IP地址和端口【默认102】和PLC型号枚举进行连接
        /// </summary>
        /// <param name="siemensPlcCategory"></param>
        /// <param name="ipAddress"></param>
        /// <param name="port"></param>
        /// <param name="timeout"></param>
        /// <returns></returns>
        public OperateResult ConnectPlcAndHandshake(SiemensPlcCategory siemensPlcCategory, string ipAddress, int port = 102, int timeout = 3000)
        {
            return ConnectPlcAndHandshake(siemensPlcCategory, new IPEndPoint(IPAddress.Parse(ipAddress), port), timeout);
        }

        private OperateResult<byte[]> Read(OperateResult<byte, int, ushort>[] address, ushort[] length)
        {
            // 构建指令 -> Build read command
            OperateResult<byte[]> command = BuildReadCommand(address, length);
            if (!command.IsSuccess) return command;

            // 核心交互 -> Core Interactions
            OperateResult<byte[]> read = SendDataAndWaitResult(command.Content);
            if (!read.IsSuccess) 
                return read;


            // 分析结果 -> Analysis results
            int receiveCount = 0;
            for (int i = 0; i < length.Length; i++)
            {
                receiveCount += length[i];
            }

            if (read.Content.Length >= 21 && read.Content[20] == length.Length)
            {
                byte[] buffer = new byte[receiveCount];
                int kk = 0;
                int ll = 0;
                for (int ii = 21; ii < read.Content.Length; ii++)
                {
                    if ((ii + 1) < read.Content.Length)
                    {
                        if (read.Content[ii] == 0xFF && read.Content[ii + 1] == 0x04)
                        {
                            Array.Copy(read.Content, ii + 4, buffer, ll, length[kk]);
                            ii += length[kk] + 3;
                            ll += length[kk];
                            kk++;
                        }
                    }
                }

                return OperateResult.CreateSuccessResult(buffer);
            }
            else
            {
                return new OperateResult<byte[]>() { ErrorCode = read.ErrorCode, Message = "数据块长度校验失败,请检查是否开启put/get以及关闭db块优化" };
            }
        }

        private OperateResult<byte[]> ReadBitFromPLC(OperateResult<byte, int, ushort> analysis)
        {
            // 指令生成 -> Build bit read command
            OperateResult<byte[]> command = BuildBitReadCommand(analysis);
            if (!command.IsSuccess) return OperateResult.CreateFailedResult<byte[]>(command);

            // 核心交互 ->  Core interactive
            OperateResult<byte[]> read = SendDataAndWaitResult(command.Content);
            if (!read.IsSuccess) return read;

            // 分析结果 -> Analysis read result
            int receiveCount = 1;
            if (read.Content.Length >= 21 && read.Content[20] == 1)
            {
                byte[] buffer = new byte[receiveCount];
                if (22 < read.Content.Length)
                {
                    if (read.Content[21] == 0xFF && read.Content[22] == 0x03)
                    {
                        buffer[0] = read.Content[25];
                    }
                }

                return OperateResult.CreateSuccessResult(buffer);
            }
            else
            {
                return new OperateResult<byte[]>(read.ErrorCode, "数据块长度校验失败,请检查是否开启put/get以及关闭db块优化");
            }
        }

        /// <summary>
        /// 基础的写入数据的操作支持 -> Operational support for the underlying write data
        /// </summary>
        /// <param name="entireValue">完整的字节数据 -> Full byte data</param>
        /// <returns>是否写入成功的结果对象 -> Whether to write a successful result object</returns>
        private OperateResult WriteBase(byte[] entireValue)
        {
            OperateResult<byte[]> write = SendDataAndWaitResult(entireValue);
            if (!write.IsSuccess) return write;

            if (write.Content[write.Content.Length - 1] != 0xFF)
            {
                // 写入异常 -> WriteError
                return new OperateResult(write.Content[write.Content.Length - 1], "写入数据异常,代号为:" + write.Content[write.Content.Length - 1]);
            }
            else
            {
                return OperateResult.CreateSuccessResult();
            }
        }

        /// <summary>
        /// 生成一个写入字节数据的指令 -> Generate an instruction to write byte data
        /// </summary>
        /// <param name="analysis">内存区域标识,起始地址*8,DB块编号 </param>
        /// <param name="data">原始的字节数据 -> Raw byte data</param>
        /// <returns>包含结果对象的报文 -> Message containing the result object</returns>
        public static OperateResult<byte[]> BuildWriteByteCommand(OperateResult<byte, int, ushort> analysis, byte[] data)
        {
            byte[] _PLCCommand = new byte[35 + data.Length];
            _PLCCommand[0] = 0x03;
            _PLCCommand[1] = 0x00;
            // 长度 -> Length
            _PLCCommand[2] = (byte)((35 + data.Length) / 256);
            _PLCCommand[3] = (byte)((35 + data.Length) % 256);
            // 固定 -> Fixed
            _PLCCommand[4] = 0x02;
            _PLCCommand[5] = 0xF0;
            _PLCCommand[6] = 0x80;
            _PLCCommand[7] = 0x32;
            // 命令 发 -> command to send
            _PLCCommand[8] = 0x01;
            // 标识序列号 -> Identification serial Number
            _PLCCommand[9] = 0x00;
            _PLCCommand[10] = 0x00;
            _PLCCommand[11] = 0x00;
            _PLCCommand[12] = 0x01;
            // 固定 -> Fixed
            _PLCCommand[13] = 0x00;
            _PLCCommand[14] = 0x0E;
            // 写入长度+4 -> Write Length +4
            _PLCCommand[15] = (byte)((4 + data.Length) / 256);
            _PLCCommand[16] = (byte)((4 + data.Length) % 256);
            // 读写指令 -> Read and write instructions
            _PLCCommand[17] = 0x05;
            // 写入数据块个数 -> Number of data blocks written
            _PLCCommand[18] = 0x01;
            // 固定,返回数据长度 -> Fixed, return data length
            _PLCCommand[19] = 0x12;
            _PLCCommand[20] = 0x0A;
            _PLCCommand[21] = 0x10;
            // 写入方式,1是按位,2是按字 -> Write mode, 1 is bitwise, 2 is by word
            _PLCCommand[22] = 0x02;
            // 写入数据的个数 -> Number of Write Data
            _PLCCommand[23] = (byte)(data.Length / 256);
            _PLCCommand[24] = (byte)(data.Length % 256);
            // DB块编号,如果访问的是DB块的话 -> DB block number, if you are accessing a DB block
            _PLCCommand[25] = (byte)(analysis.Content3 / 256);
            _PLCCommand[26] = (byte)(analysis.Content3 % 256);
            // 写入数据的类型 -> Types of writing data
            _PLCCommand[27] = analysis.Content1;
            // 偏移位置 -> Offset position 因65535*8 即0x07 FF F8 偏移地址占用三个字节:下面注释的三行等价于BitConverter.GetBytes(Int32) 
            //_PLCCommand[28] = (byte)(analysis.Content2 / 256 / 256 % 256);
            //_PLCCommand[29] = (byte)(analysis.Content2 / 256 % 256);
            //_PLCCommand[30] = (byte)(analysis.Content2 % 256);
            byte[] offsetAddress = BitConverter.GetBytes(analysis.Content2);
            _PLCCommand[28] = offsetAddress[2];
            _PLCCommand[29] = offsetAddress[1];
            _PLCCommand[30] = offsetAddress[0];
            // 按字写入 -> Write by Word
            _PLCCommand[31] = 0x00;
            _PLCCommand[32] = 0x04;
            // 按位计算的长度 -> The length of the bitwise calculation
            _PLCCommand[33] = (byte)(data.Length * 8 / 256);
            _PLCCommand[34] = (byte)(data.Length * 8 % 256);

            data.CopyTo(_PLCCommand, 35);

            return OperateResult.CreateSuccessResult(_PLCCommand);
        }

        public static OperateResult<byte[]> BuildWriteBitCommand(OperateResult<byte, int, ushort> analysis, bool data)
        {
            //OperateResult<byte, int, ushort> analysis = AnalysisAddress(address);
            if (!analysis.IsSuccess) return OperateResult.CreateFailedResult<byte[]>(analysis);


            byte[] buffer = new byte[1];
            buffer[0] = data ? (byte)0x01 : (byte)0x00;

            byte[] _PLCCommand = new byte[35 + buffer.Length];
            _PLCCommand[0] = 0x03;
            _PLCCommand[1] = 0x00;
            // 长度 -> length
            _PLCCommand[2] = (byte)((35 + buffer.Length) / 256);
            _PLCCommand[3] = (byte)((35 + buffer.Length) % 256);
            // 固定 -> fixed
            _PLCCommand[4] = 0x02;
            _PLCCommand[5] = 0xF0;
            _PLCCommand[6] = 0x80;
            _PLCCommand[7] = 0x32;
            // 命令 发 -> command to send
            _PLCCommand[8] = 0x01;
            // 标识序列号 -> Identification serial Number
            _PLCCommand[9] = 0x00;
            _PLCCommand[10] = 0x00;
            _PLCCommand[11] = 0x00;
            _PLCCommand[12] = 0x01;
            // 固定 -> fixed
            _PLCCommand[13] = 0x00;
            _PLCCommand[14] = 0x0E;
            // 写入长度+4 -> Write Length +4
            _PLCCommand[15] = (byte)((4 + buffer.Length) / 256);
            _PLCCommand[16] = (byte)((4 + buffer.Length) % 256);
            // 命令起始符 -> Command start character
            _PLCCommand[17] = 0x05;
            // 写入数据块个数 -> Number of data blocks written
            _PLCCommand[18] = 0x01;
            _PLCCommand[19] = 0x12;
            _PLCCommand[20] = 0x0A;
            _PLCCommand[21] = 0x10;
            // 写入方式,1是按位,2是按字 -> Write mode, 1 is bitwise, 2 is by word
            _PLCCommand[22] = 0x01;
            // 写入数据的个数 -> Number of Write Data
            _PLCCommand[23] = (byte)(buffer.Length / 256);
            _PLCCommand[24] = (byte)(buffer.Length % 256);
            // DB块编号,如果访问的是DB块的话 -> DB block number, if you are accessing a DB block
            _PLCCommand[25] = (byte)(analysis.Content3 / 256);
            _PLCCommand[26] = (byte)(analysis.Content3 % 256);
            // 写入数据的类型 -> Types of writing data
            _PLCCommand[27] = analysis.Content1;
            // 偏移位置 -> Offset position
            _PLCCommand[28] = (byte)(analysis.Content2 / 256 / 256);
            _PLCCommand[29] = (byte)(analysis.Content2 / 256);
            _PLCCommand[30] = (byte)(analysis.Content2 % 256);
            // 按位写入 -> Bitwise Write
            _PLCCommand[31] = 0x00;
            _PLCCommand[32] = 0x03;
            // 按位计算的长度 -> The length of the bitwise calculation
            _PLCCommand[33] = (byte)(buffer.Length / 256);
            _PLCCommand[34] = (byte)(buffer.Length % 256);

            buffer.CopyTo(_PLCCommand, 35);

            return OperateResult.CreateSuccessResult(_PLCCommand);
        }

        /// <summary>
        /// 解析地址,返回元组【地址类型,起始地址,DB块编号】
        /// </summary>
        /// <param name="plcRegisterCategory">寄存器类型</param>
        /// <param name="offsetAddress">偏移量,偏移地址</param>
        /// <param name="dbNumber">DB块号</param>
        /// <param name="bitIndex">位索引0~7</param>
        /// <returns></returns>
        private static OperateResult<byte, int, ushort> AnalysisAddress(PlcRegisterCategory plcRegisterCategory, ushort offsetAddress, ushort dbNumber = 0, int bitIndex = 0)
        {
            //地址类型,起始地址,DB块编号
            OperateResult<byte, int, ushort> result = new OperateResult<byte, int, ushort>();
            result.Content3 = 0;
            switch (plcRegisterCategory)
            {
                case PlcRegisterCategory.DB:
                    result.Content1 = 0x84;
                    result.Content2 = offsetAddress * 8 + bitIndex;
                    result.Content3 = dbNumber;
                    break;
                case PlcRegisterCategory.M:
                    result.Content1 = 0x83;
                    result.Content2 = offsetAddress * 8 + bitIndex;
                    break;
                case PlcRegisterCategory.Input:
                    result.Content1 = 0x81;
                    result.Content2 = offsetAddress * 8 + bitIndex;
                    break;
                case PlcRegisterCategory.Quit:
                    result.Content1 = 0x82;
                    result.Content2 = offsetAddress * 8 + bitIndex;
                    break;
                case PlcRegisterCategory.T:
                    result.Content1 = 0x1D;
                    result.Content2 = offsetAddress * 8 + bitIndex;
                    break;
                case PlcRegisterCategory.C:
                    result.Content1 = 0x1C;
                    result.Content2 = offsetAddress * 8 + bitIndex;
                    break;
                case PlcRegisterCategory.V:
                    result.Content1 = 0x84;
                    result.Content2 = offsetAddress * 8 + bitIndex;
                    result.Content3 = 1;
                    break;
                default:
                    result.Message = "输入的类型不支持,请重新输入";
                    result.Content1 = 0;
                    result.Content2 = 0;
                    result.Content3 = 0;
                    return result;
            }
            result.IsSuccess = true;
            return result;
        }

        /// <summary>
        /// 生成一个读取字数据指令头的通用方法【一个字Word占用两个字节Byte】 ->
        /// A general method for generating a command header to read a Word data
        /// </summary>
        /// <param name="address">起始地址,例如M100,I0,Q0,DB2.100 ->
        /// Start address, such as M100,I0,Q0,DB2.100</param>
        /// <param name="length">读取数据长度 -> Read Data length</param>
        /// <returns>包含结果对象的报文 -> Message containing the result object</returns>
        public static OperateResult<byte[]> BuildReadCommand(OperateResult<byte, int, ushort>[] address, ushort[] length)
        {
            if (address == null) throw new NullReferenceException("address");
            if (length == null) throw new NullReferenceException("count");
            if (address.Length != length.Length) throw new Exception("两个参数的个数不一致");
            if (length.Length > 19) throw new Exception("读取的数组数量不允许大于19");

            int readCount = length.Length;
            byte[] _PLCCommand = new byte[19 + readCount * 12];
            // ======================================================================================
            _PLCCommand[0] = 0x03;                                                // 报文头 -> Head
            _PLCCommand[1] = 0x00;
            _PLCCommand[2] = (byte)(_PLCCommand.Length / 256);                    // 长度 -> Length
            _PLCCommand[3] = (byte)(_PLCCommand.Length % 256);
            _PLCCommand[4] = 0x02;                                                // 固定 -> Fixed
            _PLCCommand[5] = 0xF0;
            _PLCCommand[6] = 0x80;
            _PLCCommand[7] = 0x32;                                                // 协议标识 -> Protocol identification
            _PLCCommand[8] = 0x01;                                                // 命令:发 -> Command: Send
            _PLCCommand[9] = 0x00;                                                // 冗余标识(保留)-> redundancy identification (reserved): 0x0000;
            _PLCCommand[10] = 0x00;                                               // protocol data unit reference; it’s increased by request event;
            _PLCCommand[11] = 0x00;
            _PLCCommand[12] = 0x01;                                               // 参数命令数据总长度 -> Parameter command Data total length
            _PLCCommand[13] = (byte)((_PLCCommand.Length - 17) / 256);
            _PLCCommand[14] = (byte)((_PLCCommand.Length - 17) % 256);
            _PLCCommand[15] = 0x00;                                               // 读取内部数据时为00,读取CPU型号为Data数据长度 -> Read internal data is 00, read CPU model is data length
            _PLCCommand[16] = 0x00;
            // =====================================================================================
            _PLCCommand[17] = 0x04;                                               // 读写指令,04读,05写 -> Read-write instruction, 04 read, 05 Write
            _PLCCommand[18] = (byte)readCount;                                    // 读取数据块个数 -> Number of data blocks read

            for (int ii = 0; ii < readCount; ii++)
            {
                //===========================================================================================
                // 指定有效值类型 -> Specify a valid value type
                _PLCCommand[19 + ii * 12] = 0x12;
                // 接下来本次地址访问长度 -> The next time the address access length
                _PLCCommand[20 + ii * 12] = 0x0A;
                // 语法标记,ANY -> Syntax tag, any
                _PLCCommand[21 + ii * 12] = 0x10;
                // 按字为单位 -> by word
                _PLCCommand[22 + ii * 12] = 0x02;
                // 访问数据的个数 -> Number of Access data
                _PLCCommand[23 + ii * 12] = (byte)(length[ii] / 256);
                _PLCCommand[24 + ii * 12] = (byte)(length[ii] % 256);
                // DB块编号,如果访问的是DB块的话 -> DB block number, if you are accessing a DB block
                _PLCCommand[25 + ii * 12] = (byte)(address[ii].Content3 / 256);
                _PLCCommand[26 + ii * 12] = (byte)(address[ii].Content3 % 256);
                // 访问数据类型 -> Accessing data types
                _PLCCommand[27 + ii * 12] = address[ii].Content1;
                // 偏移位置 -> Offset position
                _PLCCommand[28 + ii * 12] = (byte)(address[ii].Content2 / 256 / 256 % 256);
                _PLCCommand[29 + ii * 12] = (byte)(address[ii].Content2 / 256 % 256);
                _PLCCommand[30 + ii * 12] = (byte)(address[ii].Content2 % 256);
            }

            return OperateResult.CreateSuccessResult(_PLCCommand);
        }

        /// <summary>
        /// 生成一个位读取数据指令头的通用方法 ->
        /// A general method for generating a bit-read-Data instruction header
        /// </summary>
        /// <param name="address">起始地址,例如M100.0,I0.1,Q0.1,DB2.100.2 ->
        /// Start address, such as M100.0,I0.1,Q0.1,DB2.100.2
        /// </param>
        /// <returns>包含结果对象的报文 -> Message containing the result object</returns>
        public static OperateResult<byte[]> BuildBitReadCommand(OperateResult<byte, int, ushort> analysis)
        {
            //OperateResult<byte, int, ushort> analysis = AnalysisAddress(address);
            if (!analysis.IsSuccess) return OperateResult.CreateFailedResult<byte[]>(analysis);

            byte[] _PLCCommand = new byte[31];
            _PLCCommand[0] = 0x03;
            _PLCCommand[1] = 0x00;
            // 长度 -> Length
            _PLCCommand[2] = (byte)(_PLCCommand.Length / 256);
            _PLCCommand[3] = (byte)(_PLCCommand.Length % 256);
            // 固定 -> Fixed
            _PLCCommand[4] = 0x02;
            _PLCCommand[5] = 0xF0;
            _PLCCommand[6] = 0x80;
            _PLCCommand[7] = 0x32;
            // 命令:发 -> command to send
            _PLCCommand[8] = 0x01;
            // 标识序列号
            _PLCCommand[9] = 0x00;
            _PLCCommand[10] = 0x00;
            _PLCCommand[11] = 0x00;
            _PLCCommand[12] = 0x01;
            // 命令数据总长度 -> Identification serial Number
            _PLCCommand[13] = (byte)((_PLCCommand.Length - 17) / 256);
            _PLCCommand[14] = (byte)((_PLCCommand.Length - 17) % 256);

            _PLCCommand[15] = 0x00;
            _PLCCommand[16] = 0x00;

            // 命令起始符 -> Command start character
            _PLCCommand[17] = 0x04;
            // 读取数据块个数 -> Number of data blocks read
            _PLCCommand[18] = 0x01;

            //===========================================================================================
            // 读取地址的前缀 -> Read the prefix of the address
            _PLCCommand[19] = 0x12;
            _PLCCommand[20] = 0x0A;
            _PLCCommand[21] = 0x10;
            // 读取的数据时位 -> Data read-time bit
            _PLCCommand[22] = 0x01;
            // 访问数据的个数 -> Number of Access data
            _PLCCommand[23] = 0x00;
            _PLCCommand[24] = 0x01;
            // DB块编号,如果访问的是DB块的话 -> DB block number, if you are accessing a DB block
            _PLCCommand[25] = (byte)(analysis.Content3 / 256);
            _PLCCommand[26] = (byte)(analysis.Content3 % 256);
            // 访问数据类型 -> Types of reading data
            _PLCCommand[27] = analysis.Content1;
            // 偏移位置 -> Offset position
            _PLCCommand[28] = (byte)(analysis.Content2 / 256 / 256 % 256);
            _PLCCommand[29] = (byte)(analysis.Content2 / 256 % 256);
            _PLCCommand[30] = (byte)(analysis.Content2 % 256);

            return OperateResult.CreateSuccessResult(_PLCCommand);
        }
    }
}


http://www.kler.cn/a/305408.html

相关文章:

  • 结构体是否包含特定类型的成员变量
  • 使用elementUI实现表格行拖拽改变顺序,无需引入外部库
  • 32位、64位、x86与x64:深入解析计算机架构
  • K8资源之endpoint资源EP资源
  • C++ 数组与结构 编程练习
  • 2411C++,C++26反射示例
  • kafka的主要功能
  • 小程序面试题四
  • 【Go - 超实用,3行代码实现个自增器】
  • react 安装使用 antd+国际化+定制化主题+样式兼容
  • 【 前端优化】Vue 3 性能优化技巧
  • Java项目: 基于SpringBoot+mybatis+maven服装生产管理系统(含源码+数据库+任务书+开题报告+毕业论文)
  • 【CSS】样式水平垂直居中
  • 果蔬识别系统性能优化之路(四)
  • HarmonyOS开发之使用PhotoViewPicker(图库选择器)保存图片
  • 基于SpringBoot+Vue+MySQL的IT技术交流和分享平台
  • apt:Debian 高级包管理器
  • Pyecharts数据可视化大屏:打造沉浸式数据分析体验
  • MySQL中用with as 解决临时表的问题
  • 【Android】【Bug】使用OSmdroid绘制轨迹断裂问题
  • 数据赋能(202)——开发:数据开发管理——技术方法、主要工具
  • Djourney新手入门基础,AI摄影+AI设计+AI绘画-AIGC作图
  • 【PyCharm】和git安装教程
  • Haskell中的数据交换:通过http-conduit发送JSON请求
  • (k8s)Kubernetes本地存储接入
  • 双指针的用法以及示例