Skip to main content

rustbac_core/services/
write_property.rs

1use crate::apdu::ConfirmedRequestHeader;
2use crate::encoding::{
3    primitives::{
4        encode_closing_tag, encode_ctx_object_id, encode_ctx_unsigned, encode_opening_tag,
5    },
6    writer::Writer,
7};
8use crate::services::value_codec::encode_application_data_value;
9use crate::types::{DataValue, ObjectId, PropertyId};
10use crate::EncodeError;
11
12pub const SERVICE_WRITE_PROPERTY: u8 = 0x0F;
13
14#[derive(Debug, Clone, PartialEq)]
15pub struct WritePropertyRequest<'a> {
16    pub object_id: ObjectId,
17    pub property_id: PropertyId,
18    pub value: DataValue<'a>,
19    pub array_index: Option<u32>,
20    pub priority: Option<u8>,
21    pub invoke_id: u8,
22}
23
24impl<'a> Default for WritePropertyRequest<'a> {
25    fn default() -> Self {
26        Self {
27            object_id: ObjectId::new(crate::types::ObjectType::AnalogValue, 0),
28            property_id: PropertyId::PresentValue,
29            value: DataValue::Null,
30            array_index: None,
31            priority: None,
32            invoke_id: 1,
33        }
34    }
35}
36
37impl<'a> WritePropertyRequest<'a> {
38    pub fn encode(&self, w: &mut Writer<'_>) -> Result<(), EncodeError> {
39        ConfirmedRequestHeader {
40            segmented: false,
41            more_follows: false,
42            segmented_response_accepted: false,
43            max_segments: 0,
44            max_apdu: 5,
45            invoke_id: self.invoke_id,
46            sequence_number: None,
47            proposed_window_size: None,
48            service_choice: SERVICE_WRITE_PROPERTY,
49        }
50        .encode(w)?;
51
52        encode_ctx_object_id(w, 0, self.object_id.raw())?;
53        encode_ctx_unsigned(w, 1, self.property_id.to_u32())?;
54        if let Some(idx) = self.array_index {
55            encode_ctx_unsigned(w, 2, idx)?;
56        }
57
58        encode_opening_tag(w, 3)?;
59        encode_application_data_value(w, &self.value)?;
60        encode_closing_tag(w, 3)?;
61
62        if let Some(priority) = self.priority {
63            encode_ctx_unsigned(w, 4, priority as u32)?;
64        }
65        Ok(())
66    }
67}
68
69#[cfg(test)]
70mod tests {
71    use super::{WritePropertyRequest, SERVICE_WRITE_PROPERTY};
72    use crate::apdu::ConfirmedRequestHeader;
73    use crate::encoding::{reader::Reader, writer::Writer};
74    use crate::types::{DataValue, ObjectId, ObjectType, PropertyId};
75
76    #[test]
77    fn encode_write_property_with_character_string() {
78        let req = WritePropertyRequest {
79            object_id: ObjectId::new(ObjectType::AnalogValue, 3),
80            property_id: PropertyId::Description,
81            value: DataValue::CharacterString("loop tuning pending"),
82            priority: None,
83            ..Default::default()
84        };
85
86        let mut buf = [0u8; 256];
87        let mut w = Writer::new(&mut buf);
88        req.encode(&mut w).unwrap();
89
90        let mut r = Reader::new(w.as_written());
91        let hdr = ConfirmedRequestHeader::decode(&mut r).unwrap();
92        assert_eq!(hdr.service_choice, SERVICE_WRITE_PROPERTY);
93    }
94}