211 lines
8.8 KiB
C#
211 lines
8.8 KiB
C#
using System;
|
||
using System.Text;
|
||
|
||
namespace XP.Hardware.PLC.Helpers
|
||
{
|
||
/// <summary>
|
||
/// PLC 数据块解析器 | PLC data block parser
|
||
/// 将批量读取的 byte[] 按偏移量解析为各种数据类型,避免多次单点读取的通讯开销
|
||
/// S7 PLC 使用大端字节序(Big-Endian)
|
||
/// </summary>
|
||
public class PlcDataBlock
|
||
{
|
||
private readonly byte[] _data;
|
||
private readonly int _baseAddress;
|
||
|
||
/// <summary>
|
||
/// 原始字节数据 | Raw byte data
|
||
/// </summary>
|
||
public byte[] RawData => _data;
|
||
|
||
/// <summary>
|
||
/// 数据块起始地址 | Data block base address
|
||
/// </summary>
|
||
public int BaseAddress => _baseAddress;
|
||
|
||
/// <summary>
|
||
/// 数据长度(字节数)| Data length in bytes
|
||
/// </summary>
|
||
public int Length => _data?.Length ?? 0;
|
||
|
||
/// <summary>
|
||
/// 构造函数 | Constructor
|
||
/// </summary>
|
||
/// <param name="data">批量读取的字节数据 | Batch read byte data</param>
|
||
/// <param name="baseAddress">数据块起始地址 | Data block base address</param>
|
||
public PlcDataBlock(byte[] data, int baseAddress = 0)
|
||
{
|
||
_data = data ?? throw new ArgumentNullException(nameof(data));
|
||
_baseAddress = baseAddress;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取相对于 baseAddress 的实际偏移量 | Get actual offset relative to baseAddress
|
||
/// </summary>
|
||
/// <param name="absoluteAddress">绝对地址 | Absolute address</param>
|
||
/// <returns>数组内偏移量 | Array offset</returns>
|
||
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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 检查偏移量和长度是否在范围内 | Check if offset and length are within range
|
||
/// </summary>
|
||
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");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 读取 Bool 值(按位寻址)| Read Bool value (bit addressing)
|
||
/// </summary>
|
||
/// <param name="address">绝对字节地址 | Absolute byte address</param>
|
||
/// <param name="bitIndex">位索引 (0-7) | Bit index (0-7)</param>
|
||
/// <returns>布尔值 | Boolean value</returns>
|
||
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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 读取 Byte 值 | Read Byte value
|
||
/// </summary>
|
||
/// <param name="address">绝对字节地址 | Absolute byte address</param>
|
||
/// <returns>字节值 | Byte value</returns>
|
||
public byte GetByte(int address)
|
||
{
|
||
int offset = ToOffset(address);
|
||
return _data[offset];
|
||
}
|
||
|
||
/// <summary>
|
||
/// 读取 Int16 (Short) 值,大端字节序 | Read Int16 (Short) value, Big-Endian
|
||
/// </summary>
|
||
/// <param name="address">绝对字节地址 | Absolute byte address</param>
|
||
/// <returns>Int16 值 | Int16 value</returns>
|
||
public short GetInt16(int address)
|
||
{
|
||
int offset = ToOffset(address);
|
||
CheckRange(offset, 2);
|
||
return (short)((_data[offset] << 8) | _data[offset + 1]);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 读取 UInt16 (UShort) 值,大端字节序 | Read UInt16 (UShort) value, Big-Endian
|
||
/// </summary>
|
||
/// <param name="address">绝对字节地址 | Absolute byte address</param>
|
||
/// <returns>UInt16 值 | UInt16 value</returns>
|
||
public ushort GetUInt16(int address)
|
||
{
|
||
int offset = ToOffset(address);
|
||
CheckRange(offset, 2);
|
||
return (ushort)((_data[offset] << 8) | _data[offset + 1]);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 读取 Int32 (Int) 值,大端字节序 | Read Int32 (Int) value, Big-Endian
|
||
/// </summary>
|
||
/// <param name="address">绝对字节地址 | Absolute byte address</param>
|
||
/// <returns>Int32 值 | Int32 value</returns>
|
||
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];
|
||
}
|
||
|
||
/// <summary>
|
||
/// 读取 UInt32 值,大端字节序 | Read UInt32 value, Big-Endian
|
||
/// </summary>
|
||
/// <param name="address">绝对字节地址 | Absolute byte address</param>
|
||
/// <returns>UInt32 值 | UInt32 value</returns>
|
||
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]);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 读取 Float (Single) 值,大端字节序 | Read Float (Single) value, Big-Endian
|
||
/// </summary>
|
||
/// <param name="address">绝对字节地址 | Absolute byte address</param>
|
||
/// <returns>Float 值 | Float value</returns>
|
||
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);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 读取 Double 值,大端字节序 | Read Double value, Big-Endian
|
||
/// </summary>
|
||
/// <param name="address">绝对字节地址 | Absolute byte address</param>
|
||
/// <returns>Double 值 | Double value</returns>
|
||
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);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 读取 ASCII 字符串 | Read ASCII string
|
||
/// </summary>
|
||
/// <param name="address">绝对字节地址 | Absolute byte address</param>
|
||
/// <param name="length">字符串长度(字节数)| String length in bytes</param>
|
||
/// <returns>字符串值 | String value</returns>
|
||
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');
|
||
}
|
||
|
||
/// <summary>
|
||
/// 读取指定范围的原始字节 | Read raw bytes from specified range
|
||
/// </summary>
|
||
/// <param name="address">绝对字节地址 | Absolute byte address</param>
|
||
/// <param name="length">字节数 | Number of bytes</param>
|
||
/// <returns>字节数组 | Byte array</returns>
|
||
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;
|
||
}
|
||
}
|
||
}
|