use crate::context::SharedAddressSpace;
use crate::error::ModbusResult;
use crate::types::{RegisterConverter, WordOrder};
use super::ExceptionCode;
pub type HandlerResult = ModbusResult<Vec<u8>>;
#[derive(Clone)]
pub struct HandlerContext {
pub unit_id: u8,
pub registers: SharedAddressSpace,
pub transaction_id: u16,
pub word_order: WordOrder,
pub converter: RegisterConverter,
pub is_broadcast: bool,
}
impl HandlerContext {
pub fn new(unit_id: u8, registers: SharedAddressSpace, transaction_id: u16) -> Self {
let word_order = WordOrder::default();
Self {
unit_id,
registers,
transaction_id,
word_order,
converter: RegisterConverter::new(word_order),
is_broadcast: unit_id == 0,
}
}
pub fn with_word_order(
unit_id: u8,
registers: SharedAddressSpace,
transaction_id: u16,
word_order: WordOrder,
) -> Self {
Self {
unit_id,
registers,
transaction_id,
word_order,
converter: RegisterConverter::new(word_order),
is_broadcast: unit_id == 0,
}
}
pub fn broadcast(registers: SharedAddressSpace, transaction_id: u16) -> Self {
Self::new(0, registers, transaction_id)
}
#[inline]
pub fn converter(&self) -> &RegisterConverter {
&self.converter
}
#[inline]
pub fn is_broadcast(&self) -> bool {
self.is_broadcast
}
}
pub trait FunctionHandler: Send + Sync {
fn function_code(&self) -> u8;
fn handle(&self, pdu: &[u8], ctx: &HandlerContext) -> Result<Vec<u8>, ExceptionCode>;
fn name(&self) -> &'static str;
fn min_pdu_length(&self) -> usize {
1 }
fn supports_broadcast(&self) -> bool {
false
}
}
pub trait FunctionHandlerExt: FunctionHandler {
fn validate_pdu_length(&self, pdu: &[u8]) -> Result<(), ExceptionCode> {
if pdu.len() < self.min_pdu_length() {
return Err(ExceptionCode::IllegalDataValue);
}
Ok(())
}
fn parse_address(&self, pdu: &[u8], offset: usize) -> Result<u16, ExceptionCode> {
if pdu.len() < offset + 2 {
return Err(ExceptionCode::IllegalDataValue);
}
Ok(u16::from_be_bytes([pdu[offset], pdu[offset + 1]]))
}
fn parse_quantity(&self, pdu: &[u8], offset: usize) -> Result<u16, ExceptionCode> {
if pdu.len() < offset + 2 {
return Err(ExceptionCode::IllegalDataValue);
}
Ok(u16::from_be_bytes([pdu[offset], pdu[offset + 1]]))
}
}
impl<T: FunctionHandler + ?Sized> FunctionHandlerExt for T {}
#[cfg(test)]
mod tests {
use super::*;
struct TestHandler;
impl FunctionHandler for TestHandler {
fn function_code(&self) -> u8 {
0x99
}
fn handle(&self, _pdu: &[u8], _ctx: &HandlerContext) -> Result<Vec<u8>, ExceptionCode> {
Ok(vec![0x99])
}
fn name(&self) -> &'static str {
"Test Handler"
}
fn min_pdu_length(&self) -> usize {
5
}
}
#[test]
fn test_validate_pdu_length() {
let handler = TestHandler;
assert!(handler
.validate_pdu_length(&[0x99, 0x00, 0x00, 0x00, 0x00])
.is_ok());
assert!(handler
.validate_pdu_length(&[0x99, 0x00, 0x00, 0x00])
.is_err());
}
#[test]
fn test_parse_address() {
let handler = TestHandler;
let pdu = [0x99, 0x01, 0x00];
assert_eq!(handler.parse_address(&pdu, 1).unwrap(), 0x0100);
}
}