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, }
78
79impl Date {
80 pub const LEN: u32 = 4; 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; 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 }
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); writer.push(x.inner);
201 }
202 Self::LogBufferResult(x) => {
203 Tag::new(TagNumber::Application(ApplicationTagNumber::BitString), 2).encode(writer);
204 writer.push(0); 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); 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); writer.push(x.inner);
225 }
226 Self::LogBufferResult(x) => {
227 Tag::new(TagNumber::ContextSpecific(tag_num), 2).encode(writer);
228 writer.push(0); 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); 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; 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(); Tag::new(
383 TagNumber::Application(ApplicationTagNumber::CharacterString),
384 utf8_encoded.len() as u32 + 1, )
386 .encode(writer);
387 writer.push(0); 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 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(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}