1use std::time::{SystemTime, UNIX_EPOCH};
6
7use crate::spvd_decode::{FieldDesc, FieldType, StructureDesc, TypeCode};
8
9use spvirit_types::{
10 NdDimension, NtAlarm, NtAttribute, NtDisplay, NtEnum, NtNdArray, NtPayload, NtScalar,
11 NtScalarArray, NtTable, NtTableColumn, NtTimeStamp, PvValue, ScalarArrayValue, ScalarValue,
12};
13
14fn count_structure_fields(desc: &StructureDesc) -> usize {
15 let mut count = 0;
16 for field in &desc.fields {
17 count += 1;
18 if let FieldType::Structure(nested) = &field.field_type {
19 count += count_structure_fields(nested);
20 }
21 }
22 count
23}
24
25pub fn encode_size_pvd(size: usize, is_be: bool) -> Vec<u8> {
26 crate::encode_common::encode_size(size, is_be)
27}
28
29pub fn encode_string_pvd(value: &str, is_be: bool) -> Vec<u8> {
30 crate::encode_common::encode_string(value, is_be)
31}
32
33pub fn encode_structure_desc(desc: &StructureDesc, is_be: bool) -> Vec<u8> {
34 let mut out = Vec::new();
35 let struct_id = desc.struct_id.clone().unwrap_or_default();
36 out.extend_from_slice(&encode_string_pvd(&struct_id, is_be));
37 out.extend_from_slice(&encode_size_pvd(desc.fields.len(), is_be));
38 for field in &desc.fields {
39 out.extend_from_slice(&encode_field_desc(field, is_be));
40 }
41 out
42}
43
44fn encode_field_desc(field: &FieldDesc, is_be: bool) -> Vec<u8> {
45 let mut out = Vec::new();
46 out.extend_from_slice(&encode_string_pvd(&field.name, is_be));
47 out.extend_from_slice(&encode_type_desc(&field.field_type, is_be));
48 out
49}
50
51fn encode_type_desc(field_type: &FieldType, is_be: bool) -> Vec<u8> {
52 let mut out = Vec::new();
53 match field_type {
54 FieldType::Structure(desc) => {
55 out.push(0x80);
56 out.extend_from_slice(&encode_structure_desc(desc, is_be));
57 }
58 FieldType::StructureArray(desc) => {
59 out.push(0x88);
60 out.push(0x80); out.extend_from_slice(&encode_structure_desc(desc, is_be));
62 }
63 FieldType::Union(fields) => {
64 out.push(0x81);
65 let desc = StructureDesc {
66 struct_id: None,
67 fields: fields.clone(),
68 };
69 out.extend_from_slice(&encode_structure_desc(&desc, is_be));
70 }
71 FieldType::UnionArray(fields) => {
72 out.push(0x89);
73 out.push(0x81); let desc = StructureDesc {
75 struct_id: None,
76 fields: fields.clone(),
77 };
78 out.extend_from_slice(&encode_structure_desc(&desc, is_be));
79 }
80 FieldType::Variant => out.push(0x82),
81 FieldType::VariantArray => out.push(0x8A),
82 FieldType::BoundedString(bound) => {
83 out.push(0x83);
84 out.extend_from_slice(&encode_size_pvd(*bound as usize, is_be));
85 }
86 FieldType::String => out.push(0x60),
87 FieldType::StringArray => out.push(0x68),
88 FieldType::Scalar(tc) => out.push(*tc as u8),
89 FieldType::ScalarArray(tc) => out.push((*tc as u8) | 0x08),
90 }
91 out
92}
93
94fn encode_scalar_value(value: &ScalarValue, is_be: bool) -> Vec<u8> {
95 match value {
96 ScalarValue::Bool(v) => vec![if *v { 1 } else { 0 }],
97 ScalarValue::I8(v) => vec![*v as u8],
98 ScalarValue::I16(v) => {
99 if is_be {
100 v.to_be_bytes().to_vec()
101 } else {
102 v.to_le_bytes().to_vec()
103 }
104 }
105 ScalarValue::I32(v) => {
106 if is_be {
107 v.to_be_bytes().to_vec()
108 } else {
109 v.to_le_bytes().to_vec()
110 }
111 }
112 ScalarValue::I64(v) => {
113 if is_be {
114 v.to_be_bytes().to_vec()
115 } else {
116 v.to_le_bytes().to_vec()
117 }
118 }
119 ScalarValue::U8(v) => vec![*v],
120 ScalarValue::U16(v) => {
121 if is_be {
122 v.to_be_bytes().to_vec()
123 } else {
124 v.to_le_bytes().to_vec()
125 }
126 }
127 ScalarValue::U32(v) => {
128 if is_be {
129 v.to_be_bytes().to_vec()
130 } else {
131 v.to_le_bytes().to_vec()
132 }
133 }
134 ScalarValue::U64(v) => {
135 if is_be {
136 v.to_be_bytes().to_vec()
137 } else {
138 v.to_le_bytes().to_vec()
139 }
140 }
141 ScalarValue::F32(v) => {
142 if is_be {
143 v.to_be_bytes().to_vec()
144 } else {
145 v.to_le_bytes().to_vec()
146 }
147 }
148 ScalarValue::F64(v) => {
149 if is_be {
150 v.to_be_bytes().to_vec()
151 } else {
152 v.to_le_bytes().to_vec()
153 }
154 }
155 ScalarValue::Str(v) => encode_string_pvd(v, is_be),
156 }
157}
158
159fn encode_alarm(nt: &NtScalar, is_be: bool) -> Vec<u8> {
160 let mut out = Vec::new();
161 out.extend_from_slice(&encode_i32(nt.alarm_severity, is_be));
162 out.extend_from_slice(&encode_i32(nt.alarm_status, is_be));
163 out.extend_from_slice(&encode_string_pvd(&nt.alarm_message, is_be));
164 out
165}
166
167fn encode_bool(value: bool) -> Vec<u8> {
168 vec![if value { 1 } else { 0 }]
169}
170
171fn encode_string_array(values: &[String], is_be: bool) -> Vec<u8> {
172 let mut out = Vec::new();
173 out.extend_from_slice(&encode_size_pvd(values.len(), is_be));
174 for v in values {
175 out.extend_from_slice(&encode_string_pvd(v, is_be));
176 }
177 out
178}
179
180fn encode_enum(index: i32, choices: &[String], is_be: bool) -> Vec<u8> {
181 let mut out = Vec::new();
182 out.extend_from_slice(&encode_i32(index, is_be));
183 out.extend_from_slice(&encode_string_array(choices, is_be));
184 out
185}
186
187fn encode_timestamp(_nt: &NtScalar, is_be: bool) -> Vec<u8> {
188 let mut out = Vec::new();
189 let now = SystemTime::now()
190 .duration_since(UNIX_EPOCH)
191 .unwrap_or_default();
192 let seconds_past_epoch = now.as_secs() as i64;
193 let nanos = now.subsec_nanos() as i32;
194
195 out.extend_from_slice(&encode_i64(seconds_past_epoch, is_be));
196 out.extend_from_slice(&encode_i32(nanos, is_be));
197 out.extend_from_slice(&encode_i32(0, is_be)); out
199}
200
201fn encode_display(nt: &NtScalar, is_be: bool) -> Vec<u8> {
202 let mut out = Vec::new();
203 out.extend_from_slice(&encode_f64(nt.display_low, is_be));
204 out.extend_from_slice(&encode_f64(nt.display_high, is_be));
205 out.extend_from_slice(&encode_string_pvd(&nt.display_description, is_be));
206 out.extend_from_slice(&encode_string_pvd(&nt.units, is_be));
207 out.extend_from_slice(&encode_i32(nt.display_precision, is_be));
208 out.extend_from_slice(&encode_enum(
209 nt.display_form_index,
210 &nt.display_form_choices,
211 is_be,
212 ));
213 out
214}
215
216fn encode_control(nt: &NtScalar, is_be: bool) -> Vec<u8> {
217 let mut out = Vec::new();
218 out.extend_from_slice(&encode_f64(nt.control_low, is_be));
219 out.extend_from_slice(&encode_f64(nt.control_high, is_be));
220 out.extend_from_slice(&encode_f64(nt.control_min_step, is_be));
221 out
222}
223
224fn encode_value_alarm(nt: &NtScalar, is_be: bool) -> Vec<u8> {
225 let mut out = Vec::new();
226 out.extend_from_slice(&encode_bool(nt.value_alarm_active));
227 out.extend_from_slice(&encode_f64(nt.value_alarm_low_alarm_limit, is_be));
228 out.extend_from_slice(&encode_f64(nt.value_alarm_low_warning_limit, is_be));
229 out.extend_from_slice(&encode_f64(nt.value_alarm_high_warning_limit, is_be));
230 out.extend_from_slice(&encode_f64(nt.value_alarm_high_alarm_limit, is_be));
231 out.extend_from_slice(&encode_i32(nt.value_alarm_low_alarm_severity, is_be));
232 out.extend_from_slice(&encode_i32(nt.value_alarm_low_warning_severity, is_be));
233 out.extend_from_slice(&encode_i32(nt.value_alarm_high_warning_severity, is_be));
234 out.extend_from_slice(&encode_i32(nt.value_alarm_high_alarm_severity, is_be));
235 out.push(nt.value_alarm_hysteresis);
236 out
237}
238
239fn encode_i32(value: i32, is_be: bool) -> Vec<u8> {
240 if is_be {
241 value.to_be_bytes().to_vec()
242 } else {
243 value.to_le_bytes().to_vec()
244 }
245}
246
247fn encode_i64(value: i64, is_be: bool) -> Vec<u8> {
248 if is_be {
249 value.to_be_bytes().to_vec()
250 } else {
251 value.to_le_bytes().to_vec()
252 }
253}
254
255fn encode_f64(value: f64, is_be: bool) -> Vec<u8> {
256 if is_be {
257 value.to_be_bytes().to_vec()
258 } else {
259 value.to_le_bytes().to_vec()
260 }
261}
262
263pub fn nt_scalar_desc(value: &ScalarValue) -> StructureDesc {
264 let value_type = match value {
265 ScalarValue::Bool(_) => FieldType::Scalar(TypeCode::Boolean),
266 ScalarValue::I8(_) => FieldType::Scalar(TypeCode::Int8),
267 ScalarValue::I16(_) => FieldType::Scalar(TypeCode::Int16),
268 ScalarValue::I32(_) => FieldType::Scalar(TypeCode::Int32),
269 ScalarValue::I64(_) => FieldType::Scalar(TypeCode::Int64),
270 ScalarValue::U8(_) => FieldType::Scalar(TypeCode::UInt8),
271 ScalarValue::U16(_) => FieldType::Scalar(TypeCode::UInt16),
272 ScalarValue::U32(_) => FieldType::Scalar(TypeCode::UInt32),
273 ScalarValue::U64(_) => FieldType::Scalar(TypeCode::UInt64),
274 ScalarValue::F32(_) => FieldType::Scalar(TypeCode::Float32),
275 ScalarValue::F64(_) => FieldType::Scalar(TypeCode::Float64),
276 ScalarValue::Str(_) => FieldType::String,
277 };
278
279 StructureDesc {
280 struct_id: Some("epics:nt/NTScalar:1.0".to_string()),
281 fields: vec![
282 FieldDesc {
283 name: "value".to_string(),
284 field_type: value_type,
285 },
286 FieldDesc {
287 name: "alarm".to_string(),
288 field_type: FieldType::Structure(StructureDesc {
289 struct_id: Some("alarm_t".to_string()),
290 fields: vec![
291 FieldDesc {
292 name: "severity".to_string(),
293 field_type: FieldType::Scalar(TypeCode::Int32),
294 },
295 FieldDesc {
296 name: "status".to_string(),
297 field_type: FieldType::Scalar(TypeCode::Int32),
298 },
299 FieldDesc {
300 name: "message".to_string(),
301 field_type: FieldType::String,
302 },
303 ],
304 }),
305 },
306 FieldDesc {
307 name: "timeStamp".to_string(),
308 field_type: FieldType::Structure(StructureDesc {
309 struct_id: None,
310 fields: vec![
311 FieldDesc {
312 name: "secondsPastEpoch".to_string(),
313 field_type: FieldType::Scalar(TypeCode::Int64),
314 },
315 FieldDesc {
316 name: "nanoseconds".to_string(),
317 field_type: FieldType::Scalar(TypeCode::Int32),
318 },
319 FieldDesc {
320 name: "userTag".to_string(),
321 field_type: FieldType::Scalar(TypeCode::Int32),
322 },
323 ],
324 }),
325 },
326 FieldDesc {
327 name: "display".to_string(),
328 field_type: FieldType::Structure(StructureDesc {
329 struct_id: None,
330 fields: vec![
331 FieldDesc {
332 name: "limitLow".to_string(),
333 field_type: FieldType::Scalar(TypeCode::Float64),
334 },
335 FieldDesc {
336 name: "limitHigh".to_string(),
337 field_type: FieldType::Scalar(TypeCode::Float64),
338 },
339 FieldDesc {
340 name: "description".to_string(),
341 field_type: FieldType::String,
342 },
343 FieldDesc {
344 name: "units".to_string(),
345 field_type: FieldType::String,
346 },
347 FieldDesc {
348 name: "precision".to_string(),
349 field_type: FieldType::Scalar(TypeCode::Int32),
350 },
351 FieldDesc {
352 name: "form".to_string(),
353 field_type: FieldType::Structure(StructureDesc {
354 struct_id: Some("enum_t".to_string()),
355 fields: vec![
356 FieldDesc {
357 name: "index".to_string(),
358 field_type: FieldType::Scalar(TypeCode::Int32),
359 },
360 FieldDesc {
361 name: "choices".to_string(),
362 field_type: FieldType::StringArray,
363 },
364 ],
365 }),
366 },
367 ],
368 }),
369 },
370 FieldDesc {
371 name: "control".to_string(),
372 field_type: FieldType::Structure(StructureDesc {
373 struct_id: Some("control_t".to_string()),
374 fields: vec![
375 FieldDesc {
376 name: "limitLow".to_string(),
377 field_type: FieldType::Scalar(TypeCode::Float64),
378 },
379 FieldDesc {
380 name: "limitHigh".to_string(),
381 field_type: FieldType::Scalar(TypeCode::Float64),
382 },
383 FieldDesc {
384 name: "minStep".to_string(),
385 field_type: FieldType::Scalar(TypeCode::Float64),
386 },
387 ],
388 }),
389 },
390 FieldDesc {
391 name: "valueAlarm".to_string(),
392 field_type: FieldType::Structure(StructureDesc {
393 struct_id: Some("valueAlarm_t".to_string()),
394 fields: vec![
395 FieldDesc {
396 name: "active".to_string(),
397 field_type: FieldType::Scalar(TypeCode::Boolean),
398 },
399 FieldDesc {
400 name: "lowAlarmLimit".to_string(),
401 field_type: FieldType::Scalar(TypeCode::Float64),
402 },
403 FieldDesc {
404 name: "lowWarningLimit".to_string(),
405 field_type: FieldType::Scalar(TypeCode::Float64),
406 },
407 FieldDesc {
408 name: "highWarningLimit".to_string(),
409 field_type: FieldType::Scalar(TypeCode::Float64),
410 },
411 FieldDesc {
412 name: "highAlarmLimit".to_string(),
413 field_type: FieldType::Scalar(TypeCode::Float64),
414 },
415 FieldDesc {
416 name: "lowAlarmSeverity".to_string(),
417 field_type: FieldType::Scalar(TypeCode::Int32),
418 },
419 FieldDesc {
420 name: "lowWarningSeverity".to_string(),
421 field_type: FieldType::Scalar(TypeCode::Int32),
422 },
423 FieldDesc {
424 name: "highWarningSeverity".to_string(),
425 field_type: FieldType::Scalar(TypeCode::Int32),
426 },
427 FieldDesc {
428 name: "highAlarmSeverity".to_string(),
429 field_type: FieldType::Scalar(TypeCode::Int32),
430 },
431 FieldDesc {
432 name: "hysteresis".to_string(),
433 field_type: FieldType::Scalar(TypeCode::UInt8),
434 },
435 ],
436 }),
437 },
438 ],
439 }
440}
441
442pub fn encode_nt_scalar_full(nt: &NtScalar, is_be: bool) -> Vec<u8> {
443 let mut out = Vec::new();
444 out.extend_from_slice(&encode_scalar_value(&nt.value, is_be));
445 out.extend_from_slice(&encode_alarm(nt, is_be));
446 out.extend_from_slice(&encode_timestamp(nt, is_be));
447 out.extend_from_slice(&encode_display(nt, is_be));
448 out.extend_from_slice(&encode_control(nt, is_be));
449 out.extend_from_slice(&encode_value_alarm(nt, is_be));
450 out
451}
452
453fn encode_structure_bitset(desc: &StructureDesc, is_be: bool) -> Vec<u8> {
454 let total_bits = 1 + count_structure_fields(desc);
455 let bitset_size = (total_bits + 7) / 8;
456 let mut bitset = vec![0u8; bitset_size];
457 for bit in 0..total_bits {
458 let byte_idx = bit / 8;
459 let bit_idx = bit % 8;
460 bitset[byte_idx] |= 1 << bit_idx;
461 }
462 let mut out = Vec::new();
463 out.extend_from_slice(&encode_size_pvd(bitset_size, is_be));
464 out.extend_from_slice(&bitset);
465 out
466}
467
468fn encode_structure_with_bitset(desc: &StructureDesc, nt: &NtScalar, is_be: bool) -> Vec<u8> {
469 let mut out = Vec::new();
470 out.extend_from_slice(&encode_structure_bitset(desc, is_be));
471 out.extend_from_slice(&encode_nt_scalar_full(nt, is_be));
472 out
473}
474
475pub fn encode_nt_scalar_bitset(nt: &NtScalar, is_be: bool) -> Vec<u8> {
476 let desc = nt_scalar_desc(&nt.value);
477 encode_structure_with_bitset(&desc, nt, is_be)
478}
479
480pub fn encode_nt_scalar_bitset_parts(nt: &NtScalar, is_be: bool) -> (Vec<u8>, Vec<u8>) {
481 let desc = nt_scalar_desc(&nt.value);
482 let bitset = encode_structure_bitset(&desc, is_be);
483 let values = encode_nt_scalar_full(nt, is_be);
484 (bitset, values)
485}
486
487fn alarm_desc() -> StructureDesc {
488 StructureDesc {
489 struct_id: Some("alarm_t".to_string()),
490 fields: vec![
491 FieldDesc {
492 name: "severity".to_string(),
493 field_type: FieldType::Scalar(TypeCode::Int32),
494 },
495 FieldDesc {
496 name: "status".to_string(),
497 field_type: FieldType::Scalar(TypeCode::Int32),
498 },
499 FieldDesc {
500 name: "message".to_string(),
501 field_type: FieldType::String,
502 },
503 ],
504 }
505}
506
507fn timestamp_desc() -> StructureDesc {
508 StructureDesc {
509 struct_id: Some("time_t".to_string()),
510 fields: vec![
511 FieldDesc {
512 name: "secondsPastEpoch".to_string(),
513 field_type: FieldType::Scalar(TypeCode::Int64),
514 },
515 FieldDesc {
516 name: "nanoseconds".to_string(),
517 field_type: FieldType::Scalar(TypeCode::Int32),
518 },
519 FieldDesc {
520 name: "userTag".to_string(),
521 field_type: FieldType::Scalar(TypeCode::Int32),
522 },
523 ],
524 }
525}
526
527fn display_desc() -> StructureDesc {
528 StructureDesc {
529 struct_id: Some("display_t".to_string()),
530 fields: vec![
531 FieldDesc {
532 name: "limitLow".to_string(),
533 field_type: FieldType::Scalar(TypeCode::Float64),
534 },
535 FieldDesc {
536 name: "limitHigh".to_string(),
537 field_type: FieldType::Scalar(TypeCode::Float64),
538 },
539 FieldDesc {
540 name: "description".to_string(),
541 field_type: FieldType::String,
542 },
543 FieldDesc {
544 name: "units".to_string(),
545 field_type: FieldType::String,
546 },
547 FieldDesc {
548 name: "precision".to_string(),
549 field_type: FieldType::Scalar(TypeCode::Int32),
550 },
551 ],
552 }
553}
554
555fn scalar_array_field_type(value: &ScalarArrayValue) -> FieldType {
556 match value {
557 ScalarArrayValue::Bool(_) => FieldType::ScalarArray(TypeCode::Boolean),
558 ScalarArrayValue::I8(_) => FieldType::ScalarArray(TypeCode::Int8),
559 ScalarArrayValue::I16(_) => FieldType::ScalarArray(TypeCode::Int16),
560 ScalarArrayValue::I32(_) => FieldType::ScalarArray(TypeCode::Int32),
561 ScalarArrayValue::I64(_) => FieldType::ScalarArray(TypeCode::Int64),
562 ScalarArrayValue::U8(_) => FieldType::ScalarArray(TypeCode::UInt8),
563 ScalarArrayValue::U16(_) => FieldType::ScalarArray(TypeCode::UInt16),
564 ScalarArrayValue::U32(_) => FieldType::ScalarArray(TypeCode::UInt32),
565 ScalarArrayValue::U64(_) => FieldType::ScalarArray(TypeCode::UInt64),
566 ScalarArrayValue::F32(_) => FieldType::ScalarArray(TypeCode::Float32),
567 ScalarArrayValue::F64(_) => FieldType::ScalarArray(TypeCode::Float64),
568 ScalarArrayValue::Str(_) => FieldType::StringArray,
569 }
570}
571
572fn encode_scalar_array_value_pvd(value: &ScalarArrayValue, is_be: bool) -> Vec<u8> {
573 let mut out = Vec::new();
574 match value {
575 ScalarArrayValue::Bool(v) => {
576 out.extend_from_slice(&encode_size_pvd(v.len(), is_be));
577 for i in v {
578 out.push(if *i { 1 } else { 0 });
579 }
580 }
581 ScalarArrayValue::I8(v) => {
582 out.extend_from_slice(&encode_size_pvd(v.len(), is_be));
583 for i in v {
584 out.push(*i as u8);
585 }
586 }
587 ScalarArrayValue::I16(v) => {
588 out.extend_from_slice(&encode_size_pvd(v.len(), is_be));
589 for i in v {
590 let b = if is_be {
591 i.to_be_bytes()
592 } else {
593 i.to_le_bytes()
594 };
595 out.extend_from_slice(&b);
596 }
597 }
598 ScalarArrayValue::I32(v) => {
599 out.extend_from_slice(&encode_size_pvd(v.len(), is_be));
600 for i in v {
601 out.extend_from_slice(&encode_i32(*i, is_be));
602 }
603 }
604 ScalarArrayValue::I64(v) => {
605 out.extend_from_slice(&encode_size_pvd(v.len(), is_be));
606 for i in v {
607 out.extend_from_slice(&encode_i64(*i, is_be));
608 }
609 }
610 ScalarArrayValue::U8(v) => {
611 out.extend_from_slice(&encode_size_pvd(v.len(), is_be));
612 out.extend_from_slice(v);
613 }
614 ScalarArrayValue::U16(v) => {
615 out.extend_from_slice(&encode_size_pvd(v.len(), is_be));
616 for i in v {
617 let b = if is_be {
618 i.to_be_bytes()
619 } else {
620 i.to_le_bytes()
621 };
622 out.extend_from_slice(&b);
623 }
624 }
625 ScalarArrayValue::U32(v) => {
626 out.extend_from_slice(&encode_size_pvd(v.len(), is_be));
627 for i in v {
628 let b = if is_be {
629 i.to_be_bytes()
630 } else {
631 i.to_le_bytes()
632 };
633 out.extend_from_slice(&b);
634 }
635 }
636 ScalarArrayValue::U64(v) => {
637 out.extend_from_slice(&encode_size_pvd(v.len(), is_be));
638 for i in v {
639 let b = if is_be {
640 i.to_be_bytes()
641 } else {
642 i.to_le_bytes()
643 };
644 out.extend_from_slice(&b);
645 }
646 }
647 ScalarArrayValue::F32(v) => {
648 out.extend_from_slice(&encode_size_pvd(v.len(), is_be));
649 for i in v {
650 let b = if is_be {
651 i.to_be_bytes()
652 } else {
653 i.to_le_bytes()
654 };
655 out.extend_from_slice(&b);
656 }
657 }
658 ScalarArrayValue::F64(v) => {
659 out.extend_from_slice(&encode_size_pvd(v.len(), is_be));
660 for i in v {
661 out.extend_from_slice(&encode_f64(*i, is_be));
662 }
663 }
664 ScalarArrayValue::Str(v) => {
665 out.extend_from_slice(&encode_string_array(v, is_be));
666 }
667 }
668 out
669}
670
671fn encode_nt_alarm(alarm: &NtAlarm, is_be: bool) -> Vec<u8> {
672 let mut out = Vec::new();
673 out.extend_from_slice(&encode_i32(alarm.severity, is_be));
674 out.extend_from_slice(&encode_i32(alarm.status, is_be));
675 out.extend_from_slice(&encode_string_pvd(&alarm.message, is_be));
676 out
677}
678
679fn encode_nt_timestamp(ts: &NtTimeStamp, is_be: bool) -> Vec<u8> {
680 let mut out = Vec::new();
681 out.extend_from_slice(&encode_i64(ts.seconds_past_epoch, is_be));
682 out.extend_from_slice(&encode_i32(ts.nanoseconds, is_be));
683 out.extend_from_slice(&encode_i32(ts.user_tag, is_be));
684 out
685}
686
687fn encode_nt_display(display: &NtDisplay, is_be: bool) -> Vec<u8> {
688 let mut out = Vec::new();
689 out.extend_from_slice(&encode_f64(display.limit_low, is_be));
690 out.extend_from_slice(&encode_f64(display.limit_high, is_be));
691 out.extend_from_slice(&encode_string_pvd(&display.description, is_be));
692 out.extend_from_slice(&encode_string_pvd(&display.units, is_be));
693 out.extend_from_slice(&encode_i32(display.precision, is_be));
694 out
695}
696
697pub fn nt_scalar_array_desc(value: &ScalarArrayValue) -> StructureDesc {
698 StructureDesc {
699 struct_id: Some("epics:nt/NTScalarArray:1.0".to_string()),
700 fields: vec![
701 FieldDesc {
702 name: "value".to_string(),
703 field_type: scalar_array_field_type(value),
704 },
705 FieldDesc {
706 name: "alarm".to_string(),
707 field_type: FieldType::Structure(alarm_desc()),
708 },
709 FieldDesc {
710 name: "timeStamp".to_string(),
711 field_type: FieldType::Structure(timestamp_desc()),
712 },
713 FieldDesc {
714 name: "display".to_string(),
715 field_type: FieldType::Structure(display_desc()),
716 },
717 FieldDesc {
718 name: "control".to_string(),
719 field_type: FieldType::Structure(StructureDesc {
720 struct_id: Some("control_t".to_string()),
721 fields: vec![
722 FieldDesc {
723 name: "limitLow".to_string(),
724 field_type: FieldType::Scalar(TypeCode::Float64),
725 },
726 FieldDesc {
727 name: "limitHigh".to_string(),
728 field_type: FieldType::Scalar(TypeCode::Float64),
729 },
730 FieldDesc {
731 name: "minStep".to_string(),
732 field_type: FieldType::Scalar(TypeCode::Float64),
733 },
734 ],
735 }),
736 },
737 ],
738 }
739}
740
741pub fn encode_nt_scalar_array_full(nt: &NtScalarArray, is_be: bool) -> Vec<u8> {
742 let mut out = Vec::new();
743 out.extend_from_slice(&encode_scalar_array_value_pvd(&nt.value, is_be));
744 out.extend_from_slice(&encode_nt_alarm(&nt.alarm, is_be));
745 out.extend_from_slice(&encode_nt_timestamp(&nt.time_stamp, is_be));
746 out.extend_from_slice(&encode_nt_display(&nt.display, is_be));
747 out.extend_from_slice(&encode_f64(nt.control.limit_low, is_be));
748 out.extend_from_slice(&encode_f64(nt.control.limit_high, is_be));
749 out.extend_from_slice(&encode_f64(nt.control.min_step, is_be));
750 out
751}
752
753pub fn nt_table_desc(nt: &NtTable) -> StructureDesc {
754 let mut value_fields: Vec<FieldDesc> = Vec::new();
755 for col in &nt.columns {
756 value_fields.push(FieldDesc {
757 name: col.name.clone(),
758 field_type: scalar_array_field_type(&col.values),
759 });
760 }
761 StructureDesc {
762 struct_id: Some("epics:nt/NTTable:1.0".to_string()),
763 fields: vec![
764 FieldDesc {
765 name: "labels".to_string(),
766 field_type: FieldType::StringArray,
767 },
768 FieldDesc {
769 name: "value".to_string(),
770 field_type: FieldType::Structure(StructureDesc {
771 struct_id: None,
772 fields: value_fields,
773 }),
774 },
775 ],
776 }
777}
778
779pub fn encode_nt_table_full(nt: &NtTable, is_be: bool) -> Vec<u8> {
780 let mut out = Vec::new();
781 out.extend_from_slice(&encode_string_array(&nt.labels, is_be));
782 for NtTableColumn { values, .. } in &nt.columns {
783 out.extend_from_slice(&encode_scalar_array_value_pvd(values, is_be));
784 }
785 out
786}
787
788fn nt_ndarray_value_union_fields() -> Vec<FieldDesc> {
789 vec![
790 FieldDesc {
791 name: "booleanValue".to_string(),
792 field_type: FieldType::ScalarArray(TypeCode::Boolean),
793 },
794 FieldDesc {
795 name: "byteValue".to_string(),
796 field_type: FieldType::ScalarArray(TypeCode::Int8),
797 },
798 FieldDesc {
799 name: "shortValue".to_string(),
800 field_type: FieldType::ScalarArray(TypeCode::Int16),
801 },
802 FieldDesc {
803 name: "intValue".to_string(),
804 field_type: FieldType::ScalarArray(TypeCode::Int32),
805 },
806 FieldDesc {
807 name: "longValue".to_string(),
808 field_type: FieldType::ScalarArray(TypeCode::Int64),
809 },
810 FieldDesc {
811 name: "ubyteValue".to_string(),
812 field_type: FieldType::ScalarArray(TypeCode::UInt8),
813 },
814 FieldDesc {
815 name: "ushortValue".to_string(),
816 field_type: FieldType::ScalarArray(TypeCode::UInt16),
817 },
818 FieldDesc {
819 name: "uintValue".to_string(),
820 field_type: FieldType::ScalarArray(TypeCode::UInt32),
821 },
822 FieldDesc {
823 name: "ulongValue".to_string(),
824 field_type: FieldType::ScalarArray(TypeCode::UInt64),
825 },
826 FieldDesc {
827 name: "floatValue".to_string(),
828 field_type: FieldType::ScalarArray(TypeCode::Float32),
829 },
830 FieldDesc {
831 name: "doubleValue".to_string(),
832 field_type: FieldType::ScalarArray(TypeCode::Float64),
833 },
834 FieldDesc {
835 name: "stringValue".to_string(),
836 field_type: FieldType::StringArray,
837 },
838 ]
839}
840
841fn ndarray_union_index(value: &ScalarArrayValue) -> usize {
842 match value {
843 ScalarArrayValue::Bool(_) => 0,
844 ScalarArrayValue::I8(_) => 1,
845 ScalarArrayValue::I16(_) => 2,
846 ScalarArrayValue::I32(_) => 3,
847 ScalarArrayValue::I64(_) => 4,
848 ScalarArrayValue::U8(_) => 5,
849 ScalarArrayValue::U16(_) => 6,
850 ScalarArrayValue::U32(_) => 7,
851 ScalarArrayValue::U64(_) => 8,
852 ScalarArrayValue::F32(_) => 9,
853 ScalarArrayValue::F64(_) => 10,
854 ScalarArrayValue::Str(_) => 11,
855 }
856}
857
858fn encode_ndarray_union(value: &ScalarArrayValue, is_be: bool) -> Vec<u8> {
859 let mut out = Vec::new();
860 out.extend_from_slice(&encode_size_pvd(ndarray_union_index(value), is_be));
861 out.extend_from_slice(&encode_scalar_array_value_pvd(value, is_be));
862 out
863}
864
865fn encode_codec_parameters(
866 parameters: &std::collections::HashMap<String, String>,
867 is_be: bool,
868) -> Vec<u8> {
869 if parameters.is_empty() {
870 return vec![0xFF];
871 }
872 let mut out = Vec::new();
873 out.push(0x80);
874 let mut fields = Vec::new();
875 for key in parameters.keys() {
876 fields.push(FieldDesc {
877 name: key.clone(),
878 field_type: FieldType::String,
879 });
880 }
881 let desc = StructureDesc {
882 struct_id: None,
883 fields,
884 };
885 out.extend_from_slice(&encode_structure_desc(&desc, is_be));
886 for value in parameters.values() {
887 out.extend_from_slice(&encode_string_pvd(value, is_be));
888 }
889 out
890}
891
892pub fn nt_ndarray_desc_default() -> StructureDesc {
893 nt_ndarray_desc(&NtNdArray::empty())
894}
895
896pub fn nt_ndarray_desc(_nt: &NtNdArray) -> StructureDesc {
897 StructureDesc {
898 struct_id: Some("epics:nt/NTNDArray:1.0".to_string()),
899 fields: vec![
900 FieldDesc {
901 name: "value".to_string(),
902 field_type: FieldType::Union(nt_ndarray_value_union_fields()),
903 },
904 FieldDesc {
905 name: "codec".to_string(),
906 field_type: FieldType::Structure(StructureDesc {
907 struct_id: Some("codec_t".to_string()),
908 fields: vec![
909 FieldDesc {
910 name: "name".to_string(),
911 field_type: FieldType::String,
912 },
913 FieldDesc {
914 name: "parameters".to_string(),
915 field_type: FieldType::Variant,
916 },
917 ],
918 }),
919 },
920 FieldDesc {
921 name: "compressedSize".to_string(),
922 field_type: FieldType::Scalar(TypeCode::Int64),
923 },
924 FieldDesc {
925 name: "uncompressedSize".to_string(),
926 field_type: FieldType::Scalar(TypeCode::Int64),
927 },
928 FieldDesc {
929 name: "dimension".to_string(),
930 field_type: FieldType::StructureArray(StructureDesc {
931 struct_id: Some("dimension_t".to_string()),
932 fields: vec![
933 FieldDesc {
934 name: "size".to_string(),
935 field_type: FieldType::Scalar(TypeCode::Int32),
936 },
937 FieldDesc {
938 name: "offset".to_string(),
939 field_type: FieldType::Scalar(TypeCode::Int32),
940 },
941 FieldDesc {
942 name: "fullSize".to_string(),
943 field_type: FieldType::Scalar(TypeCode::Int32),
944 },
945 FieldDesc {
946 name: "binning".to_string(),
947 field_type: FieldType::Scalar(TypeCode::Int32),
948 },
949 FieldDesc {
950 name: "reverse".to_string(),
951 field_type: FieldType::Scalar(TypeCode::Boolean),
952 },
953 ],
954 }),
955 },
956 FieldDesc {
957 name: "uniqueId".to_string(),
958 field_type: FieldType::Scalar(TypeCode::Int32),
959 },
960 FieldDesc {
961 name: "dataTimeStamp".to_string(),
962 field_type: FieldType::Structure(timestamp_desc()),
963 },
964 FieldDesc {
965 name: "attribute".to_string(),
966 field_type: FieldType::StructureArray(StructureDesc {
967 struct_id: Some("NTAttribute".to_string()),
968 fields: vec![
969 FieldDesc {
970 name: "name".to_string(),
971 field_type: FieldType::String,
972 },
973 FieldDesc {
974 name: "value".to_string(),
975 field_type: FieldType::Variant,
976 },
977 FieldDesc {
978 name: "descriptor".to_string(),
979 field_type: FieldType::String,
980 },
981 FieldDesc {
982 name: "sourceType".to_string(),
983 field_type: FieldType::Scalar(TypeCode::Int32),
984 },
985 FieldDesc {
986 name: "source".to_string(),
987 field_type: FieldType::String,
988 },
989 ],
990 }),
991 },
992 FieldDesc {
993 name: "descriptor".to_string(),
994 field_type: FieldType::String,
995 },
996 FieldDesc {
997 name: "alarm".to_string(),
998 field_type: FieldType::Structure(alarm_desc()),
999 },
1000 FieldDesc {
1001 name: "timeStamp".to_string(),
1002 field_type: FieldType::Structure(timestamp_desc()),
1003 },
1004 FieldDesc {
1005 name: "display".to_string(),
1006 field_type: FieldType::Structure(display_desc()),
1007 },
1008 ],
1009 }
1010}
1011
1012fn encode_attribute_variant(attr: &NtAttribute, is_be: bool) -> Vec<u8> {
1013 match &attr.value {
1014 ScalarValue::Bool(v) => {
1015 let mut out = vec![TypeCode::Boolean as u8];
1016 out.push(if *v { 1 } else { 0 });
1017 out
1018 }
1019 ScalarValue::I8(v) => {
1020 let mut out = vec![TypeCode::Int8 as u8];
1021 out.push(*v as u8);
1022 out
1023 }
1024 ScalarValue::I16(v) => {
1025 let mut out = vec![TypeCode::Int16 as u8];
1026 out.extend_from_slice(&if is_be {
1027 v.to_be_bytes().to_vec()
1028 } else {
1029 v.to_le_bytes().to_vec()
1030 });
1031 out
1032 }
1033 ScalarValue::I32(v) => {
1034 let mut out = vec![TypeCode::Int32 as u8];
1035 out.extend_from_slice(&encode_i32(*v, is_be));
1036 out
1037 }
1038 ScalarValue::I64(v) => {
1039 let mut out = vec![TypeCode::Int64 as u8];
1040 out.extend_from_slice(&encode_i64(*v, is_be));
1041 out
1042 }
1043 ScalarValue::U8(v) => {
1044 let mut out = vec![TypeCode::UInt8 as u8];
1045 out.push(*v);
1046 out
1047 }
1048 ScalarValue::U16(v) => {
1049 let mut out = vec![TypeCode::UInt16 as u8];
1050 out.extend_from_slice(&if is_be {
1051 v.to_be_bytes().to_vec()
1052 } else {
1053 v.to_le_bytes().to_vec()
1054 });
1055 out
1056 }
1057 ScalarValue::U32(v) => {
1058 let mut out = vec![TypeCode::UInt32 as u8];
1059 out.extend_from_slice(&if is_be {
1060 v.to_be_bytes().to_vec()
1061 } else {
1062 v.to_le_bytes().to_vec()
1063 });
1064 out
1065 }
1066 ScalarValue::U64(v) => {
1067 let mut out = vec![TypeCode::UInt64 as u8];
1068 out.extend_from_slice(&if is_be {
1069 v.to_be_bytes().to_vec()
1070 } else {
1071 v.to_le_bytes().to_vec()
1072 });
1073 out
1074 }
1075 ScalarValue::F32(v) => {
1076 let mut out = vec![TypeCode::Float32 as u8];
1077 out.extend_from_slice(&if is_be {
1078 v.to_be_bytes().to_vec()
1079 } else {
1080 v.to_le_bytes().to_vec()
1081 });
1082 out
1083 }
1084 ScalarValue::F64(v) => {
1085 let mut out = vec![TypeCode::Float64 as u8];
1086 out.extend_from_slice(&encode_f64(*v, is_be));
1087 out
1088 }
1089 ScalarValue::Str(v) => {
1090 let mut out = vec![TypeCode::String as u8];
1091 out.extend_from_slice(&encode_string_pvd(v, is_be));
1092 out
1093 }
1094 }
1095}
1096
1097pub fn encode_nt_ndarray_full(nt: &NtNdArray, is_be: bool) -> Vec<u8> {
1098 let mut out = Vec::new();
1099 out.extend_from_slice(&encode_ndarray_union(&nt.value, is_be));
1100 out.extend_from_slice(&encode_string_pvd(&nt.codec.name, is_be));
1101 out.extend_from_slice(&encode_codec_parameters(&nt.codec.parameters, is_be));
1102 out.extend_from_slice(&encode_i64(nt.compressed_size, is_be));
1103 out.extend_from_slice(&encode_i64(nt.uncompressed_size, is_be));
1104 out.extend_from_slice(&encode_size_pvd(nt.dimension.len(), is_be));
1105 for NdDimension {
1106 size,
1107 offset,
1108 full_size,
1109 binning,
1110 reverse,
1111 } in &nt.dimension
1112 {
1113 out.push(1); out.extend_from_slice(&encode_i32(*size, is_be));
1115 out.extend_from_slice(&encode_i32(*offset, is_be));
1116 out.extend_from_slice(&encode_i32(*full_size, is_be));
1117 out.extend_from_slice(&encode_i32(*binning, is_be));
1118 out.push(if *reverse { 1 } else { 0 });
1119 }
1120 out.extend_from_slice(&encode_i32(nt.unique_id, is_be));
1121 out.extend_from_slice(&encode_nt_timestamp(&nt.data_time_stamp, is_be));
1122 out.extend_from_slice(&encode_size_pvd(nt.attribute.len(), is_be));
1123 for attr in &nt.attribute {
1124 out.push(1); out.extend_from_slice(&encode_string_pvd(&attr.name, is_be));
1126 out.extend_from_slice(&encode_attribute_variant(attr, is_be));
1127 out.extend_from_slice(&encode_string_pvd(&attr.descriptor, is_be));
1128 out.extend_from_slice(&encode_i32(attr.source_type, is_be));
1129 out.extend_from_slice(&encode_string_pvd(&attr.source, is_be));
1130 }
1131 out.extend_from_slice(&encode_string_pvd(
1132 nt.descriptor.as_deref().unwrap_or(""),
1133 is_be,
1134 ));
1135 out.extend_from_slice(&encode_nt_alarm(
1136 nt.alarm.as_ref().unwrap_or(&NtAlarm::default()),
1137 is_be,
1138 ));
1139 out.extend_from_slice(&encode_nt_timestamp(
1140 nt.time_stamp.as_ref().unwrap_or(&NtTimeStamp::default()),
1141 is_be,
1142 ));
1143 out.extend_from_slice(&encode_nt_display(
1144 nt.display.as_ref().unwrap_or(&NtDisplay::default()),
1145 is_be,
1146 ));
1147 out
1148}
1149
1150pub fn nt_enum_desc() -> StructureDesc {
1155 StructureDesc {
1156 struct_id: Some("epics:nt/NTEnum:1.0".to_string()),
1157 fields: vec![
1158 FieldDesc {
1159 name: "value".to_string(),
1160 field_type: FieldType::Structure(StructureDesc {
1161 struct_id: Some("enum_t".to_string()),
1162 fields: vec![
1163 FieldDesc {
1164 name: "index".to_string(),
1165 field_type: FieldType::Scalar(TypeCode::Int32),
1166 },
1167 FieldDesc {
1168 name: "choices".to_string(),
1169 field_type: FieldType::StringArray,
1170 },
1171 ],
1172 }),
1173 },
1174 FieldDesc {
1175 name: "alarm".to_string(),
1176 field_type: FieldType::Structure(alarm_desc()),
1177 },
1178 FieldDesc {
1179 name: "timeStamp".to_string(),
1180 field_type: FieldType::Structure(timestamp_desc()),
1181 },
1182 ],
1183 }
1184}
1185
1186pub fn encode_nt_enum_full(nt: &NtEnum, is_be: bool) -> Vec<u8> {
1187 let mut out = Vec::new();
1188 out.extend_from_slice(&encode_enum(nt.index, &nt.choices, is_be));
1190 out.extend_from_slice(&encode_nt_alarm(&nt.alarm, is_be));
1192 out.extend_from_slice(&encode_nt_timestamp(&nt.time_stamp, is_be));
1194 out
1195}
1196
1197fn scalar_value_type_code(v: &ScalarValue) -> TypeCode {
1202 match v {
1203 ScalarValue::Bool(_) => TypeCode::Boolean,
1204 ScalarValue::I8(_) => TypeCode::Int8,
1205 ScalarValue::I16(_) => TypeCode::Int16,
1206 ScalarValue::I32(_) => TypeCode::Int32,
1207 ScalarValue::I64(_) => TypeCode::Int64,
1208 ScalarValue::U8(_) => TypeCode::UInt8,
1209 ScalarValue::U16(_) => TypeCode::UInt16,
1210 ScalarValue::U32(_) => TypeCode::UInt32,
1211 ScalarValue::U64(_) => TypeCode::UInt64,
1212 ScalarValue::F32(_) => TypeCode::Float32,
1213 ScalarValue::F64(_) => TypeCode::Float64,
1214 ScalarValue::Str(_) => TypeCode::String,
1215 }
1216}
1217
1218pub fn pv_value_desc(struct_id: &str, fields: &[(String, PvValue)]) -> StructureDesc {
1220 StructureDesc {
1221 struct_id: if struct_id.is_empty() {
1222 None
1223 } else {
1224 Some(struct_id.to_string())
1225 },
1226 fields: fields
1227 .iter()
1228 .map(|(name, val)| FieldDesc {
1229 name: name.clone(),
1230 field_type: pv_value_field_type(val),
1231 })
1232 .collect(),
1233 }
1234}
1235
1236fn pv_value_field_type(val: &PvValue) -> FieldType {
1237 match val {
1238 PvValue::Scalar(sv) => {
1239 if matches!(sv, ScalarValue::Str(_)) {
1240 FieldType::String
1241 } else {
1242 FieldType::Scalar(scalar_value_type_code(sv))
1243 }
1244 }
1245 PvValue::ScalarArray(sa) => scalar_array_field_type(sa),
1246 PvValue::Structure { struct_id, fields } => {
1247 FieldType::Structure(pv_value_desc(struct_id, fields))
1248 }
1249 }
1250}
1251
1252pub fn encode_pv_value(val: &PvValue, is_be: bool) -> Vec<u8> {
1254 match val {
1255 PvValue::Scalar(sv) => encode_scalar_value(sv, is_be),
1256 PvValue::ScalarArray(sa) => encode_scalar_array_value_pvd(sa, is_be),
1257 PvValue::Structure { fields, .. } => {
1258 let mut out = Vec::new();
1259 for (_, v) in fields {
1260 out.extend_from_slice(&encode_pv_value(v, is_be));
1261 }
1262 out
1263 }
1264 }
1265}
1266
1267pub fn nt_payload_desc(payload: &NtPayload) -> StructureDesc {
1268 match payload {
1269 NtPayload::Scalar(nt) => nt_scalar_desc(&nt.value),
1270 NtPayload::ScalarArray(nt) => nt_scalar_array_desc(&nt.value),
1271 NtPayload::Table(nt) => nt_table_desc(nt),
1272 NtPayload::NdArray(nt) => nt_ndarray_desc(nt),
1273 NtPayload::Enum(_) => nt_enum_desc(),
1274 NtPayload::Generic { struct_id, fields } => pv_value_desc(struct_id, fields),
1275 }
1276}
1277
1278pub fn encode_nt_payload_full(payload: &NtPayload, is_be: bool) -> Vec<u8> {
1279 match payload {
1280 NtPayload::Scalar(nt) => encode_nt_scalar_full(nt, is_be),
1281 NtPayload::ScalarArray(nt) => encode_nt_scalar_array_full(nt, is_be),
1282 NtPayload::Table(nt) => encode_nt_table_full(nt, is_be),
1283 NtPayload::NdArray(nt) => encode_nt_ndarray_full(nt, is_be),
1284 NtPayload::Enum(nt) => encode_nt_enum_full(nt, is_be),
1285 NtPayload::Generic { fields, .. } => {
1286 let mut out = Vec::new();
1287 for (_, v) in fields {
1288 out.extend_from_slice(&encode_pv_value(v, is_be));
1289 }
1290 out
1291 }
1292 }
1293}
1294
1295pub fn encode_nt_payload_bitset(payload: &NtPayload, is_be: bool) -> Vec<u8> {
1296 let desc = nt_payload_desc(payload);
1297 let mut out = Vec::new();
1298 out.extend_from_slice(&encode_structure_bitset(&desc, is_be));
1299 out.extend_from_slice(&encode_nt_payload_full(payload, is_be));
1300 out
1301}
1302
1303pub fn encode_nt_payload_bitset_parts(payload: &NtPayload, is_be: bool) -> (Vec<u8>, Vec<u8>) {
1304 let desc = nt_payload_desc(payload);
1305 (
1306 encode_structure_bitset(&desc, is_be),
1307 encode_nt_payload_full(payload, is_be),
1308 )
1309}
1310
1311use crate::spvd_decode::DecodedValue;
1316
1317pub fn encode_decoded_value(val: &DecodedValue, is_be: bool) -> Vec<u8> {
1319 match val {
1320 DecodedValue::Null => Vec::new(),
1321 DecodedValue::Boolean(v) => vec![if *v { 1 } else { 0 }],
1322 DecodedValue::Int8(v) => vec![*v as u8],
1323 DecodedValue::Int16(v) => {
1324 if is_be {
1325 v.to_be_bytes().to_vec()
1326 } else {
1327 v.to_le_bytes().to_vec()
1328 }
1329 }
1330 DecodedValue::Int32(v) => encode_i32(*v, is_be),
1331 DecodedValue::Int64(v) => encode_i64(*v, is_be),
1332 DecodedValue::UInt8(v) => vec![*v],
1333 DecodedValue::UInt16(v) => {
1334 if is_be {
1335 v.to_be_bytes().to_vec()
1336 } else {
1337 v.to_le_bytes().to_vec()
1338 }
1339 }
1340 DecodedValue::UInt32(v) => {
1341 if is_be {
1342 v.to_be_bytes().to_vec()
1343 } else {
1344 v.to_le_bytes().to_vec()
1345 }
1346 }
1347 DecodedValue::UInt64(v) => {
1348 if is_be {
1349 v.to_be_bytes().to_vec()
1350 } else {
1351 v.to_le_bytes().to_vec()
1352 }
1353 }
1354 DecodedValue::Float32(v) => {
1355 if is_be {
1356 v.to_be_bytes().to_vec()
1357 } else {
1358 v.to_le_bytes().to_vec()
1359 }
1360 }
1361 DecodedValue::Float64(v) => encode_f64(*v, is_be),
1362 DecodedValue::String(v) => encode_string_pvd(v, is_be),
1363 DecodedValue::Array(arr) => {
1364 let mut out = encode_size_pvd(arr.len(), is_be);
1365 for item in arr {
1366 out.extend_from_slice(&encode_decoded_value(item, is_be));
1367 }
1368 out
1369 }
1370 DecodedValue::Structure(fields) => {
1371 let mut out = Vec::new();
1372 for (_name, value) in fields {
1373 out.extend_from_slice(&encode_decoded_value(value, is_be));
1374 }
1375 out
1376 }
1377 DecodedValue::Raw(data) => data.clone(),
1378 }
1379}
1380
1381pub fn decode_pv_request_fields(body: &[u8], is_be: bool) -> Option<Vec<String>> {
1399 if body.is_empty() {
1400 return None;
1401 }
1402 let decoder = crate::spvd_decode::PvdDecoder::new(is_be);
1403 let desc = decoder.parse_introspection(body)?;
1404 for field in &desc.fields {
1405 if field.name == "field" {
1406 if let FieldType::Structure(ref inner) = field.field_type {
1407 if inner.fields.is_empty() {
1408 return None;
1409 }
1410 let mut paths = Vec::new();
1411 collect_pv_request_paths(inner, "", &mut paths);
1412 if paths.is_empty() {
1413 return None;
1414 }
1415 return Some(paths);
1416 }
1417 }
1418 }
1419 None
1420}
1421
1422fn collect_pv_request_paths(desc: &StructureDesc, prefix: &str, out: &mut Vec<String>) {
1423 for field in &desc.fields {
1424 let joined = if prefix.is_empty() {
1425 field.name.clone()
1426 } else {
1427 format!("{}.{}", prefix, field.name)
1428 };
1429 match &field.field_type {
1430 FieldType::Structure(nested) if !nested.fields.is_empty() => {
1431 collect_pv_request_paths(nested, &joined, out);
1432 }
1433 _ => out.push(joined),
1434 }
1435 }
1436}
1437
1438pub fn filter_structure_desc(desc: &StructureDesc, requested: &[String]) -> StructureDesc {
1445 if requested.is_empty() {
1446 return desc.clone();
1447 }
1448 let tree = build_path_tree(requested);
1449 prune_structure(desc, &tree)
1450}
1451
1452#[derive(Default, Debug, Clone)]
1453struct PathNode {
1454 select_all: bool,
1457 children: Vec<(String, PathNode)>,
1461}
1462
1463impl PathNode {
1464 fn child_mut(&mut self, name: &str) -> &mut PathNode {
1465 if let Some(idx) = self.children.iter().position(|(n, _)| n == name) {
1466 return &mut self.children[idx].1;
1467 }
1468 self.children.push((name.to_string(), PathNode::default()));
1469 &mut self.children.last_mut().unwrap().1
1470 }
1471
1472 fn child(&self, name: &str) -> Option<&PathNode> {
1473 self.children
1474 .iter()
1475 .find(|(n, _)| n == name)
1476 .map(|(_, c)| c)
1477 }
1478}
1479
1480fn build_path_tree(paths: &[String]) -> PathNode {
1481 let mut root = PathNode::default();
1482 for p in paths {
1483 let parts: Vec<&str> = p.split('.').filter(|s| !s.is_empty()).collect();
1484 if parts.is_empty() {
1485 continue;
1486 }
1487 let mut node = &mut root;
1488 for (i, part) in parts.iter().enumerate() {
1489 let is_last = i == parts.len() - 1;
1490 let child = node.child_mut(part);
1491 if is_last {
1492 child.select_all = true;
1493 child.children.clear();
1494 }
1495 node = child;
1496 }
1497 }
1498 root
1499}
1500
1501fn prune_structure(desc: &StructureDesc, node: &PathNode) -> StructureDesc {
1502 if node.select_all {
1503 return desc.clone();
1504 }
1505 let mut fields = Vec::new();
1506 for field in &desc.fields {
1507 let Some(child) = node.child(&field.name) else {
1508 continue;
1509 };
1510 if child.select_all {
1511 fields.push(field.clone());
1512 continue;
1513 }
1514 match &field.field_type {
1515 FieldType::Structure(inner) => {
1516 let pruned = prune_structure(inner, child);
1517 if !pruned.fields.is_empty() {
1518 fields.push(FieldDesc {
1519 name: field.name.clone(),
1520 field_type: FieldType::Structure(pruned),
1521 });
1522 }
1523 }
1524 FieldType::StructureArray(inner) => {
1525 let pruned = prune_structure(inner, child);
1529 if !pruned.fields.is_empty() {
1530 fields.push(FieldDesc {
1531 name: field.name.clone(),
1532 field_type: FieldType::StructureArray(pruned),
1533 });
1534 }
1535 }
1536 _ => {
1537 }
1539 }
1540 }
1541 StructureDesc {
1542 struct_id: desc.struct_id.clone(),
1543 fields,
1544 }
1545}
1546
1547pub fn encode_nt_payload_filtered(
1555 payload: &NtPayload,
1556 filtered_desc: &StructureDesc,
1557 is_be: bool,
1558) -> (Vec<u8>, Vec<u8>) {
1559 let bitset = encode_structure_bitset(filtered_desc, is_be);
1560 let values = encode_nt_payload_values_for_desc(payload, filtered_desc, is_be);
1561 (bitset, values)
1562}
1563
1564pub fn encode_nt_payload_values_for_desc(
1568 payload: &NtPayload,
1569 desc: &StructureDesc,
1570 is_be: bool,
1571) -> Vec<u8> {
1572 let full_desc = nt_payload_desc(payload);
1573 if structure_desc_equal(&full_desc, desc) {
1574 return encode_nt_payload_full(payload, is_be);
1576 }
1577 let decoded = decode_payload_to_structure(payload, is_be).unwrap_or_else(|| {
1578 DecodedValue::Structure(Vec::new())
1579 });
1580 encode_decoded_projected(&decoded, desc, is_be)
1581}
1582
1583fn structure_desc_equal(a: &StructureDesc, b: &StructureDesc) -> bool {
1584 if a.struct_id != b.struct_id {
1585 return false;
1586 }
1587 if a.fields.len() != b.fields.len() {
1588 return false;
1589 }
1590 a.fields.iter().zip(&b.fields).all(|(x, y)| {
1591 x.name == y.name && field_type_equal(&x.field_type, &y.field_type)
1592 })
1593}
1594
1595fn field_type_equal(a: &FieldType, b: &FieldType) -> bool {
1596 match (a, b) {
1597 (FieldType::Scalar(x), FieldType::Scalar(y)) => x == y,
1598 (FieldType::ScalarArray(x), FieldType::ScalarArray(y)) => x == y,
1599 (FieldType::String, FieldType::String) => true,
1600 (FieldType::StringArray, FieldType::StringArray) => true,
1601 (FieldType::Structure(x), FieldType::Structure(y)) => structure_desc_equal(x, y),
1602 (FieldType::StructureArray(x), FieldType::StructureArray(y)) => {
1603 structure_desc_equal(x, y)
1604 }
1605 (FieldType::Variant, FieldType::Variant) => true,
1606 (FieldType::VariantArray, FieldType::VariantArray) => true,
1607 (FieldType::BoundedString(x), FieldType::BoundedString(y)) => x == y,
1608 (FieldType::Union(x), FieldType::Union(y)) => x.len() == y.len(),
1610 (FieldType::UnionArray(x), FieldType::UnionArray(y)) => x.len() == y.len(),
1611 _ => false,
1612 }
1613}
1614
1615fn decode_payload_to_structure(payload: &NtPayload, is_be: bool) -> Option<DecodedValue> {
1618 let desc = nt_payload_desc(payload);
1619 let bytes = encode_nt_payload_full(payload, is_be);
1620 let decoder = crate::spvd_decode::PvdDecoder::new(is_be);
1621 decoder.decode_structure(&bytes, &desc).map(|(v, _)| v)
1622}
1623
1624pub fn encode_decoded_projected(
1627 value: &DecodedValue,
1628 desc: &StructureDesc,
1629 is_be: bool,
1630) -> Vec<u8> {
1631 let DecodedValue::Structure(fields) = value else {
1632 return encode_decoded_value(value, is_be);
1634 };
1635 let mut out = Vec::new();
1636 for target in &desc.fields {
1637 let Some((_, sub_value)) = fields.iter().find(|(n, _)| n == &target.name) else {
1638 continue;
1639 };
1640 match &target.field_type {
1641 FieldType::Structure(inner) => {
1642 out.extend_from_slice(&encode_decoded_projected(sub_value, inner, is_be));
1643 }
1644 _ => {
1645 out.extend_from_slice(&encode_decoded_value(sub_value, is_be));
1646 }
1647 }
1648 }
1649 out
1650}
1651
1652fn project_payload_on_desc(
1660 payload: &NtPayload,
1661 desc: &StructureDesc,
1662 is_be: bool,
1663) -> DecodedValue {
1664 let decoded = decode_payload_to_structure(payload, is_be)
1665 .unwrap_or_else(|| DecodedValue::Structure(Vec::new()));
1666 project_decoded(&decoded, desc)
1667}
1668
1669fn project_decoded(value: &DecodedValue, desc: &StructureDesc) -> DecodedValue {
1670 let DecodedValue::Structure(fields) = value else {
1671 return value.clone();
1672 };
1673 let mut out: Vec<(String, DecodedValue)> = Vec::new();
1674 for target in &desc.fields {
1675 let Some((_, v)) = fields.iter().find(|(n, _)| n == &target.name) else {
1676 continue;
1677 };
1678 match &target.field_type {
1679 FieldType::Structure(inner) => {
1680 out.push((target.name.clone(), project_decoded(v, inner)));
1681 }
1682 _ => {
1683 out.push((target.name.clone(), v.clone()));
1684 }
1685 }
1686 }
1687 DecodedValue::Structure(out)
1688}
1689
1690pub fn decoded_values_equal(a: &DecodedValue, b: &DecodedValue) -> bool {
1693 use DecodedValue::*;
1694 match (a, b) {
1695 (Null, Null) => true,
1696 (Boolean(x), Boolean(y)) => x == y,
1697 (Int8(x), Int8(y)) => x == y,
1698 (Int16(x), Int16(y)) => x == y,
1699 (Int32(x), Int32(y)) => x == y,
1700 (Int64(x), Int64(y)) => x == y,
1701 (UInt8(x), UInt8(y)) => x == y,
1702 (UInt16(x), UInt16(y)) => x == y,
1703 (UInt32(x), UInt32(y)) => x == y,
1704 (UInt64(x), UInt64(y)) => x == y,
1705 (Float32(x), Float32(y)) => x == y || (x.is_nan() && y.is_nan()),
1706 (Float64(x), Float64(y)) => x == y || (x.is_nan() && y.is_nan()),
1707 (String(x), String(y)) => x == y,
1708 (Raw(x), Raw(y)) => x == y,
1709 (Array(x), Array(y)) => {
1710 x.len() == y.len() && x.iter().zip(y).all(|(a, b)| decoded_values_equal(a, b))
1711 }
1712 (Structure(x), Structure(y)) => {
1713 x.len() == y.len()
1714 && x.iter().zip(y).all(|((ln, lv), (rn, rv))| {
1715 ln == rn && decoded_values_equal(lv, rv)
1716 })
1717 }
1718 _ => false,
1719 }
1720}
1721
1722pub fn compute_changed_bits(
1731 prev: &DecodedValue,
1732 next: &DecodedValue,
1733 desc: &StructureDesc,
1734) -> Option<Vec<bool>> {
1735 let total = 1 + spvd_count_structure_fields(desc);
1736 let mut bits = vec![false; total];
1737 let mut idx = 1usize;
1738 let any = fill_changed_bits(prev, next, desc, &mut bits, &mut idx);
1739 if any { Some(bits) } else { None }
1740}
1741
1742fn get_field_by_name<'a>(val: &'a DecodedValue, name: &str) -> Option<&'a DecodedValue> {
1743 match val {
1744 DecodedValue::Structure(f) => f.iter().find(|(n, _)| n == name).map(|(_, v)| v),
1745 _ => None,
1746 }
1747}
1748
1749fn fill_changed_bits(
1750 prev: &DecodedValue,
1751 next: &DecodedValue,
1752 desc: &StructureDesc,
1753 bits: &mut [bool],
1754 idx: &mut usize,
1755) -> bool {
1756 let mut any = false;
1757 for field in &desc.fields {
1758 let this = *idx;
1759 *idx += 1;
1760 let p = get_field_by_name(prev, &field.name);
1761 let n = get_field_by_name(next, &field.name);
1762 match &field.field_type {
1763 FieldType::Structure(inner) => {
1764 let empty = DecodedValue::Structure(Vec::new());
1765 let pv = p.unwrap_or(&empty);
1766 let nv = n.unwrap_or(&empty);
1767 if fill_changed_bits(pv, nv, inner, bits, idx) {
1768 any = true;
1769 }
1770 }
1771 _ => {
1772 let changed = match (p, n) {
1773 (Some(a), Some(b)) => !decoded_values_equal(a, b),
1774 (Some(_), None) | (None, Some(_)) => true,
1775 (None, None) => false,
1776 };
1777 if changed {
1778 bits[this] = true;
1779 any = true;
1780 }
1781 }
1782 }
1783 }
1784 any
1785}
1786
1787fn encode_values_for_bits(
1788 value: &DecodedValue,
1789 desc: &StructureDesc,
1790 bits: &[bool],
1791 idx: &mut usize,
1792 is_be: bool,
1793 out: &mut Vec<u8>,
1794) {
1795 for field in &desc.fields {
1796 let this = *idx;
1797 *idx += 1;
1798 let sub = get_field_by_name(value, &field.name);
1799 match &field.field_type {
1800 FieldType::Structure(inner) => {
1801 let empty = DecodedValue::Structure(Vec::new());
1802 let v = sub.unwrap_or(&empty);
1803 encode_values_for_bits(v, inner, bits, idx, is_be, out);
1804 }
1805 _ => {
1806 if bits[this] {
1807 if let Some(v) = sub {
1808 out.extend_from_slice(&encode_decoded_value(v, is_be));
1809 }
1810 }
1811 }
1812 }
1813 }
1814}
1815
1816fn encode_bitset_from_flags(bits: &[bool], is_be: bool) -> Vec<u8> {
1817 let bitset_size = (bits.len() + 7) / 8;
1818 let mut bitset = vec![0u8; bitset_size];
1819 for (i, b) in bits.iter().enumerate() {
1820 if *b {
1821 bitset[i / 8] |= 1 << (i % 8);
1822 }
1823 }
1824 let mut out = Vec::new();
1825 out.extend_from_slice(&encode_size_pvd(bitset_size, is_be));
1826 out.extend_from_slice(&bitset);
1827 out
1828}
1829
1830pub fn encode_nt_payload_delta(
1835 prev: &NtPayload,
1836 next: &NtPayload,
1837 filtered_desc: &StructureDesc,
1838 is_be: bool,
1839) -> Option<(Vec<u8>, Vec<u8>)> {
1840 let prev_proj = project_payload_on_desc(prev, filtered_desc, is_be);
1841 let next_proj = project_payload_on_desc(next, filtered_desc, is_be);
1842 let bits = compute_changed_bits(&prev_proj, &next_proj, filtered_desc)?;
1843 let bitset = encode_bitset_from_flags(&bits, is_be);
1844 let mut values = Vec::new();
1845 let mut idx = 1usize;
1846 encode_values_for_bits(&next_proj, filtered_desc, &bits, &mut idx, is_be, &mut values);
1847 Some((bitset, values))
1848}
1849
1850fn spvd_count_structure_fields(desc: &StructureDesc) -> usize {
1851 let mut count = 0;
1852 for field in &desc.fields {
1853 count += 1;
1854 if let FieldType::Structure(inner) = &field.field_type {
1855 count += spvd_count_structure_fields(inner);
1856 }
1857 }
1858 count
1859}
1860
1861pub fn encode_pv_request(fields: &[&str], is_be: bool) -> Vec<u8> {
1887 let tree = build_path_tree_from_strs(fields);
1888 let inner_fields = path_tree_to_field_descs(&tree);
1889
1890 let field_desc = StructureDesc {
1891 struct_id: None,
1892 fields: inner_fields,
1893 };
1894
1895 let pv_request_desc = StructureDesc {
1896 struct_id: None,
1897 fields: vec![FieldDesc {
1898 name: "field".to_string(),
1899 field_type: FieldType::Structure(field_desc),
1900 }],
1901 };
1902
1903 let mut out = Vec::new();
1904 out.push(0x80); out.extend_from_slice(&encode_structure_desc(&pv_request_desc, is_be));
1906 out
1909}
1910
1911fn build_path_tree_from_strs(paths: &[&str]) -> PathNode {
1912 let owned: Vec<String> = paths.iter().map(|s| (*s).to_string()).collect();
1913 build_path_tree(&owned)
1914}
1915
1916fn path_tree_to_field_descs(node: &PathNode) -> Vec<FieldDesc> {
1917 node.children
1918 .iter()
1919 .map(|(name, child)| {
1920 let nested_fields = if child.select_all {
1921 Vec::new()
1922 } else {
1923 path_tree_to_field_descs(child)
1924 };
1925 FieldDesc {
1926 name: name.clone(),
1927 field_type: FieldType::Structure(StructureDesc {
1928 struct_id: None,
1929 fields: nested_fields,
1930 }),
1931 }
1932 })
1933 .collect()
1934}
1935
1936#[cfg(test)]
1937mod tests {
1938 use super::*;
1939 use crate::spvd_decode::PvdDecoder;
1940
1941 #[test]
1942 fn nt_scalar_roundtrip() {
1943 let nt = NtScalar::from_value(ScalarValue::F64(12.5));
1944 let desc = nt_scalar_desc(&nt.value);
1945 let desc_bytes = encode_structure_desc(&desc, false);
1946 let mut pvd = Vec::new();
1947 pvd.push(0x80);
1948 pvd.extend_from_slice(&desc_bytes);
1949 pvd.extend_from_slice(&encode_nt_scalar_full(&nt, false));
1950
1951 let decoder = PvdDecoder::new(false);
1952 let parsed_desc = decoder.parse_introspection(&pvd).expect("desc");
1953 let (_, consumed) = decoder
1954 .decode_structure(&pvd[1 + desc_bytes.len()..], &parsed_desc)
1955 .expect("value");
1956 assert!(consumed > 0);
1957 }
1958
1959 #[test]
1960 fn nt_ndarray_roundtrip() {
1961 use spvirit_types::{
1962 NdCodec, NdDimension, NtAlarm, NtNdArray, NtTimeStamp, ScalarArrayValue,
1963 };
1964 use std::collections::HashMap;
1965
1966 let nt = NtNdArray {
1967 value: ScalarArrayValue::U8(vec![1, 2, 3, 4]),
1968 codec: NdCodec {
1969 name: String::new(),
1970 parameters: HashMap::new(),
1971 },
1972 compressed_size: 4,
1973 uncompressed_size: 4,
1974 dimension: vec![NdDimension {
1975 size: 2,
1976 offset: 0,
1977 full_size: 2,
1978 binning: 1,
1979 reverse: false,
1980 }],
1981 unique_id: 42,
1982 data_time_stamp: NtTimeStamp {
1983 seconds_past_epoch: 1000,
1984 nanoseconds: 500,
1985 user_tag: 0,
1986 },
1987 attribute: Vec::new(),
1988 descriptor: Some("test".to_string()),
1989 alarm: Some(NtAlarm::default()),
1990 time_stamp: Some(NtTimeStamp::default()),
1991 display: None,
1992 };
1993
1994 let desc = nt_ndarray_desc(&nt);
1995 let desc_bytes = encode_structure_desc(&desc, false);
1996 let data_bytes = encode_nt_ndarray_full(&nt, false);
1997
1998 let mut pvd = Vec::new();
2000 pvd.push(0x80);
2001 pvd.extend_from_slice(&desc_bytes);
2002 pvd.extend_from_slice(&data_bytes);
2003
2004 let decoder = PvdDecoder::new(false);
2005 let parsed_desc = decoder
2006 .parse_introspection(&pvd)
2007 .expect("desc parse failed");
2008 let data_start = 1 + desc_bytes.len();
2009 let (_decoded, consumed) = decoder
2010 .decode_structure(&pvd[data_start..], &parsed_desc)
2011 .expect("data decode failed");
2012 assert!(consumed > 0, "consumed should be > 0");
2013 assert_eq!(
2014 consumed,
2015 data_bytes.len(),
2016 "consumed should match data_bytes.len()"
2017 );
2018 }
2019
2020 #[test]
2021 fn pv_request_flat_roundtrip() {
2022 for is_be in [false, true] {
2023 let body = encode_pv_request(&["value", "alarm", "timeStamp"], is_be);
2024 let fields = decode_pv_request_fields(&body, is_be).expect("fields");
2025 assert_eq!(fields, vec!["value", "alarm", "timeStamp"]);
2026 }
2027 }
2028
2029 #[test]
2030 fn pv_request_nested_roundtrip() {
2031 let body = encode_pv_request(&["alarm.severity", "timeStamp.secondsPastEpoch"], false);
2032 let fields = decode_pv_request_fields(&body, false).expect("fields");
2033 assert_eq!(
2034 fields,
2035 vec!["alarm.severity".to_string(), "timeStamp.secondsPastEpoch".to_string()]
2036 );
2037 }
2038
2039 #[test]
2040 fn pv_request_whole_subtree_beats_leaf() {
2041 let body = encode_pv_request(&["alarm.severity", "alarm"], false);
2044 let fields = decode_pv_request_fields(&body, false).expect("fields");
2045 assert_eq!(fields, vec!["alarm".to_string()]);
2046 }
2047
2048 #[test]
2049 fn pv_request_empty_body_none() {
2050 assert!(decode_pv_request_fields(&[], false).is_none());
2051 }
2052
2053 #[test]
2054 fn filter_structure_desc_nested() {
2055 let alarm = StructureDesc {
2056 struct_id: Some("alarm_t".to_string()),
2057 fields: vec![
2058 FieldDesc { name: "severity".into(), field_type: FieldType::Scalar(TypeCode::Int32) },
2059 FieldDesc { name: "status".into(), field_type: FieldType::Scalar(TypeCode::Int32) },
2060 FieldDesc { name: "message".into(), field_type: FieldType::String },
2061 ],
2062 };
2063 let desc = StructureDesc {
2064 struct_id: Some("epics:nt/NTScalar:1.0".into()),
2065 fields: vec![
2066 FieldDesc { name: "value".into(), field_type: FieldType::Scalar(TypeCode::Float64) },
2067 FieldDesc { name: "alarm".into(), field_type: FieldType::Structure(alarm.clone()) },
2068 ],
2069 };
2070
2071 let pruned = filter_structure_desc(&desc, &["alarm.severity".to_string()]);
2072 assert_eq!(pruned.fields.len(), 1);
2073 assert_eq!(pruned.fields[0].name, "alarm");
2074 match &pruned.fields[0].field_type {
2075 FieldType::Structure(inner) => {
2076 assert_eq!(inner.fields.len(), 1);
2077 assert_eq!(inner.fields[0].name, "severity");
2078 }
2079 other => panic!("expected Structure, got {:?}", other),
2080 }
2081
2082 let pruned_all = filter_structure_desc(&desc, &["alarm".to_string()]);
2084 match &pruned_all.fields[0].field_type {
2085 FieldType::Structure(inner) => assert_eq!(inner.fields.len(), 3),
2086 other => panic!("expected Structure, got {:?}", other),
2087 }
2088
2089 let pruned_unknown = filter_structure_desc(&desc, &["nope".into(), "alarm.missing".into()]);
2091 assert!(pruned_unknown.fields.is_empty());
2092 }
2093
2094 #[test]
2095 fn filtered_monitor_round_trip_nested() {
2096 use crate::spvd_decode::PvdDecoder;
2097 use spvirit_types::{NtPayload, NtScalar, ScalarValue};
2098
2099 let mut nt = NtScalar::from_value(ScalarValue::F64(42.0));
2100 nt.alarm_severity = 2;
2101 nt.alarm_status = 7;
2102 nt.alarm_message = "hi".into();
2103 let payload = NtPayload::Scalar(nt);
2104
2105 let full_desc = nt_payload_desc(&payload);
2107 let paths = vec!["alarm.severity".to_string()];
2108 let filtered = filter_structure_desc(&full_desc, &paths);
2109 let (bitset, values) = encode_nt_payload_filtered(&payload, &filtered, false);
2110
2111 let decoder = PvdDecoder::new(false);
2113 let mut body = bitset.clone();
2114 body.extend_from_slice(&values);
2115 let (decoded, _) = decoder
2116 .decode_structure_with_bitset(&body, &filtered)
2117 .expect("decode filtered");
2118
2119 let DecodedValue::Structure(fields) = decoded else {
2120 panic!("expected structure");
2121 };
2122 assert_eq!(fields.len(), 1);
2123 assert_eq!(fields[0].0, "alarm");
2124 match &fields[0].1 {
2125 DecodedValue::Structure(inner) => {
2126 assert_eq!(inner.len(), 1);
2127 assert_eq!(inner[0].0, "severity");
2128 assert!(matches!(inner[0].1, DecodedValue::Int32(2)));
2129 }
2130 other => panic!("expected Structure, got {:?}", other),
2131 }
2132
2133 let full_body_len = encode_nt_payload_full(&payload, false).len();
2136 assert!(values.len() < full_body_len);
2137 }
2138
2139 #[test]
2140 fn delta_returns_none_when_nothing_changed() {
2141 use spvirit_types::{NtPayload, NtScalar, ScalarValue};
2142 let mut a = NtScalar::from_value(ScalarValue::F64(1.0));
2143 a.alarm_severity = 1;
2144 let p1 = NtPayload::Scalar(a.clone());
2145 let p2 = NtPayload::Scalar(a);
2146 let desc = filter_structure_desc(&nt_payload_desc(&p1), &["alarm.severity".to_string()]);
2147 assert!(encode_nt_payload_delta(&p1, &p2, &desc, false).is_none());
2148 }
2149
2150 #[test]
2151 fn delta_marks_only_changed_leaf() {
2152 use crate::spvd_decode::PvdDecoder;
2153 use spvirit_types::{NtPayload, NtScalar, ScalarValue};
2154
2155 let mut a = NtScalar::from_value(ScalarValue::F64(1.0));
2156 a.alarm_severity = 1;
2157 a.alarm_status = 0;
2158 a.alarm_message = "ok".into();
2159 let mut b = a.clone();
2160 b.alarm_severity = 2; let p1 = NtPayload::Scalar(a);
2162 let p2 = NtPayload::Scalar(b);
2163 let desc = filter_structure_desc(
2164 &nt_payload_desc(&p1),
2165 &[
2166 "alarm.severity".to_string(),
2167 "alarm.status".to_string(),
2168 "alarm.message".to_string(),
2169 ],
2170 );
2171
2172 let (bitset, values) = encode_nt_payload_delta(&p1, &p2, &desc, false)
2173 .expect("delta must produce a frame when a leaf changed");
2174
2175 assert_eq!(bitset[0], 1u8, "size prefix");
2181 let b0 = bitset[1];
2182 assert_eq!(b0 & 0x01, 0, "root bit must be clear");
2183 assert_eq!(b0 & 0x02, 0, "alarm struct bit must be clear");
2184 assert_eq!(b0 & 0x04, 0x04, "severity bit must be set");
2185 assert_eq!(b0 & 0x08, 0, "status bit must be clear");
2186 assert_eq!(b0 & 0x10, 0, "message bit must be clear");
2187
2188 assert_eq!(values.len(), 4);
2190
2191 let decoder = PvdDecoder::new(false);
2193 let mut body = bitset.clone();
2194 body.extend_from_slice(&values);
2195 let (decoded, _) = decoder
2196 .decode_structure_with_bitset(&body, &desc)
2197 .expect("decode delta");
2198 let DecodedValue::Structure(fields) = decoded else {
2199 panic!("expected struct")
2200 };
2201 assert_eq!(fields.len(), 1);
2202 assert_eq!(fields[0].0, "alarm");
2203 match &fields[0].1 {
2204 DecodedValue::Structure(inner) => {
2205 assert_eq!(inner.len(), 1);
2206 assert_eq!(inner[0].0, "severity");
2207 assert!(matches!(inner[0].1, DecodedValue::Int32(2)));
2208 }
2209 other => panic!("expected struct got {:?}", other),
2210 }
2211 }
2212
2213 #[test]
2214 fn decoded_values_equal_treats_nan_as_equal() {
2215 let a = DecodedValue::Float64(f64::NAN);
2216 let b = DecodedValue::Float64(f64::NAN);
2217 assert!(decoded_values_equal(&a, &b));
2218 let c = DecodedValue::Float32(f32::NAN);
2219 let d = DecodedValue::Float32(f32::NAN);
2220 assert!(decoded_values_equal(&c, &d));
2221 assert!(!decoded_values_equal(
2223 &DecodedValue::Float64(1.0),
2224 &DecodedValue::Float64(2.0)
2225 ));
2226 }
2227}