embedded_bacnet/application_protocol/primitives/
data_value.rs

1use core::{fmt::Display, str::from_utf8};
2
3use crate::common::{
4    daily_schedule::WeeklySchedule,
5    error::Error,
6    helper::{decode_unsigned, encode_application_enumerated},
7    io::{Reader, Writer},
8    object_id::{ObjectId, ObjectType},
9    property_id::PropertyId,
10    spec::{
11        Binary, EngineeringUnits, EventState, LogBufferResult, LoggingType, NotifyType, Status,
12    },
13    tag::{ApplicationTagNumber, Tag, TagNumber},
14};
15
16#[derive(Debug, Clone)]
17#[cfg_attr(feature = "defmt", derive(defmt::Format))]
18pub enum ApplicationDataValue<'a> {
19    Boolean(bool),
20    Real(f32),
21    Double(f64),
22    Date(Date),
23    Time(Time),
24    ObjectId(ObjectId),
25    CharacterString(CharacterString<'a>),
26    Enumerated(Enumerated),
27    BitString(BitString<'a>),
28    UnsignedInt(u32),
29    WeeklySchedule(WeeklySchedule<'a>),
30}
31
32#[derive(Debug, Clone)]
33#[cfg_attr(feature = "defmt", derive(defmt::Format))]
34pub enum ApplicationDataValueWrite<'a> {
35    Boolean(bool),
36    Enumerated(Enumerated),
37    Real(f32),
38    WeeklySchedule(WeeklySchedule<'a>),
39}
40
41#[derive(Debug, Clone)]
42#[cfg_attr(feature = "defmt", derive(defmt::Format))]
43#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
44pub enum Enumerated {
45    Units(EngineeringUnits),
46    Binary(Binary),
47    ObjectType(ObjectType),
48    EventState(EventState),
49    NotifyType(NotifyType),
50    LoggingType(LoggingType),
51    Unknown(u32),
52}
53
54impl Enumerated {
55    pub fn encode(&self, writer: &mut Writer) {
56        let value = match self {
57            Self::Units(x) => x.clone() as u32,
58            Self::Binary(x) => x.clone() as u32,
59            Self::ObjectType(x) => *x as u32,
60            Self::EventState(x) => x.clone() as u32,
61            Self::NotifyType(x) => x.clone() as u32,
62            Self::LoggingType(x) => x.clone() as u32,
63            Self::Unknown(x) => *x,
64        };
65        encode_application_enumerated(writer, value);
66    }
67}
68
69#[derive(Debug, Clone)]
70#[cfg_attr(feature = "defmt", derive(defmt::Format))]
71#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
72pub struct Date {
73    pub year: u16,
74    pub month: u8,
75    pub day: u8,
76    pub wday: u8, // 1 (Monday) to 7 (Sunday)
77}
78
79impl Date {
80    pub const LEN: u32 = 4; // 4 bytes
81
82    //  year = years since 1900, wildcard=1900+255
83    //  month 1=Jan
84    //  day = day of month
85    //  wday 1=Monday...7=Sunday
86    pub fn decode_from_tag(tag: &Tag) -> Self {
87        let value = tag.value;
88        let value = value.to_be_bytes();
89        Self::decode_inner(value)
90    }
91
92    pub fn decode(reader: &mut Reader, buf: &[u8]) -> Result<Self, Error> {
93        let value = reader.read_bytes(buf)?;
94        Ok(Self::decode_inner(value))
95    }
96
97    fn decode_inner(value: [u8; 4]) -> Self {
98        let year = value[0] as u16 + 1900;
99        let month = value[1];
100        let day = value[2];
101        let wday = value[3];
102        Self {
103            year,
104            month,
105            day,
106            wday,
107        }
108    }
109
110    pub fn encode(&self, writer: &mut Writer) {
111        let year = (self.year - 1900) as u8;
112        writer.push(year);
113        writer.push(self.month);
114        writer.push(self.day);
115        writer.push(self.wday);
116    }
117}
118
119#[derive(Debug, Clone)]
120#[cfg_attr(feature = "defmt", derive(defmt::Format))]
121#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
122pub struct Time {
123    pub hour: u8,
124    pub minute: u8,
125    pub second: u8,
126    pub hundredths: u8,
127}
128
129impl Time {
130    pub const LEN: u32 = 4; // 4 bytes
131
132    // assuming that this comes from a Time tag
133    pub fn decode(reader: &mut Reader, buf: &[u8]) -> Result<Self, Error> {
134        let hour = reader.read_byte(buf)?;
135        let minute = reader.read_byte(buf)?;
136        let second = reader.read_byte(buf)?;
137        let hundredths = reader.read_byte(buf)?;
138        Ok(Time {
139            hour,
140            minute,
141            second,
142            hundredths,
143        })
144    }
145
146    pub fn encode(&self, writer: &mut Writer) {
147        writer.push(self.hour);
148        writer.push(self.minute);
149        writer.push(self.second);
150        writer.push(self.hundredths);
151    }
152}
153
154#[derive(Debug, Clone)]
155#[cfg_attr(feature = "defmt", derive(defmt::Format))]
156#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
157pub struct CharacterString<'a> {
158    pub inner: &'a str,
159}
160
161impl<'a> Display for ApplicationDataValue<'a> {
162    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
163        match self {
164            ApplicationDataValue::Real(x) => write!(f, "{}", x),
165            ApplicationDataValue::Double(x) => write!(f, "{}", x),
166            ApplicationDataValue::CharacterString(x) => write!(f, "{}", &x.inner),
167            ApplicationDataValue::Boolean(x) => write!(f, "{}", x),
168            x => write!(f, "{:?}", x),
169        }
170    }
171}
172
173#[derive(Debug, Clone)]
174pub enum BitString<'a> {
175    Status(Status),
176    LogBufferResult(LogBufferResult),
177    Custom(CustomBitStream<'a>),
178}
179
180#[cfg(feature = "defmt")]
181impl<'a> defmt::Format for BitString<'a> {
182    fn format(&self, _fmt: defmt::Formatter) {
183        // do nothing for now because it is too complicated due to StatusFlags
184    }
185}
186
187#[derive(Debug, Clone)]
188#[cfg_attr(feature = "defmt", derive(defmt::Format))]
189pub struct CustomBitStream<'a> {
190    pub unused_bits: u8,
191    pub bits: &'a [u8],
192}
193
194impl<'a> BitString<'a> {
195    pub fn encode_application(&self, writer: &mut Writer) {
196        match self {
197            Self::Status(x) => {
198                Tag::new(TagNumber::Application(ApplicationTagNumber::BitString), 2).encode(writer);
199                writer.push(0); // no unused bits
200                writer.push(x.inner);
201            }
202            Self::LogBufferResult(x) => {
203                Tag::new(TagNumber::Application(ApplicationTagNumber::BitString), 2).encode(writer);
204                writer.push(0); // no unused bits
205                writer.push(x.inner);
206            }
207            Self::Custom(x) => {
208                Tag::new(
209                    TagNumber::Application(ApplicationTagNumber::BitString),
210                    x.bits.len() as u32 + 1,
211                )
212                .encode(writer);
213                writer.push(0); // no unused bits
214                writer.extend_from_slice(x.bits);
215            }
216        }
217    }
218
219    pub fn encode_context(&self, tag_num: u8, writer: &mut Writer) {
220        match self {
221            Self::Status(x) => {
222                Tag::new(TagNumber::ContextSpecific(tag_num), 2).encode(writer);
223                writer.push(0); // no unused bits
224                writer.push(x.inner);
225            }
226            Self::LogBufferResult(x) => {
227                Tag::new(TagNumber::ContextSpecific(tag_num), 2).encode(writer);
228                writer.push(0); // no unused bits
229                writer.push(x.inner);
230            }
231            Self::Custom(x) => {
232                Tag::new(TagNumber::ContextSpecific(tag_num), x.bits.len() as u32 + 1)
233                    .encode(writer);
234                writer.push(0); // no unused bits
235                writer.extend_from_slice(x.bits);
236            }
237        }
238    }
239
240    pub fn decode(
241        property_id: &PropertyId,
242        len: u32,
243        reader: &mut Reader,
244        buf: &'a [u8],
245    ) -> Result<Self, Error> {
246        let unused_bits = reader.read_byte(buf)?;
247        match property_id {
248            PropertyId::PropStatusFlags => {
249                let status_flags = Status::new(reader.read_byte(buf)?);
250                Ok(Self::Status(status_flags))
251            }
252            PropertyId::PropLogBuffer => {
253                let flags = LogBufferResult::new(reader.read_byte(buf)?);
254                Ok(Self::LogBufferResult(flags))
255            }
256            _ => {
257                let len = (len - 1) as usize; // we have already read a byte
258                let bits = reader.read_slice(len, buf)?;
259                Ok(Self::Custom(CustomBitStream { unused_bits, bits }))
260            }
261        }
262    }
263}
264
265impl<'a> CharacterString<'a> {
266    pub fn decode(len: u32, reader: &mut Reader, buf: &'a [u8]) -> Result<Self, Error> {
267        let character_set = reader.read_byte(buf)?;
268        if character_set != 0 {
269            unimplemented!("non-utf8 characterset not supported")
270        }
271        let slice = reader.read_slice(len as usize - 1, buf)?;
272        let inner = from_utf8(slice).map_err(|_| {
273            Error::InvalidValue("CharacterString bytes are not a valid utf8 string")
274        })?;
275
276        Ok(CharacterString { inner })
277    }
278}
279
280impl<'a> ApplicationDataValueWrite<'a> {
281    pub fn decode(
282        object_id: &ObjectId,
283        property_id: &PropertyId,
284        reader: &mut Reader,
285        buf: &'a [u8],
286    ) -> Result<Self, Error> {
287        match property_id {
288            PropertyId::PropWeeklySchedule => {
289                let weekly_schedule = WeeklySchedule::decode(reader, buf)?;
290                Ok(Self::WeeklySchedule(weekly_schedule))
291            }
292            _ => {
293                let tag = Tag::decode(reader, buf)?;
294                match tag.number {
295                    TagNumber::Application(ApplicationTagNumber::Boolean) => {
296                        Ok(Self::Boolean(tag.value > 0))
297                    }
298                    TagNumber::Application(ApplicationTagNumber::Real) => {
299                        if tag.value != 4 {
300                            return Err(Error::Length((
301                                "real tag should have length of 4",
302                                tag.value,
303                            )));
304                        }
305                        let bytes = reader.read_bytes(buf)?;
306                        Ok(Self::Real(f32::from_be_bytes(bytes)))
307                    }
308                    TagNumber::Application(ApplicationTagNumber::Enumerated) => {
309                        let value = decode_enumerated(object_id, property_id, &tag, reader, buf)?;
310                        Ok(Self::Enumerated(value))
311                    }
312                    tag_number => Err(Error::TagNotSupported((
313                        "ApplicationDataValueWrite decode",
314                        tag_number,
315                    ))),
316                }
317            }
318        }
319    }
320
321    pub fn encode(&self, writer: &mut Writer) {
322        match self {
323            Self::Boolean(x) => {
324                let len = 1;
325                let tag = Tag::new(TagNumber::Application(ApplicationTagNumber::Boolean), len);
326                tag.encode(writer);
327                let value = if *x { 1_u8 } else { 0_u8 };
328                writer.push(value)
329            }
330            Self::Real(x) => {
331                let len = 4;
332                let tag = Tag::new(TagNumber::Application(ApplicationTagNumber::Real), len);
333                tag.encode(writer);
334                writer.extend_from_slice(&f32::to_be_bytes(*x))
335            }
336            Self::Enumerated(x) => {
337                x.encode(writer);
338            }
339            Self::WeeklySchedule(x) => x.encode(writer),
340        }
341    }
342}
343
344impl<'a> ApplicationDataValue<'a> {
345    pub fn encode(&self, writer: &mut Writer) {
346        match self {
347            ApplicationDataValue::Boolean(x) => Tag::new(
348                TagNumber::Application(ApplicationTagNumber::Boolean),
349                if *x { 1 } else { 0 },
350            )
351            .encode(writer),
352            ApplicationDataValue::Real(x) => {
353                Tag::new(TagNumber::Application(ApplicationTagNumber::Real), 4).encode(writer);
354                writer.extend_from_slice(&x.to_be_bytes());
355            }
356            ApplicationDataValue::Date(x) => {
357                Tag::new(
358                    TagNumber::Application(ApplicationTagNumber::Date),
359                    Date::LEN,
360                )
361                .encode(writer);
362                x.encode(writer);
363            }
364            ApplicationDataValue::Time(x) => {
365                Tag::new(
366                    TagNumber::Application(ApplicationTagNumber::Time),
367                    Time::LEN,
368                )
369                .encode(writer);
370                x.encode(writer);
371            }
372            ApplicationDataValue::ObjectId(x) => {
373                Tag::new(
374                    TagNumber::Application(ApplicationTagNumber::ObjectId),
375                    ObjectId::LEN,
376                )
377                .encode(writer);
378                x.encode(writer);
379            }
380            ApplicationDataValue::CharacterString(x) => {
381                let utf8_encoded = x.inner.as_bytes(); // strings in rust are utf8 encoded already
382                Tag::new(
383                    TagNumber::Application(ApplicationTagNumber::CharacterString),
384                    utf8_encoded.len() as u32 + 1, // keep space for encoding byte
385                )
386                .encode(writer);
387                writer.push(0); // utf8 encoding
388                writer.extend_from_slice(utf8_encoded);
389            }
390            ApplicationDataValue::Enumerated(x) => {
391                x.encode(writer);
392            }
393            ApplicationDataValue::BitString(x) => {
394                x.encode_application(writer);
395            }
396            ApplicationDataValue::UnsignedInt(x) => {
397                Tag::new(TagNumber::Application(ApplicationTagNumber::UnsignedInt), 4)
398                    .encode(writer);
399                writer.extend_from_slice(&x.to_be_bytes());
400            }
401            ApplicationDataValue::WeeklySchedule(x) => {
402                // no application tag required for weekly schedule
403                x.encode(writer);
404            }
405
406            x => todo!("{:?}", x),
407        };
408    }
409
410    pub fn decode(
411        tag: &Tag,
412        object_id: &ObjectId,
413        property_id: &PropertyId,
414        reader: &mut Reader,
415        buf: &'a [u8],
416    ) -> Result<Self, Error> {
417        let tag_num = match &tag.number {
418            TagNumber::Application(x) => x,
419            unknown => {
420                return Err(Error::TagNotSupported((
421                    "Expected Application tag",
422                    unknown.clone(),
423                )))
424            }
425        };
426
427        match tag_num {
428            ApplicationTagNumber::Real => {
429                if tag.value != 4 {
430                    return Err(Error::Length((
431                        "real tag should have length of 4",
432                        tag.value,
433                    )));
434                }
435                Ok(ApplicationDataValue::Real(f32::from_be_bytes(
436                    reader.read_bytes(buf)?,
437                )))
438            }
439            ApplicationTagNumber::ObjectId => {
440                let object_id = ObjectId::decode(tag.value, reader, buf)?;
441                Ok(ApplicationDataValue::ObjectId(object_id))
442            }
443            ApplicationTagNumber::CharacterString => {
444                let text = CharacterString::decode(tag.value, reader, buf)?;
445                Ok(ApplicationDataValue::CharacterString(text))
446            }
447            ApplicationTagNumber::Enumerated => {
448                let value = decode_enumerated(object_id, property_id, tag, reader, buf)?;
449                Ok(ApplicationDataValue::Enumerated(value))
450            }
451            ApplicationTagNumber::BitString => {
452                let bit_string = BitString::decode(property_id, tag.value, reader, buf)?;
453                Ok(ApplicationDataValue::BitString(bit_string))
454            }
455            ApplicationTagNumber::Boolean => {
456                let value = tag.value > 0;
457                Ok(ApplicationDataValue::Boolean(value))
458            }
459            ApplicationTagNumber::UnsignedInt => {
460                let value = decode_unsigned(tag.value, reader, buf)? as u32;
461                Ok(ApplicationDataValue::UnsignedInt(value))
462            }
463            ApplicationTagNumber::Time => {
464                if tag.value != 4 {
465                    return Err(Error::Length((
466                        "time tag should have length of 4",
467                        tag.value,
468                    )));
469                }
470                let time = Time::decode(reader, buf)?;
471                Ok(ApplicationDataValue::Time(time))
472            }
473            ApplicationTagNumber::Date => {
474                // let date = Date::decode_from_tag(&tag);
475                let date = Date::decode(reader, buf)?;
476                Ok(ApplicationDataValue::Date(date))
477            }
478
479            x => Err(Error::TagNotSupported((
480                "ApplicationDataValue decode",
481                TagNumber::Application(x.clone()),
482            ))),
483        }
484    }
485}
486
487fn decode_enumerated(
488    object_id: &ObjectId,
489    property_id: &PropertyId,
490    tag: &Tag,
491    reader: &mut Reader,
492    buf: &[u8],
493) -> Result<Enumerated, Error> {
494    let value = decode_unsigned(tag.value, reader, buf)? as u32;
495    match property_id {
496        PropertyId::PropUnits => {
497            let units = value
498                .try_into()
499                .map_err(|x| Error::InvalidVariant(("EngineeringUnits", x)))?;
500            Ok(Enumerated::Units(units))
501        }
502        PropertyId::PropPresentValue => match object_id.object_type {
503            ObjectType::ObjectBinaryInput
504            | ObjectType::ObjectBinaryOutput
505            | ObjectType::ObjectBinaryValue => {
506                let binary = value
507                    .try_into()
508                    .map_err(|x| Error::InvalidVariant(("Binary", x)))?;
509                Ok(Enumerated::Binary(binary))
510            }
511            _ => Ok(Enumerated::Unknown(value)),
512        },
513        PropertyId::PropObjectType => {
514            let object_type = ObjectType::try_from(value)
515                .map_err(|x| Error::InvalidVariant(("ObjectType", x)))?;
516            Ok(Enumerated::ObjectType(object_type))
517        }
518        PropertyId::PropEventState => {
519            let event_state = EventState::try_from(value)
520                .map_err(|x| Error::InvalidVariant(("EventState", x)))?;
521            Ok(Enumerated::EventState(event_state))
522        }
523        PropertyId::PropNotifyType => {
524            let notify_type = NotifyType::try_from(value)
525                .map_err(|x| Error::InvalidVariant(("NotifyType", x)))?;
526            Ok(Enumerated::NotifyType(notify_type))
527        }
528        PropertyId::PropLoggingType => {
529            let logging_type = LoggingType::try_from(value)
530                .map_err(|x| Error::InvalidVariant(("LoggingType", x)))?;
531            Ok(Enumerated::LoggingType(logging_type))
532        }
533
534        _ => Ok(Enumerated::Unknown(value)),
535    }
536}