#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Function {
ReadCoils,
ReadDiscreteInputs,
ReadHoldingRegisters,
ReadInputRegisters,
WriteSingleCoil,
WriteSingleRegister,
WriteMultipleCoils,
WriteMultipleRegisters,
}
impl Function {
pub fn code(self) -> u8 {
match self {
Function::ReadCoils => 0x01,
Function::ReadDiscreteInputs => 0x02,
Function::ReadHoldingRegisters => 0x03,
Function::ReadInputRegisters => 0x04,
Function::WriteSingleCoil => 0x05,
Function::WriteSingleRegister => 0x06,
Function::WriteMultipleCoils => 0x0F,
Function::WriteMultipleRegisters => 0x10,
}
}
pub fn from_code(code: u8) -> Option<Function> {
match code {
0x01 => Some(Function::ReadCoils),
0x02 => Some(Function::ReadDiscreteInputs),
0x03 => Some(Function::ReadHoldingRegisters),
0x04 => Some(Function::ReadInputRegisters),
0x05 => Some(Function::WriteSingleCoil),
0x06 => Some(Function::WriteSingleRegister),
0x0F => Some(Function::WriteMultipleCoils),
0x10 => Some(Function::WriteMultipleRegisters),
_ => None,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Exception {
IllegalFunction,
IllegalDataAddress,
IllegalDataValue,
ServerDeviceFailure,
Acknowledge,
ServerDeviceBusy,
MemoryParityError,
GatewayPathUnavailable,
GatewayTargetFailedToRespond,
}
impl Exception {
pub fn code(self) -> u8 {
match self {
Exception::IllegalFunction => 0x01,
Exception::IllegalDataAddress => 0x02,
Exception::IllegalDataValue => 0x03,
Exception::ServerDeviceFailure => 0x04,
Exception::Acknowledge => 0x05,
Exception::ServerDeviceBusy => 0x06,
Exception::MemoryParityError => 0x08,
Exception::GatewayPathUnavailable => 0x0A,
Exception::GatewayTargetFailedToRespond => 0x0B,
}
}
pub fn from_code(code: u8) -> Option<Exception> {
match code {
0x01 => Some(Exception::IllegalFunction),
0x02 => Some(Exception::IllegalDataAddress),
0x03 => Some(Exception::IllegalDataValue),
0x04 => Some(Exception::ServerDeviceFailure),
0x05 => Some(Exception::Acknowledge),
0x06 => Some(Exception::ServerDeviceBusy),
0x08 => Some(Exception::MemoryParityError),
0x0A => Some(Exception::GatewayPathUnavailable),
0x0B => Some(Exception::GatewayTargetFailedToRespond),
_ => None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn function_codes_round_trip() {
for function in [
Function::ReadCoils,
Function::ReadDiscreteInputs,
Function::ReadHoldingRegisters,
Function::ReadInputRegisters,
Function::WriteSingleCoil,
Function::WriteSingleRegister,
Function::WriteMultipleCoils,
Function::WriteMultipleRegisters,
] {
assert_eq!(Function::from_code(function.code()), Some(function));
}
}
#[test]
fn an_exception_response_byte_is_not_a_function() {
assert_eq!(Function::from_code(0x83), None);
}
#[test]
fn exception_codes_round_trip() {
for exception in [
Exception::IllegalFunction,
Exception::IllegalDataAddress,
Exception::IllegalDataValue,
Exception::ServerDeviceFailure,
Exception::Acknowledge,
Exception::ServerDeviceBusy,
Exception::MemoryParityError,
Exception::GatewayPathUnavailable,
Exception::GatewayTargetFailedToRespond,
] {
assert_eq!(Exception::from_code(exception.code()), Some(exception));
}
}
#[test]
fn an_undefined_exception_code_is_none() {
assert_eq!(Exception::from_code(0x07), None);
}
}