///| 工业现场总线:Modbus RTU 二进制物理帧与 CRC16 校验器
///|
/// 计算 Modbus RTU 16 位循环冗余校验码 (CRC-16/MODBUS, 多项式 0xA001)
pub fn crc16_modbus(bytes : Array[Int]) -> Int {
let mut crc = 0xFFFF
let mut i = 0
while i < bytes.length() {
let b = bytes[i] & 0xFF
crc = crc ^ b
let mut j = 0
while j < 8 {
if (crc & 0x0001) != 0 {
crc = (crc >> 1) ^ 0xA001
} else {
crc = crc >> 1
}
j = j + 1
}
i = i + 1
}
crc & 0xFFFF
}
///|
/// 将 Modbus 请求编码为带有 CRC16 校验的 RTU 物理字节帧
pub fn encode_rtu_request(
slave_id : Int,
fc : Int,
addr : Int,
qty_or_val : Int,
) -> Array[Int] {
let frame = [
slave_id & 0xFF,
fc & 0xFF,
(addr >> 8) & 0xFF,
addr & 0xFF,
(qty_or_val >> 8) & 0xFF,
qty_or_val & 0xFF,
]
let crc = crc16_modbus(frame)
frame.push(crc & 0xFF) // CRC 低位在前
frame.push((crc >> 8) & 0xFF) // CRC 高位在后
frame
}
///|
/// 解析 RTU 二进制数据帧 (校验长度与 CRC)
pub fn parse_rtu_frame(bytes : Array[Int]) -> ModbusRequest? {
if bytes.length() < 4 {
return None
}
// 校验 CRC
let payload_len = bytes.length() - 2
let payload = []
let mut i = 0
while i < payload_len {
payload.push(bytes[i])
i = i + 1
}
let calculated_crc = crc16_modbus(payload)
let received_crc = (bytes[payload_len] & 0xFF) |
((bytes[payload_len + 1] & 0xFF) << 8)
if calculated_crc != received_crc {
return None
}
let slave_id = bytes[0]
let fc_code = bytes[1]
let fc = ModbusFunction::from_int(fc_code)
let addr = ((bytes[2] & 0xFF) << 8) | (bytes[3] & 0xFF)
let qty = if bytes.length() >= 6 {
((bytes[4] & 0xFF) << 8) | (bytes[5] & 0xFF)
} else {
0
}
Some({
slave_id,
function_code: fc,
address: addr,
quantity: qty,
values: [],
})
}
///|
/// 将 ModbusResponse 编码为带有 CRC16 的二进制 RTU 响应帧
pub fn encode_rtu_response(resp : ModbusResponse) -> Array[Int] {
let frame = [resp.slave_id & 0xFF, resp.function_code.to_int() & 0xFF]
if resp.is_error {
// 异常响应: 功能码最高位置 1 (fc | 0x80)
frame[1] = (resp.function_code.to_int() | 0x80) & 0xFF
let ex_code = match resp.exception_code {
Some(ModbusException::IllegalFunction) => 1
Some(ModbusException::IllegalDataAddress) => 2
Some(ModbusException::IllegalDataValue) => 3
Some(ModbusException::SlaveDeviceFailure) => 4
None => 1
}
frame.push(ex_code)
} else {
frame.push(resp.byte_count & 0xFF)
for d in resp.data {
frame.push((d >> 8) & 0xFF)
frame.push(d & 0xFF)
}
}
let crc = crc16_modbus(frame)
frame.push(crc & 0xFF)
frame.push((crc >> 8) & 0xFF)
frame
}