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, }
84
85impl Date {
86 pub const LEN: u32 = 4; 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; 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 }
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); writer.push(x.inner);
246 }
247 Self::LogBufferResult(x) => {
248 Tag::new(TagNumber::Application(ApplicationTagNumber::BitString), 2).encode(writer);
249 writer.push(0); 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); 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); writer.push(x.inner);
270 }
271 Self::LogBufferResult(x) => {
272 Tag::new(TagNumber::ContextSpecific(tag_num), 2).encode(writer);
273 writer.push(0); 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); 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; 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(); Tag::new(
446 TagNumber::Application(ApplicationTagNumber::CharacterString),
447 utf8_encoded.len() as u32 + 1, )
449 .encode(writer);
450 writer.push(0); 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 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(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}