Files
XplorePlane/XP.Hardware.PLC/Helpers/PlcDataBlock.cs
T

211 lines
8.8 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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;
}
}
}