1use crate::error::{DecodeError, DecodeResult};
15use crate::spvd_decode::{DecodedValue, FieldType, PvdDecoder, StructureDesc};
16
17#[derive(Debug, Clone)]
19pub struct MonitorUpdate {
20 pub value: DecodedValue,
21 pub changed: Vec<u8>,
23 pub overrun: Vec<u8>,
26 pub consumed: usize,
28 pub paths: Vec<String>,
34}
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq)]
38pub enum MonitorLayout {
39 SpecOrder,
41 OverrunBeforeData,
43 ChangedOnly,
45}
46
47impl MonitorUpdate {
48 pub fn has_overrun(&self) -> bool {
50 self.overrun.iter().any(|b| *b != 0)
51 }
52
53 pub fn overrun_fields(&self, desc: &StructureDesc) -> Vec<String> {
59 select_paths(&bit_paths(desc), &self.overrun)
60 }
61
62 pub fn changed_paths(&self) -> Vec<String> {
66 select_paths(&self.paths, &self.changed)
67 }
68
69 pub fn overrun_paths(&self) -> Vec<String> {
72 select_paths(&self.paths, &self.overrun)
73 }
74}
75
76pub(crate) fn bit_paths(desc: &StructureDesc) -> Vec<String> {
79 let mut paths = vec!["<whole structure>".to_string()];
80 flatten_field_paths(desc, "", &mut paths);
81 paths
82}
83
84fn select_paths(paths: &[String], bits: &[u8]) -> Vec<String> {
86 paths
87 .iter()
88 .enumerate()
89 .filter(|(bit, _)| {
90 let byte = bit / 8;
91 byte < bits.len() && (bits[byte] & (1 << (bit % 8))) != 0
92 })
93 .map(|(_, path)| path.clone())
94 .collect()
95}
96
97fn flatten_field_paths(desc: &StructureDesc, prefix: &str, out: &mut Vec<String>) {
100 for field in &desc.fields {
101 let path = if prefix.is_empty() {
102 field.name.clone()
103 } else {
104 format!("{prefix}.{}", field.name)
105 };
106 out.push(path.clone());
107 if let FieldType::Structure(nested) = &field.field_type {
108 flatten_field_paths(nested, &path, out);
109 }
110 }
111}
112
113impl PvdDecoder {
114 pub fn decode_monitor_update(
117 &self,
118 data: &[u8],
119 desc: &StructureDesc,
120 ) -> DecodeResult<MonitorUpdate> {
121 let (changed, mut offset) = self.read_bitset(data, 0)?;
122 let (value, consumed) =
123 self.decode_structure_with_bitset_body(&data[offset..], desc, &changed)?;
124 offset += consumed;
125 let (overrun, next) = self.read_bitset(data, offset)?;
126 Ok(MonitorUpdate {
127 value,
128 changed,
129 overrun,
130 consumed: next,
131 paths: bit_paths(desc),
132 })
133 }
134
135 pub fn decode_monitor_update_lenient(
141 &self,
142 data: &[u8],
143 desc: &StructureDesc,
144 ) -> DecodeResult<(MonitorUpdate, MonitorLayout)> {
145 let candidates = [
146 (
147 MonitorLayout::SpecOrder,
148 self.decode_monitor_update(data, desc),
149 ),
150 (
151 MonitorLayout::OverrunBeforeData,
152 self.decode_overrun_before_data(data, desc),
153 ),
154 (
155 MonitorLayout::ChangedOnly,
156 self.decode_changed_only(data, desc),
157 ),
158 ];
159
160 let mut best: Option<(MonitorUpdate, MonitorLayout, i32)> = None;
161 let mut last_err = DecodeError::Malformed("no monitor layout matched");
162 for (layout, result) in candidates {
163 match result {
164 Ok(update) => {
165 let score = score_decoded(&update.value);
166 let better = match &best {
167 None => true,
168 Some((prev, _, prev_score)) => {
169 score > *prev_score
170 || (score == *prev_score && update.consumed > prev.consumed)
171 }
172 };
173 if better {
174 best = Some((update, layout, score));
175 }
176 }
177 Err(e) => last_err = e,
178 }
179 }
180
181 best.map(|(u, l, _)| (u, l)).ok_or(last_err)
182 }
183
184 fn decode_overrun_before_data(
186 &self,
187 data: &[u8],
188 desc: &StructureDesc,
189 ) -> DecodeResult<MonitorUpdate> {
190 let (changed, offset) = self.read_bitset(data, 0)?;
191 let (overrun, mut offset) = self.read_bitset(data, offset)?;
192 let (value, consumed) =
193 self.decode_structure_with_bitset_body(&data[offset..], desc, &changed)?;
194 offset += consumed;
195 Ok(MonitorUpdate {
196 value,
197 changed,
198 overrun,
199 consumed: offset,
200 paths: bit_paths(desc),
201 })
202 }
203
204 fn decode_changed_only(
206 &self,
207 data: &[u8],
208 desc: &StructureDesc,
209 ) -> DecodeResult<MonitorUpdate> {
210 let (changed, mut offset) = self.read_bitset(data, 0)?;
211 let (value, consumed) =
212 self.decode_structure_with_bitset_body(&data[offset..], desc, &changed)?;
213 offset += consumed;
214 Ok(MonitorUpdate {
215 value,
216 changed,
217 overrun: Vec::new(),
218 consumed: offset,
219 paths: bit_paths(desc),
220 })
221 }
222
223 fn read_bitset(&self, data: &[u8], offset: usize) -> DecodeResult<(Vec<u8>, usize)> {
226 if offset > data.len() {
227 return Err(DecodeError::Truncated {
228 needed: offset,
229 available: data.len(),
230 });
231 }
232 let (size, consumed) = self.decode_size(&data[offset..])?;
233 let start = offset + consumed;
234 let end = start + size;
235 if end > data.len() {
236 return Err(DecodeError::Truncated {
237 needed: end,
238 available: data.len(),
239 });
240 }
241 Ok((data[start..end].to_vec(), end))
242 }
243}
244
245fn score_decoded(value: &DecodedValue) -> i32 {
251 let DecodedValue::Structure(fields) = value else {
252 return -1;
253 };
254
255 let mut score = fields.len() as i32;
256
257 let mut has_value = false;
258 let mut has_alarm = false;
259 let mut has_ts = false;
260
261 for (name, val) in fields {
262 match name.as_str() {
263 "value" => {
264 has_value = true;
265 score += 4;
266 match val {
267 DecodedValue::Array(items) => {
268 if items.is_empty() {
269 score -= 2;
270 } else {
271 score += 6 + (items.len().min(8) as i32);
272 }
273 }
274 DecodedValue::Structure(_) => score += 1,
275 _ => score += 2,
276 }
277 }
278 "alarm" => {
279 has_alarm = true;
280 score += 2;
281 }
282 "timeStamp" => {
283 has_ts = true;
284 score += 2;
285 if let DecodedValue::Structure(ts_fields) = val {
286 if let Some(secs) = ts_fields.iter().find_map(|(n, v)| {
287 if n == "secondsPastEpoch" {
288 if let DecodedValue::Int64(s) = v {
289 return Some(*s);
290 }
291 }
292 None
293 }) {
294 if (0..=4_000_000_000i64).contains(&secs) {
295 score += 2;
296 } else if secs.abs() > 10_000_000_000i64 {
297 score -= 2;
298 }
299 }
300 }
301 }
302 "display" | "control" => {
303 score += 1;
304 }
305 _ => {}
306 }
307 }
308
309 if !has_value {
310 score -= 2;
311 }
312 if !has_alarm {
313 score -= 1;
314 }
315 if !has_ts {
316 score -= 1;
317 }
318
319 score
320}
321
322#[cfg(test)]
323mod tests {
324 use super::*;
325 use crate::spvd_decode::{FieldDesc, FieldType, PvdDecoder, StructureDesc, TypeCode};
326
327 fn nt_scalar_desc() -> StructureDesc {
328 let mut alarm = StructureDesc::new();
329 alarm.fields.push(FieldDesc {
330 name: "severity".to_string(),
331 field_type: FieldType::Scalar(TypeCode::Int32),
332 });
333
334 let mut desc = StructureDesc::new();
335 desc.fields.push(FieldDesc {
336 name: "value".to_string(),
337 field_type: FieldType::Scalar(TypeCode::Int32),
338 });
339 desc.fields.push(FieldDesc {
340 name: "alarm".to_string(),
341 field_type: FieldType::Structure(alarm),
342 });
343 desc
344 }
345
346 fn spec_order_body(changed: u8, value: i32, overrun: u8) -> Vec<u8> {
348 let mut b = vec![1, changed];
349 b.extend_from_slice(&value.to_le_bytes());
350 b.extend_from_slice(&[1, overrun]);
351 b
352 }
353
354 #[test]
355 fn decodes_spec_order_and_reports_consumed() {
356 let decoder = PvdDecoder::new(false);
357 let desc = nt_scalar_desc();
358 let body = spec_order_body(0b0000_0010, 42, 0);
360 let update = decoder.decode_monitor_update(&body, &desc).unwrap();
361
362 assert_eq!(update.changed, vec![0b0000_0010]);
363 assert_eq!(update.overrun, vec![0]);
364 assert!(!update.has_overrun());
365 assert_eq!(update.consumed, body.len());
366 let DecodedValue::Structure(fields) = &update.value else {
367 panic!("expected a structure");
368 };
369 assert_eq!(fields.len(), 1);
370 assert_eq!(fields[0].0, "value");
371 }
372
373 #[test]
374 fn overrun_bits_resolve_to_field_paths() {
375 let decoder = PvdDecoder::new(false);
376 let desc = nt_scalar_desc();
377 let body = spec_order_body(0b0000_0010, 42, 0b0000_1010);
379 let update = decoder.decode_monitor_update(&body, &desc).unwrap();
380
381 assert!(update.has_overrun());
382 assert_eq!(
383 update.overrun_fields(&desc),
384 vec!["value", "alarm.severity"]
385 );
386 }
387
388 #[test]
389 fn bit_zero_overrun_reports_the_whole_structure() {
390 let decoder = PvdDecoder::new(false);
391 let desc = nt_scalar_desc();
392 let body = spec_order_body(0b0000_0010, 42, 0b0000_0001);
393 let update = decoder.decode_monitor_update(&body, &desc).unwrap();
394 assert_eq!(update.overrun_fields(&desc), vec!["<whole structure>"]);
395 }
396
397 #[test]
402 fn flatten_field_paths_agrees_with_count_structure_fields() {
403 let mut leaf = StructureDesc::new();
404 leaf.fields.push(FieldDesc {
405 name: "deep".to_string(),
406 field_type: FieldType::Scalar(TypeCode::Int32),
407 });
408
409 let mut mid = StructureDesc::new();
410 mid.fields.push(FieldDesc {
411 name: "a".to_string(),
412 field_type: FieldType::Scalar(TypeCode::Int32),
413 });
414 mid.fields.push(FieldDesc {
415 name: "leaf".to_string(),
416 field_type: FieldType::Structure(leaf),
417 });
418 mid.fields.push(FieldDesc {
419 name: "b".to_string(),
420 field_type: FieldType::Scalar(TypeCode::Int32),
421 });
422
423 let mut root = StructureDesc::new();
424 root.fields.push(FieldDesc {
425 name: "value".to_string(),
426 field_type: FieldType::Scalar(TypeCode::Int32),
427 });
428 root.fields.push(FieldDesc {
429 name: "mid".to_string(),
430 field_type: FieldType::Structure(mid),
431 });
432 root.fields.push(FieldDesc {
433 name: "tail".to_string(),
434 field_type: FieldType::Scalar(TypeCode::Int32),
435 });
436
437 let mut paths = Vec::new();
438 flatten_field_paths(&root, "", &mut paths);
439
440 assert_eq!(
441 paths,
442 vec![
443 "value",
444 "mid",
445 "mid.a",
446 "mid.leaf",
447 "mid.leaf.deep",
448 "mid.b",
449 "tail",
450 ]
451 );
452 assert_eq!(
453 paths.len(),
454 crate::spvd_decode::count_structure_fields(&root),
455 "one path per bit, in the same order the bits are numbered"
456 );
457
458 let mut overrun = vec![0u8; 2];
461 let bit = 1 + 4; overrun[bit / 8] |= 1 << (bit % 8);
463 let update = MonitorUpdate {
464 value: DecodedValue::Structure(Vec::new()),
465 changed: Vec::new(),
466 overrun,
467 consumed: 0,
468 paths: bit_paths(&root),
469 };
470 assert_eq!(update.overrun_fields(&root), vec!["mid.leaf.deep"]);
471 assert_eq!(update.overrun_paths(), vec!["mid.leaf.deep"]);
472 }
473
474 #[test]
478 fn monitor_update_reports_overrun_paths() {
479 let decoder = PvdDecoder::new(false);
480 let desc = nt_scalar_desc();
481 let body = spec_order_body(0b0000_0010, 42, 0b0000_1000);
483 let update = decoder.decode_monitor_update(&body, &desc).unwrap();
484
485 assert_eq!(update.changed_paths(), vec!["value"]);
486 assert_eq!(update.overrun_paths(), vec!["alarm.severity"]);
487 assert!(update.has_overrun());
488 }
489
490 #[test]
491 fn missing_overrun_bitset_is_truncated_not_silently_accepted() {
492 let decoder = PvdDecoder::new(false);
493 let desc = nt_scalar_desc();
494 let mut body = vec![1, 0b0000_0010];
496 body.extend_from_slice(&42i32.to_le_bytes());
497 assert!(matches!(
498 decoder.decode_monitor_update(&body, &desc).unwrap_err(),
499 DecodeError::Truncated { .. }
500 ));
501 }
502
503 #[test]
504 fn lenient_identifies_the_spec_layout() {
505 let decoder = PvdDecoder::new(false);
506 let desc = nt_scalar_desc();
507 let body = spec_order_body(0b0000_0010, 42, 0);
508 let (_, layout) = decoder.decode_monitor_update_lenient(&body, &desc).unwrap();
509 assert_eq!(layout, MonitorLayout::SpecOrder);
510 }
511
512 #[test]
513 fn lenient_recovers_the_overrun_before_data_layout() {
514 let decoder = PvdDecoder::new(false);
515 let desc = nt_scalar_desc();
516 let mut body = vec![1, 0b0000_0010, 1, 0];
518 body.extend_from_slice(&42i32.to_le_bytes());
519
520 let strict = decoder.decode_monitor_update(&body, &desc);
523 let strict_ok = strict.map(|u| u.value).ok();
524 assert_ne!(
525 strict_ok.as_ref().and_then(scalar_value_of),
526 Some(42),
527 "strict must not accidentally decode the non-spec layout"
528 );
529
530 let (update, layout) = decoder.decode_monitor_update_lenient(&body, &desc).unwrap();
531 assert_eq!(layout, MonitorLayout::OverrunBeforeData);
532 assert_eq!(scalar_value_of(&update.value), Some(42));
533 }
534
535 fn scalar_value_of(v: &DecodedValue) -> Option<i32> {
536 let DecodedValue::Structure(fields) = v else {
537 return None;
538 };
539 fields
540 .iter()
541 .find_map(|(name, val)| match (name.as_str(), val) {
542 ("value", DecodedValue::Int32(n)) => Some(*n),
543 _ => None,
544 })
545 }
546
547 #[test]
548 fn lenient_falls_back_to_changed_only() {
549 let decoder = PvdDecoder::new(false);
550 let desc = nt_scalar_desc();
551 let mut body = vec![1, 0b0000_0010];
553 body.extend_from_slice(&42i32.to_le_bytes());
554 let (update, layout) = decoder.decode_monitor_update_lenient(&body, &desc).unwrap();
555 assert_eq!(layout, MonitorLayout::ChangedOnly);
556 assert!(update.overrun.is_empty());
557 }
558
559 #[test]
560 fn round_trips_an_encoded_delta() {
561 use crate::spvd_encode::{compute_changed_bits, encode_nt_payload_delta, nt_payload_desc};
562 use spvirit_types::{NtPayload, NtScalar, ScalarValue};
563
564 let prev = NtPayload::Scalar(NtScalar::from_value(ScalarValue::F64(1.0)));
565 let next = NtPayload::Scalar(NtScalar::from_value(ScalarValue::F64(3.5)));
566 let desc = nt_payload_desc(&next);
567
568 let (bitset, values) =
569 encode_nt_payload_delta(&prev, &next, &desc, false).expect("value changed");
570
571 let mut body = bitset.clone();
573 body.extend_from_slice(&values);
574 body.extend_from_slice(&[0u8]); let decoder = PvdDecoder::new(false);
577 let update = decoder.decode_monitor_update(&body, &desc).unwrap();
578
579 assert_eq!(update.consumed, body.len());
580 assert!(!update.has_overrun());
581
582 let bits =
585 compute_changed_bits(&projection(&prev, &desc), &projection(&next, &desc), &desc)
586 .expect("value changed");
587 let mut expected = vec![0u8; bits.len().div_ceil(8)];
588 for (i, b) in bits.iter().enumerate() {
589 if *b {
590 expected[i / 8] |= 1 << (i % 8);
591 }
592 }
593 assert_eq!(update.changed, expected);
594 assert!(bits[1], "bit 1 is 'value', which is what changed");
595
596 let DecodedValue::Structure(fields) = &update.value else {
598 panic!("expected a structure");
599 };
600 let value = fields
601 .iter()
602 .find(|(n, _)| n == "value")
603 .map(|(_, v)| v)
604 .expect("value field present");
605 match value {
606 DecodedValue::Float64(v) => assert!((v - 3.5).abs() < 1e-9),
607 other => panic!("unexpected value {other:?}"),
608 }
609 }
610
611 fn projection(payload: &spvirit_types::NtPayload, desc: &StructureDesc) -> DecodedValue {
615 let bytes = crate::spvd_encode::encode_nt_payload_values_for_desc(payload, desc, false);
616 PvdDecoder::new(false)
617 .decode_structure(&bytes, desc)
618 .expect("projection")
619 .0
620 }
621}