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