///| 工业现场总线协议:Modbus 协议栈与 PLC 映像映射层
///|
/// Modbus 标准功能码定义
pub(all) enum ModbusFunction {
ReadCoils // 0x01: 读线圈状态 (映射 %QX)
ReadDiscreteInputs // 0x02: 读离散输入 (映射 %IX)
ReadHoldingRegisters // 0x03: 读保持寄存器 (映射 %MW)
ReadInputRegisters // 0x04: 读输入寄存器 (映射 %IW)
WriteSingleCoil // 0x05: 写单个线圈
WriteSingleRegister // 0x06: 写单个保持寄存器
WriteMultipleRegisters // 0x10: 写多个保持寄存器
Unknown(Int)
} derive(Debug, Eq)
///|
pub fn ModbusFunction::from_int(code : Int) -> ModbusFunction {
match code {
1 => ModbusFunction::ReadCoils
2 => ModbusFunction::ReadDiscreteInputs
3 => ModbusFunction::ReadHoldingRegisters
4 => ModbusFunction::ReadInputRegisters
5 => ModbusFunction::WriteSingleCoil
6 => ModbusFunction::WriteSingleRegister
16 => ModbusFunction::WriteMultipleRegisters
c => ModbusFunction::Unknown(c)
}
}
///|
pub fn ModbusFunction::to_int(self : ModbusFunction) -> Int {
match self {
ModbusFunction::ReadCoils => 1
ModbusFunction::ReadDiscreteInputs => 2
ModbusFunction::ReadHoldingRegisters => 3
ModbusFunction::ReadInputRegisters => 4
ModbusFunction::WriteSingleCoil => 5
ModbusFunction::WriteSingleRegister => 6
ModbusFunction::WriteMultipleRegisters => 16
ModbusFunction::Unknown(c) => c
}
}
///|
/// Modbus 异常码 (Exception Codes)
pub(all) enum ModbusException {
IllegalFunction // 0x01
IllegalDataAddress // 0x02
IllegalDataValue // 0x03
SlaveDeviceFailure // 0x04
} derive(Debug, Eq)
///|
/// Modbus 请求数据包 (PDU - Protocol Data Unit)
pub(all) struct ModbusRequest {
slave_id : Int
function_code : ModbusFunction
address : Int
quantity : Int
values : Array[Int]
} derive(Debug)
///|
pub fn ModbusRequest::new(
slave_id : Int,
fc : ModbusFunction,
address : Int,
quantity : Int,
) -> ModbusRequest {
{ slave_id, function_code: fc, address, quantity, values: [], }
}
///|
/// Modbus 响应数据包
pub(all) struct ModbusResponse {
slave_id : Int
function_code : ModbusFunction
is_error : Bool
exception_code : ModbusException?
byte_count : Int
data : Array[Int]
} derive(Debug)
///|
/// PLC Modbus 服务端适配器
pub(all) struct PlcModbusServer {
mem : PlcMemory
slave_id : Int
} derive(Debug)
///|
pub fn PlcModbusServer::new(mem : PlcMemory, slave_id : Int) -> PlcModbusServer {
{ mem, slave_id, }
}
///|
/// 处理 Modbus 请求并生成响应
pub fn PlcModbusServer::handle_request(
self : PlcModbusServer,
req : ModbusRequest,
) -> ModbusResponse {
// 从机地址不匹配
if req.slave_id != self.slave_id && req.slave_id != 0 {
return {
slave_id: req.slave_id,
function_code: req.function_code,
is_error: true,
exception_code: Some(ModbusException::SlaveDeviceFailure),
byte_count: 0,
data: [],
}
}
match req.function_code {
// 0x01: 读线圈 (输出 %QX)
ModbusFunction::ReadCoils => {
let res_bits = []
let mut i = 0
while i < req.quantity {
let addr_idx = req.address + i
let byte_offset = addr_idx / 8
let bit_offset = addr_idx % 8
let key = "%QX\{byte_offset}.\{bit_offset}"
let bit_val = self.mem.get_output_bool(key)
res_bits.push(if bit_val { 1 } else { 0 })
i = i + 1
}
{
slave_id: self.slave_id,
function_code: req.function_code,
is_error: false,
exception_code: None,
byte_count: (req.quantity + 7) / 8,
data: res_bits,
}
}
// 0x02: 读离散输入 (%IX)
ModbusFunction::ReadDiscreteInputs => {
let res_bits = []
let mut i = 0
while i < req.quantity {
let addr_idx = req.address + i
let byte_offset = addr_idx / 8
let bit_offset = addr_idx % 8
let key = "%IX\{byte_offset}.\{bit_offset}"
let addr = match parse_plc_address(key) {
Some(a) => a
None =>
{
area: PlcArea::Input,
size: PlcDataSize::Bit,
byte_offset,
bit_offset,
}
}
let bit_val = self.mem.get_address_val(addr).is_true()
res_bits.push(if bit_val { 1 } else { 0 })
i = i + 1
}
{
slave_id: self.slave_id,
function_code: req.function_code,
is_error: false,
exception_code: None,
byte_count: (req.quantity + 7) / 8,
data: res_bits,
}
}
// 0x03: 读保持寄存器 (%MW)
ModbusFunction::ReadHoldingRegisters => {
let res_words = []
let mut i = 0
while i < req.quantity {
let reg_addr = req.address + i
let key = "%MW\{reg_addr}"
let val = match self.mem.flags.get(key) {
Some(v) => v.to_int()
None => 0
}
res_words.push(val)
i = i + 1
}
{
slave_id: self.slave_id,
function_code: req.function_code,
is_error: false,
exception_code: None,
byte_count: req.quantity * 2,
data: res_words,
}
}
// 0x05: 写单个线圈
ModbusFunction::WriteSingleCoil => {
let byte_offset = req.address / 8
let bit_offset = req.address % 8
let is_on = req.quantity == 0xFF00 || req.quantity == 1
let key = "%QX\{byte_offset}.\{bit_offset}"
let addr = match parse_plc_address(key) {
Some(a) => a
None =>
{
area: PlcArea::Output,
size: PlcDataSize::Bit,
byte_offset,
bit_offset,
}
}
self.mem.set_address_val(addr, PlcValue::Bool(is_on))
{
slave_id: self.slave_id,
function_code: req.function_code,
is_error: false,
exception_code: None,
byte_count: 2,
data: [req.address, req.quantity],
}
}
// 0x06: 写单个保持寄存器
ModbusFunction::WriteSingleRegister => {
let key = "%MW\{req.address}"
let addr = match parse_plc_address(key) {
Some(a) => a
None =>
{
area: PlcArea::Memory,
size: PlcDataSize::Word,
byte_offset: req.address,
bit_offset: 0,
}
}
self.mem.set_address_val(addr, PlcValue::Int(req.quantity))
{
slave_id: self.slave_id,
function_code: req.function_code,
is_error: false,
exception_code: None,
byte_count: 2,
data: [req.address, req.quantity],
}
}
_ =>
{
slave_id: self.slave_id,
function_code: req.function_code,
is_error: true,
exception_code: Some(ModbusException::IllegalFunction),
byte_count: 0,
data: [],
}
}
}