using System; using System.Text; namespace XP.Hardware.PLC.Helpers { /// /// PLC 数据块解析器 | PLC data block parser /// 将批量读取的 byte[] 按偏移量解析为各种数据类型,避免多次单点读取的通讯开销 /// S7 PLC 使用大端字节序(Big-Endian) /// public class PlcDataBlock { private readonly byte[] _data; private readonly int _baseAddress; /// /// 原始字节数据 | Raw byte data /// public byte[] RawData => _data; /// /// 数据块起始地址 | Data block base address /// public int BaseAddress => _baseAddress; /// /// 数据长度(字节数)| Data length in bytes /// public int Length => _data?.Length ?? 0; /// /// 构造函数 | Constructor /// /// 批量读取的字节数据 | Batch read byte data /// 数据块起始地址 | Data block base address public PlcDataBlock(byte[] data, int baseAddress = 0) { _data = data ?? throw new ArgumentNullException(nameof(data)); _baseAddress = baseAddress; } /// /// 获取相对于 baseAddress 的实际偏移量 | Get actual offset relative to baseAddress /// /// 绝对地址 | Absolute address /// 数组内偏移量 | Array offset private int ToOffset(int absoluteAddress) { int offset = absoluteAddress - _baseAddress; if (offset < 0 || offset >= _data.Length) throw new ArgumentOutOfRangeException(nameof(absoluteAddress), $"地址 {absoluteAddress} 超出数据范围 [{_baseAddress}, {_baseAddress + _data.Length - 1}] | " + $"Address {absoluteAddress} out of range [{_baseAddress}, {_baseAddress + _data.Length - 1}]"); return offset; } /// /// 检查偏移量和长度是否在范围内 | Check if offset and length are within range /// private void CheckRange(int offset, int requiredBytes) { if (offset + requiredBytes > _data.Length) throw new ArgumentOutOfRangeException( $"地址 {_baseAddress + offset} 需要 {requiredBytes} 字节,但剩余仅 {_data.Length - offset} 字节 | " + $"Address {_baseAddress + offset} requires {requiredBytes} bytes, but only {_data.Length - offset} remaining"); } /// /// 读取 Bool 值(按位寻址)| Read Bool value (bit addressing) /// /// 绝对字节地址 | Absolute byte address /// 位索引 (0-7) | Bit index (0-7) /// 布尔值 | Boolean value public bool GetBool(int address, int bitIndex) { if (bitIndex < 0 || bitIndex > 7) throw new ArgumentOutOfRangeException(nameof(bitIndex), $"位索引必须在 0-7 之间 | Bit index must be between 0-7"); int offset = ToOffset(address); return (_data[offset] & (1 << bitIndex)) != 0; } /// /// 读取 Byte 值 | Read Byte value /// /// 绝对字节地址 | Absolute byte address /// 字节值 | Byte value public byte GetByte(int address) { int offset = ToOffset(address); return _data[offset]; } /// /// 读取 Int16 (Short) 值,大端字节序 | Read Int16 (Short) value, Big-Endian /// /// 绝对字节地址 | Absolute byte address /// Int16 值 | Int16 value public short GetInt16(int address) { int offset = ToOffset(address); CheckRange(offset, 2); return (short)((_data[offset] << 8) | _data[offset + 1]); } /// /// 读取 UInt16 (UShort) 值,大端字节序 | Read UInt16 (UShort) value, Big-Endian /// /// 绝对字节地址 | Absolute byte address /// UInt16 值 | UInt16 value public ushort GetUInt16(int address) { int offset = ToOffset(address); CheckRange(offset, 2); return (ushort)((_data[offset] << 8) | _data[offset + 1]); } /// /// 读取 Int32 (Int) 值,大端字节序 | Read Int32 (Int) value, Big-Endian /// /// 绝对字节地址 | Absolute byte address /// Int32 值 | Int32 value public int GetInt32(int address) { int offset = ToOffset(address); CheckRange(offset, 4); return (_data[offset] << 24) | (_data[offset + 1] << 16) | (_data[offset + 2] << 8) | _data[offset + 3]; } /// /// 读取 UInt32 值,大端字节序 | Read UInt32 value, Big-Endian /// /// 绝对字节地址 | Absolute byte address /// UInt32 值 | UInt32 value public uint GetUInt32(int address) { int offset = ToOffset(address); CheckRange(offset, 4); return (uint)((_data[offset] << 24) | (_data[offset + 1] << 16) | (_data[offset + 2] << 8) | _data[offset + 3]); } /// /// 读取 Float (Single) 值,大端字节序 | Read Float (Single) value, Big-Endian /// /// 绝对字节地址 | Absolute byte address /// Float 值 | Float value public float GetFloat(int address) { int offset = ToOffset(address); CheckRange(offset, 4); // S7 大端:需要反转为小端后转换 | S7 Big-Endian: reverse to Little-Endian for conversion byte[] temp = new byte[4]; temp[0] = _data[offset + 3]; temp[1] = _data[offset + 2]; temp[2] = _data[offset + 1]; temp[3] = _data[offset]; return BitConverter.ToSingle(temp, 0); } /// /// 读取 Double 值,大端字节序 | Read Double value, Big-Endian /// /// 绝对字节地址 | Absolute byte address /// Double 值 | Double value public double GetDouble(int address) { int offset = ToOffset(address); CheckRange(offset, 8); // S7 大端:需要反转为小端后转换 | S7 Big-Endian: reverse to Little-Endian for conversion byte[] temp = new byte[8]; for (int i = 0; i < 8; i++) temp[i] = _data[offset + 7 - i]; return BitConverter.ToDouble(temp, 0); } /// /// 读取 ASCII 字符串 | Read ASCII string /// /// 绝对字节地址 | Absolute byte address /// 字符串长度(字节数)| String length in bytes /// 字符串值 | String value public string GetString(int address, int length) { if (length <= 0) throw new ArgumentOutOfRangeException(nameof(length), $"字符串长度必须大于 0 | String length must be greater than 0"); int offset = ToOffset(address); CheckRange(offset, length); // 去除尾部空字符 | Trim trailing null characters return Encoding.ASCII.GetString(_data, offset, length).TrimEnd('\0'); } /// /// 读取指定范围的原始字节 | Read raw bytes from specified range /// /// 绝对字节地址 | Absolute byte address /// 字节数 | Number of bytes /// 字节数组 | Byte array public byte[] GetBytes(int address, int length) { int offset = ToOffset(address); CheckRange(offset, length); byte[] result = new byte[length]; Array.Copy(_data, offset, result, 0, length); return result; } } }