use crate::{data_unit::common::MAX_PDU_DATA_LEN, errors::MbusError};
use core::fmt;
use heapless::Vec;
#[derive(Debug, Clone, PartialEq)]
pub struct DeviceIdObject {
pub object_id: ObjectId,
pub value: Vec<u8, MAX_PDU_DATA_LEN>,
}
pub struct DeviceIdObjectIterator<'a> {
pub(crate) data: &'a [u8],
offset: usize,
count: u8,
total: u8,
}
impl<'a> Iterator for DeviceIdObjectIterator<'a> {
type Item = Result<DeviceIdObject, MbusError>;
fn next(&mut self) -> Option<Self::Item> {
if self.count >= self.total {
return None;
}
self.parse_next()
}
}
impl<'a> DeviceIdObjectIterator<'a> {
fn parse_next(&mut self) -> Option<Result<DeviceIdObject, MbusError>> {
if self.offset + 2 > self.data.len() {
return Some(Err(MbusError::InvalidPduLength));
}
let obj_id = ObjectId::from(self.data[self.offset]);
let obj_len = self.data[self.offset + 1] as usize;
self.offset += 2;
if self.offset + obj_len > self.data.len() {
return Some(Err(MbusError::InvalidPduLength));
}
let mut value = Vec::new();
if value
.extend_from_slice(&self.data[self.offset..self.offset + obj_len])
.is_err()
{
return Some(Err(MbusError::BufferTooSmall));
}
self.offset += obj_len;
self.count += 1;
Some(Ok(DeviceIdObject {
object_id: obj_id,
value,
}))
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct DeviceIdentificationResponse {
pub read_device_id_code: ReadDeviceIdCode,
pub conformity_level: ConformityLevel,
pub more_follows: bool,
pub next_object_id: ObjectId,
pub objects_data: [u8; MAX_PDU_DATA_LEN],
pub number_of_objects: u8,
}
impl DeviceIdentificationResponse {
pub fn objects(&self) -> DeviceIdObjectIterator<'_> {
DeviceIdObjectIterator {
data: &self.objects_data,
offset: 0,
count: 0,
total: self.number_of_objects,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum BasicObjectId {
VendorName = 0x00,
ProductCode = 0x01,
MajorMinorRevision = 0x02,
}
impl TryFrom<u8> for BasicObjectId {
type Error = MbusError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0x00 => Ok(BasicObjectId::VendorName),
0x01 => Ok(BasicObjectId::ProductCode),
0x02 => Ok(BasicObjectId::MajorMinorRevision),
_ => Err(MbusError::InvalidAddress),
}
}
}
impl fmt::Display for BasicObjectId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
BasicObjectId::VendorName => write!(f, "VendorName"),
BasicObjectId::ProductCode => write!(f, "ProductCode"),
BasicObjectId::MajorMinorRevision => write!(f, "MajorMinorRevision"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum RegularObjectId {
VendorUrl = 0x03,
ProductName = 0x04,
ModelName = 0x05,
UserApplicationName = 0x06,
}
impl TryFrom<u8> for RegularObjectId {
type Error = MbusError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0x03 => Ok(RegularObjectId::VendorUrl),
0x04 => Ok(RegularObjectId::ProductName),
0x05 => Ok(RegularObjectId::ModelName),
0x06 => Ok(RegularObjectId::UserApplicationName),
_ => Err(MbusError::InvalidAddress),
}
}
}
impl fmt::Display for RegularObjectId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
RegularObjectId::VendorUrl => write!(f, "VendorUrl"),
RegularObjectId::ProductName => write!(f, "ProductName"),
RegularObjectId::ModelName => write!(f, "ModelName"),
RegularObjectId::UserApplicationName => write!(f, "UserApplicationName"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ExtendedObjectId(u8);
impl ExtendedObjectId {
pub fn new(id: u8) -> Option<Self> {
if (0x80..=0xFF).contains(&id) {
Some(Self(id))
} else {
None
}
}
pub fn value(&self) -> u8 {
self.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[repr(u8)]
pub enum ReadDeviceIdCode {
#[default]
Err,
Basic = 0x01,
Regular = 0x02,
Extended = 0x03,
Specific = 0x04,
}
impl TryFrom<u8> for ReadDeviceIdCode {
type Error = MbusError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0x01 => Ok(ReadDeviceIdCode::Basic),
0x02 => Ok(ReadDeviceIdCode::Regular),
0x03 => Ok(ReadDeviceIdCode::Extended),
0x04 => Ok(ReadDeviceIdCode::Specific),
_ => Err(MbusError::InvalidDeviceIdCode),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum ConformityLevel {
BasicStreamOnly = 0x01,
RegularStreamOnly = 0x02,
ExtendedStreamOnly = 0x03,
BasicStreamAndIndividual = 0x81,
RegularStreamAndIndividual = 0x82,
ExtendedStreamAndIndividual = 0x83,
}
impl TryFrom<u8> for ConformityLevel {
type Error = MbusError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0x01 => Ok(ConformityLevel::BasicStreamOnly),
0x02 => Ok(ConformityLevel::RegularStreamOnly),
0x03 => Ok(ConformityLevel::ExtendedStreamOnly),
0x81 => Ok(ConformityLevel::BasicStreamAndIndividual),
0x82 => Ok(ConformityLevel::RegularStreamAndIndividual),
0x83 => Ok(ConformityLevel::ExtendedStreamAndIndividual),
_ => Err(MbusError::ParseError),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum ObjectId {
#[default]
Err,
Basic(BasicObjectId),
Regular(RegularObjectId),
Extended(ExtendedObjectId),
Reserved(u8),
}
impl From<u8> for ObjectId {
fn from(id: u8) -> Self {
if let Ok(basic) = BasicObjectId::try_from(id) {
ObjectId::Basic(basic)
} else if let Ok(regular) = RegularObjectId::try_from(id) {
ObjectId::Regular(regular)
} else if let Some(extended) = ExtendedObjectId::new(id) {
ObjectId::Extended(extended)
} else {
ObjectId::Reserved(id)
}
}
}
impl fmt::Display for ObjectId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ObjectId::Basic(id) => write!(f, "Basic({})", id),
ObjectId::Regular(id) => write!(f, "Regular({})", id),
ObjectId::Extended(id) => write!(f, "Extended({:#04X})", id.value()),
ObjectId::Reserved(id) => write!(f, "Reserved({:#04X})", id),
ObjectId::Err => write!(f, "Err (sentinel default)"),
}
}
}
impl From<ObjectId> for u8 {
fn from(oid: ObjectId) -> u8 {
match oid {
ObjectId::Basic(id) => id as u8,
ObjectId::Regular(id) => id as u8,
ObjectId::Extended(id) => id.value(),
ObjectId::Reserved(id) => id,
ObjectId::Err => 0, }
}
}