use rusty_modbus_types::{DeviceIdCode, FunctionCode, MeiType};
use crate::error::{DecodeError, EncodeError};
use crate::request::Encode;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ReadDeviceIdentificationRequest {
pub device_id_code: DeviceIdCode,
pub object_id: u8,
}
impl ReadDeviceIdentificationRequest {
pub fn decode(data: &[u8]) -> Result<Self, DecodeError> {
DecodeError::check_exact_len(data, 3)?;
if data[0] != MeiType::ReadDeviceIdentification.code() {
return Err(DecodeError::UnknownMeiType(data[0]));
}
let device_id_code =
DeviceIdCode::from_raw(data[1]).ok_or(DecodeError::InvalidDeviceIdCode(data[1]))?;
let object_id = data[2];
Ok(Self {
device_id_code,
object_id,
})
}
}
impl Encode for ReadDeviceIdentificationRequest {
fn encode_into(&self, buf: &mut [u8]) -> Result<usize, EncodeError> {
let len = self.encoded_len();
if buf.len() < len {
return Err(EncodeError::BufferTooSmall {
required: len,
available: buf.len(),
});
}
EncodeError::check_pdu_len(len)?;
buf[0] = FunctionCode::EncapsulatedInterfaceTransport.code();
buf[1] = MeiType::ReadDeviceIdentification.code();
buf[2] = self.device_id_code.code();
buf[3] = self.object_id;
Ok(len)
}
fn encoded_len(&self) -> usize {
4 }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn encode_basic_stream_request() {
let req = ReadDeviceIdentificationRequest {
device_id_code: DeviceIdCode::BasicStream,
object_id: 0x00,
};
let mut buf = [0u8; 5];
let len = req.encode_into(&mut buf).unwrap();
assert_eq!(&buf[..len], &[0x2B, 0x0E, 0x01, 0x00]);
}
#[test]
fn decode_basic_stream_request() {
let data = [0x0E, 0x01, 0x00];
let req = ReadDeviceIdentificationRequest::decode(&data).unwrap();
assert_eq!(req.device_id_code, DeviceIdCode::BasicStream);
assert_eq!(req.object_id, 0x00);
}
#[test]
fn decode_individual_request() {
let data = [0x0E, 0x04, 0x03];
let req = ReadDeviceIdentificationRequest::decode(&data).unwrap();
assert_eq!(req.device_id_code, DeviceIdCode::Individual);
assert_eq!(req.object_id, 0x03);
}
#[test]
fn decode_wrong_mei_type() {
let data = [0x0D, 0x01, 0x00];
assert!(matches!(
ReadDeviceIdentificationRequest::decode(&data),
Err(DecodeError::UnknownMeiType(0x0D))
));
}
#[test]
fn decode_invalid_device_id_code() {
let data = [0x0E, 0xFF, 0x00];
assert!(matches!(
ReadDeviceIdentificationRequest::decode(&data),
Err(DecodeError::InvalidDeviceIdCode(0xFF))
));
}
#[test]
fn decode_truncated() {
let data = [0x0E, 0x01];
assert!(matches!(
ReadDeviceIdentificationRequest::decode(&data),
Err(DecodeError::Truncated { .. })
));
}
}