use crate::errors::{ExceptionCode, MbusError};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[repr(u8)]
pub enum FunctionCode {
#[default]
Default = 0x00,
#[cfg(feature = "coils")]
ReadCoils = 0x01,
#[cfg(feature = "discrete-inputs")]
ReadDiscreteInputs = 0x02,
#[cfg(feature = "coils")]
WriteSingleCoil = 0x05,
#[cfg(feature = "coils")]
WriteMultipleCoils = 0x0F,
#[cfg(feature = "registers")]
ReadHoldingRegisters = 0x03,
#[cfg(feature = "registers")]
ReadInputRegisters = 0x04,
#[cfg(feature = "registers")]
WriteSingleRegister = 0x06,
#[cfg(feature = "registers")]
WriteMultipleRegisters = 0x10,
#[cfg(feature = "registers")]
MaskWriteRegister = 0x16,
#[cfg(feature = "registers")]
ReadWriteMultipleRegisters = 0x17,
#[cfg(feature = "fifo")]
ReadFifoQueue = 0x18,
#[cfg(feature = "file-record")]
ReadFileRecord = 0x14,
#[cfg(feature = "file-record")]
WriteFileRecord = 0x15,
#[cfg(feature = "diagnostics")]
ReadExceptionStatus = 0x07,
#[cfg(feature = "diagnostics")]
Diagnostics = 0x08,
#[cfg(feature = "diagnostics")]
GetCommEventCounter = 0x0B,
#[cfg(feature = "diagnostics")]
GetCommEventLog = 0x0C,
#[cfg(feature = "diagnostics")]
ReportServerId = 0x11,
#[cfg(feature = "diagnostics")]
EncapsulatedInterfaceTransport = 0x2B,
#[cfg(feature = "coils")]
ReadCoilsException = 0x81,
#[cfg(feature = "discrete-inputs")]
ReadDiscreteInputsException = 0x82,
#[cfg(feature = "registers")]
ReadHoldingRegistersException = 0x83,
#[cfg(feature = "registers")]
ReadInputRegistersException = 0x84,
#[cfg(feature = "coils")]
WriteSingleCoilException = 0x85,
#[cfg(feature = "registers")]
WriteSingleRegisterException = 0x86,
#[cfg(feature = "diagnostics")]
ReadExceptionStatusException = 0x87,
#[cfg(feature = "diagnostics")]
DiagnosticsException = 0x88,
#[cfg(feature = "diagnostics")]
GetCommEventCounterException = 0x8B,
#[cfg(feature = "diagnostics")]
GetCommEventLogException = 0x8C,
#[cfg(feature = "coils")]
WriteMultipleCoilsException = 0x8F,
#[cfg(feature = "registers")]
WriteMultipleRegistersException = 0x90,
#[cfg(feature = "diagnostics")]
ReportServerIdException = 0x91,
#[cfg(feature = "file-record")]
ReadFileRecordException = 0x94,
#[cfg(feature = "file-record")]
WriteFileRecordException = 0x95,
#[cfg(feature = "registers")]
MaskWriteRegisterException = 0x96,
#[cfg(feature = "registers")]
ReadWriteMultipleRegistersException = 0x97,
#[cfg(feature = "fifo")]
ReadFifoQueueException = 0x98,
#[cfg(feature = "diagnostics")]
EncapsulatedInterfaceTransportException = 0xAB,
}
impl TryFrom<u8> for FunctionCode {
type Error = MbusError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
use FunctionCode::*;
match value {
#[cfg(feature = "coils")]
0x01 => Ok(ReadCoils),
#[cfg(feature = "discrete-inputs")]
0x02 => Ok(ReadDiscreteInputs),
#[cfg(feature = "registers")]
0x03 => Ok(ReadHoldingRegisters),
#[cfg(feature = "registers")]
0x04 => Ok(ReadInputRegisters),
#[cfg(feature = "coils")]
0x05 => Ok(WriteSingleCoil),
#[cfg(feature = "registers")]
0x06 => Ok(WriteSingleRegister),
#[cfg(feature = "diagnostics")]
0x07 => Ok(ReadExceptionStatus),
#[cfg(feature = "diagnostics")]
0x08 => Ok(Diagnostics),
#[cfg(feature = "diagnostics")]
0x0B => Ok(GetCommEventCounter),
#[cfg(feature = "diagnostics")]
0x0C => Ok(GetCommEventLog),
#[cfg(feature = "coils")]
0x0F => Ok(WriteMultipleCoils),
#[cfg(feature = "registers")]
0x10 => Ok(WriteMultipleRegisters),
#[cfg(feature = "diagnostics")]
0x11 => Ok(ReportServerId),
#[cfg(feature = "file-record")]
0x14 => Ok(ReadFileRecord),
#[cfg(feature = "file-record")]
0x15 => Ok(WriteFileRecord),
#[cfg(feature = "registers")]
0x16 => Ok(MaskWriteRegister),
#[cfg(feature = "registers")]
0x17 => Ok(ReadWriteMultipleRegisters),
#[cfg(feature = "fifo")]
0x18 => Ok(ReadFifoQueue),
#[cfg(feature = "diagnostics")]
0x2B => Ok(EncapsulatedInterfaceTransport),
#[cfg(feature = "coils")]
0x81 => Ok(ReadCoilsException),
#[cfg(feature = "discrete-inputs")]
0x82 => Ok(ReadDiscreteInputsException),
#[cfg(feature = "registers")]
0x83 => Ok(ReadHoldingRegistersException),
#[cfg(feature = "registers")]
0x84 => Ok(ReadInputRegistersException),
#[cfg(feature = "coils")]
0x85 => Ok(WriteSingleCoilException),
#[cfg(feature = "registers")]
0x86 => Ok(WriteSingleRegisterException),
#[cfg(feature = "diagnostics")]
0x87 => Ok(ReadExceptionStatusException),
#[cfg(feature = "diagnostics")]
0x88 => Ok(DiagnosticsException),
#[cfg(feature = "diagnostics")]
0x8B => Ok(GetCommEventCounterException),
#[cfg(feature = "diagnostics")]
0x8C => Ok(GetCommEventLogException),
#[cfg(feature = "coils")]
0x8F => Ok(WriteMultipleCoilsException),
#[cfg(feature = "registers")]
0x90 => Ok(WriteMultipleRegistersException),
#[cfg(feature = "diagnostics")]
0x91 => Ok(ReportServerIdException),
#[cfg(feature = "file-record")]
0x94 => Ok(ReadFileRecordException),
#[cfg(feature = "file-record")]
0x95 => Ok(WriteFileRecordException),
#[cfg(feature = "registers")]
0x96 => Ok(MaskWriteRegisterException),
#[cfg(feature = "registers")]
0x97 => Ok(ReadWriteMultipleRegistersException),
#[cfg(feature = "fifo")]
0x98 => Ok(ReadFifoQueueException),
#[cfg(feature = "diagnostics")]
0xAB => Ok(EncapsulatedInterfaceTransportException),
_ => Err(MbusError::UnsupportedFunction(value)),
}
}
}
impl FunctionCode {
pub fn exception_code_for_error(&self, error: &MbusError) -> ExceptionCode {
match error {
MbusError::InvalidAddress | MbusError::InvalidOffset => {
ExceptionCode::IllegalDataAddress
}
MbusError::InvalidDataLen
| MbusError::ParseError
| MbusError::BasicParseError
| MbusError::InvalidPduLength => ExceptionCode::IllegalDataAddress,
MbusError::InvalidQuantity
| MbusError::InvalidValue
| MbusError::InvalidByteCount
| MbusError::InvalidAndMask
| MbusError::InvalidOrMask
| MbusError::InvalidDeviceIdCode => ExceptionCode::IllegalDataValue,
MbusError::InvalidFunctionCode
| MbusError::UnsupportedFunction(_)
| MbusError::ReservedSubFunction(_)
| MbusError::InvalidMeiType
| MbusError::BroadcastNotAllowed
| MbusError::InvalidBroadcastAddress => ExceptionCode::IllegalFunction,
_ => ExceptionCode::ServerDeviceFailure,
}
}
pub fn exception_response(&self) -> Option<FunctionCode> {
match self {
#[cfg(feature = "coils")]
FunctionCode::ReadCoils => Some(FunctionCode::ReadCoilsException),
#[cfg(feature = "discrete-inputs")]
FunctionCode::ReadDiscreteInputs => Some(FunctionCode::ReadDiscreteInputsException),
#[cfg(feature = "registers")]
FunctionCode::ReadHoldingRegisters => Some(FunctionCode::ReadHoldingRegistersException),
#[cfg(feature = "registers")]
FunctionCode::ReadInputRegisters => Some(FunctionCode::ReadInputRegistersException),
#[cfg(feature = "coils")]
FunctionCode::WriteSingleCoil => Some(FunctionCode::WriteSingleCoilException),
#[cfg(feature = "registers")]
FunctionCode::WriteSingleRegister => Some(FunctionCode::WriteSingleRegisterException),
#[cfg(feature = "diagnostics")]
FunctionCode::ReadExceptionStatus => Some(FunctionCode::ReadExceptionStatusException),
#[cfg(feature = "diagnostics")]
FunctionCode::Diagnostics => Some(FunctionCode::DiagnosticsException),
#[cfg(feature = "diagnostics")]
FunctionCode::GetCommEventCounter => Some(FunctionCode::GetCommEventCounterException),
#[cfg(feature = "diagnostics")]
FunctionCode::GetCommEventLog => Some(FunctionCode::GetCommEventLogException),
#[cfg(feature = "coils")]
FunctionCode::WriteMultipleCoils => Some(FunctionCode::WriteMultipleCoilsException),
#[cfg(feature = "registers")]
FunctionCode::WriteMultipleRegisters => {
Some(FunctionCode::WriteMultipleRegistersException)
}
#[cfg(feature = "diagnostics")]
FunctionCode::ReportServerId => Some(FunctionCode::ReportServerIdException),
#[cfg(feature = "file-record")]
FunctionCode::ReadFileRecord => Some(FunctionCode::ReadFileRecordException),
#[cfg(feature = "file-record")]
FunctionCode::WriteFileRecord => Some(FunctionCode::WriteFileRecordException),
#[cfg(feature = "registers")]
FunctionCode::MaskWriteRegister => Some(FunctionCode::MaskWriteRegisterException),
#[cfg(feature = "registers")]
FunctionCode::ReadWriteMultipleRegisters => {
Some(FunctionCode::ReadWriteMultipleRegistersException)
}
#[cfg(feature = "fifo")]
FunctionCode::ReadFifoQueue => Some(FunctionCode::ReadFifoQueueException),
#[cfg(feature = "diagnostics")]
FunctionCode::EncapsulatedInterfaceTransport => {
Some(FunctionCode::EncapsulatedInterfaceTransportException)
}
_ => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u16)]
pub enum DiagnosticSubFunction {
ReturnQueryData = 0x0000,
RestartCommunicationsOption = 0x0001,
ReturnDiagnosticRegister = 0x0002,
ChangeAsciiInputDelimiter = 0x0003,
ForceListenOnlyMode = 0x0004,
ClearCountersAndDiagnosticRegister = 0x000A,
ReturnBusMessageCount = 0x000B,
ReturnBusCommunicationErrorCount = 0x000C,
ReturnBusExceptionErrorCount = 0x000D,
ReturnServerMessageCount = 0x000E,
ReturnServerNoResponseCount = 0x000F,
ReturnServerNakCount = 0x0010,
ReturnServerBusyCount = 0x0011,
ReturnBusCharacterOverrunCount = 0x0012,
ClearOverrunCounterAndFlag = 0x0014,
}
impl DiagnosticSubFunction {
pub fn to_be_bytes(self) -> [u8; 2] {
(self as u16).to_be_bytes()
}
}
impl From<DiagnosticSubFunction> for u16 {
fn from(sub_func: DiagnosticSubFunction) -> Self {
sub_func as u16
}
}
impl TryFrom<u16> for DiagnosticSubFunction {
type Error = MbusError;
fn try_from(value: u16) -> Result<Self, Self::Error> {
use DiagnosticSubFunction::*;
match value {
0x0000 => Ok(ReturnQueryData),
0x0001 => Ok(RestartCommunicationsOption),
0x0002 => Ok(ReturnDiagnosticRegister),
0x0003 => Ok(ChangeAsciiInputDelimiter),
0x0004 => Ok(ForceListenOnlyMode),
0x000A => Ok(ClearCountersAndDiagnosticRegister),
0x000B => Ok(ReturnBusMessageCount),
0x000C => Ok(ReturnBusCommunicationErrorCount),
0x000D => Ok(ReturnBusExceptionErrorCount),
0x000E => Ok(ReturnServerMessageCount),
0x000F => Ok(ReturnServerNoResponseCount),
0x0010 => Ok(ReturnServerNakCount),
0x0011 => Ok(ReturnServerBusyCount),
0x0012 => Ok(ReturnBusCharacterOverrunCount),
0x0014 => Ok(ClearOverrunCounterAndFlag),
_ => Err(MbusError::ReservedSubFunction(value)),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[repr(u8)]
pub enum EncapsulatedInterfaceType {
#[default]
Err,
CanopenGeneralReference = 0x0D,
ReadDeviceIdentification = 0x0E,
}
impl From<EncapsulatedInterfaceType> for u8 {
fn from(val: EncapsulatedInterfaceType) -> Self {
val as u8
}
}
impl TryFrom<u8> for EncapsulatedInterfaceType {
type Error = MbusError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0x0D => Ok(Self::CanopenGeneralReference),
0x0E => Ok(Self::ReadDeviceIdentification),
_ => Err(MbusError::ReservedSubFunction(value as u16)),
}
}
}