rustbac-core 0.4.0

Core BACnet protocol types, encoders, and service codecs for rustbac.
Documentation
use crate::apdu::ConfirmedRequestHeader;
use crate::encoding::{
    primitives::{decode_unsigned, encode_ctx_object_id, encode_ctx_unsigned},
    reader::Reader,
    tag::Tag,
    writer::Writer,
};
use crate::services::value_codec::decode_application_data_value;
use crate::types::{DataValue, ObjectId, PropertyId};
use crate::{DecodeError, EncodeError};

pub const SERVICE_READ_PROPERTY: u8 = 0x0C;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ReadPropertyRequest {
    pub object_id: ObjectId,
    pub property_id: PropertyId,
    pub array_index: Option<u32>,
    pub invoke_id: u8,
}

impl ReadPropertyRequest {
    pub fn encode(&self, w: &mut Writer<'_>) -> Result<(), EncodeError> {
        ConfirmedRequestHeader {
            segmented: false,
            more_follows: false,
            segmented_response_accepted: true,
            max_segments: 0,
            max_apdu: 5,
            invoke_id: self.invoke_id,
            sequence_number: None,
            proposed_window_size: None,
            service_choice: SERVICE_READ_PROPERTY,
        }
        .encode(w)?;

        encode_ctx_object_id(w, 0, self.object_id.raw())?;
        encode_ctx_unsigned(w, 1, self.property_id.to_u32())?;
        if let Some(idx) = self.array_index {
            encode_ctx_unsigned(w, 2, idx)?;
        }
        Ok(())
    }
}

#[derive(Debug, Clone, PartialEq)]
pub struct ReadPropertyAck<'a> {
    pub object_id: ObjectId,
    pub property_id: PropertyId,
    pub array_index: Option<u32>,
    pub value: DataValue<'a>,
}

impl<'a> ReadPropertyAck<'a> {
    pub fn decode_after_header(r: &mut Reader<'a>) -> Result<Self, DecodeError> {
        let object_id = match Tag::decode(r)? {
            Tag::Context { tag_num: 0, len } => {
                ObjectId::from_raw(decode_unsigned(r, len as usize)?)
            }
            _ => return Err(DecodeError::InvalidTag),
        };

        let property_id = match Tag::decode(r)? {
            Tag::Context { tag_num: 1, len } => {
                PropertyId::from_u32(decode_unsigned(r, len as usize)?)
            }
            _ => return Err(DecodeError::InvalidTag),
        };

        let next = Tag::decode(r)?;
        let (array_index, value_start_tag) = match next {
            Tag::Context { tag_num: 2, len } => {
                let idx = decode_unsigned(r, len as usize)?;
                (Some(idx), Tag::decode(r)?)
            }
            other => (None, other),
        };

        if value_start_tag != (Tag::Opening { tag_num: 3 }) {
            return Err(DecodeError::InvalidTag);
        }

        let value = decode_application_data_value(r)?;

        match Tag::decode(r)? {
            Tag::Closing { tag_num: 3 } => {}
            _ => return Err(DecodeError::InvalidTag),
        }

        Ok(Self {
            object_id,
            property_id,
            array_index,
            value,
        })
    }
}