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>> {
1391 if body.is_empty() {
1392 return None;
1393 }
1394 let decoder = crate::spvd_decode::PvdDecoder::new(is_be);
1395 let desc = decoder.parse_introspection(body)?;
1396 for field in &desc.fields {
1398 if field.name == "field" {
1399 if let FieldType::Structure(ref inner) = field.field_type {
1400 if inner.fields.is_empty() {
1401 return None;
1403 }
1404 let names: Vec<String> = inner.fields.iter().map(|f| f.name.clone()).collect();
1405 return Some(names);
1406 }
1407 }
1408 }
1409 None
1410}
1411
1412pub fn filter_structure_desc(desc: &StructureDesc, requested: &[String]) -> StructureDesc {
1416 if requested.is_empty() {
1417 return desc.clone();
1418 }
1419 StructureDesc {
1420 struct_id: desc.struct_id.clone(),
1421 fields: desc
1422 .fields
1423 .iter()
1424 .filter(|f| requested.iter().any(|r| r == &f.name))
1425 .cloned()
1426 .collect(),
1427 }
1428}
1429
1430pub fn encode_nt_payload_filtered(
1435 payload: &NtPayload,
1436 filtered_desc: &StructureDesc,
1437 is_be: bool,
1438) -> (Vec<u8>, Vec<u8>) {
1439 let requested: Vec<&str> = filtered_desc
1440 .fields
1441 .iter()
1442 .map(|f| f.name.as_str())
1443 .collect();
1444 let full_desc = nt_payload_desc(payload);
1445 let full_fields = &full_desc.fields;
1446
1447 let field_bytes: Vec<(&str, Vec<u8>)> = encode_nt_payload_fields(payload, full_fields, is_be);
1449
1450 let mut values = Vec::new();
1452 for (name, bytes) in &field_bytes {
1453 if requested.iter().any(|r| *r == *name) {
1454 values.extend_from_slice(bytes);
1455 }
1456 }
1457
1458 let bitset = encode_structure_bitset(filtered_desc, is_be);
1459 (bitset, values)
1460}
1461
1462fn encode_nt_table_field(nt: &NtTable, name: &str, is_be: bool) -> Vec<u8> {
1465 match name {
1466 "labels" => encode_string_array(&nt.labels, is_be),
1467 "value" => {
1468 let mut out = Vec::new();
1469 for NtTableColumn { values, .. } in &nt.columns {
1470 out.extend_from_slice(&encode_scalar_array_value_pvd(values, is_be));
1471 }
1472 out
1473 }
1474 _ => Vec::new(),
1475 }
1476}
1477
1478fn encode_nt_ndarray_field(nt: &NtNdArray, name: &str, is_be: bool) -> Vec<u8> {
1479 match name {
1480 "value" => encode_ndarray_union(&nt.value, is_be),
1481 "codec" => {
1482 let mut out = Vec::new();
1483 out.extend_from_slice(&encode_string_pvd(&nt.codec.name, is_be));
1484 out.extend_from_slice(&encode_codec_parameters(&nt.codec.parameters, is_be));
1485 out
1486 }
1487 "compressedSize" => encode_i64(nt.compressed_size, is_be),
1488 "uncompressedSize" => encode_i64(nt.uncompressed_size, is_be),
1489 "dimension" => {
1490 let mut out = encode_size_pvd(nt.dimension.len(), is_be);
1491 for d in &nt.dimension {
1492 out.push(1);
1493 out.extend_from_slice(&encode_i32(d.size, is_be));
1494 out.extend_from_slice(&encode_i32(d.offset, is_be));
1495 out.extend_from_slice(&encode_i32(d.full_size, is_be));
1496 out.extend_from_slice(&encode_i32(d.binning, is_be));
1497 out.push(if d.reverse { 1 } else { 0 });
1498 }
1499 out
1500 }
1501 "uniqueId" => encode_i32(nt.unique_id, is_be),
1502 "dataTimeStamp" => encode_nt_timestamp(&nt.data_time_stamp, is_be),
1503 "attribute" => {
1504 let mut out = encode_size_pvd(nt.attribute.len(), is_be);
1505 for attr in &nt.attribute {
1506 out.push(1);
1507 out.extend_from_slice(&encode_string_pvd(&attr.name, is_be));
1508 out.extend_from_slice(&encode_attribute_variant(attr, is_be));
1509 out.extend_from_slice(&encode_string_pvd(&attr.descriptor, is_be));
1510 out.extend_from_slice(&encode_i32(attr.source_type, is_be));
1511 out.extend_from_slice(&encode_string_pvd(&attr.source, is_be));
1512 }
1513 out
1514 }
1515 "descriptor" => encode_string_pvd(nt.descriptor.as_deref().unwrap_or(""), is_be),
1516 "alarm" => encode_nt_alarm(nt.alarm.as_ref().unwrap_or(&NtAlarm::default()), is_be),
1517 "timeStamp" => encode_nt_timestamp(
1518 nt.time_stamp.as_ref().unwrap_or(&NtTimeStamp::default()),
1519 is_be,
1520 ),
1521 "display" => encode_nt_display(nt.display.as_ref().unwrap_or(&NtDisplay::default()), is_be),
1522 _ => Vec::new(),
1523 }
1524}
1525
1526fn encode_nt_payload_fields<'a>(
1527 payload: &'a NtPayload,
1528 full_fields: &'a [FieldDesc],
1529 is_be: bool,
1530) -> Vec<(&'a str, Vec<u8>)> {
1531 fn scalar_field(nt: &NtScalar, name: &str, is_be: bool) -> Vec<u8> {
1533 match name {
1534 "value" => encode_scalar_value(&nt.value, is_be),
1535 "alarm" => encode_alarm(nt, is_be),
1536 "timeStamp" => encode_timestamp(nt, is_be),
1537 "display" => encode_display(nt, is_be),
1538 "control" => encode_control(nt, is_be),
1539 "valueAlarm" => encode_value_alarm(nt, is_be),
1540 _ => Vec::new(),
1541 }
1542 }
1543
1544 fn scalar_array_field(nt: &NtScalarArray, name: &str, is_be: bool) -> Vec<u8> {
1545 match name {
1546 "value" => encode_scalar_array_value_pvd(&nt.value, is_be),
1547 "alarm" => encode_nt_alarm(&nt.alarm, is_be),
1548 "timeStamp" => encode_nt_timestamp(&nt.time_stamp, is_be),
1549 "display" => encode_nt_display(&nt.display, is_be),
1550 "control" => {
1551 let mut out = Vec::new();
1552 out.extend_from_slice(&encode_f64(nt.control.limit_low, is_be));
1553 out.extend_from_slice(&encode_f64(nt.control.limit_high, is_be));
1554 out.extend_from_slice(&encode_f64(nt.control.min_step, is_be));
1555 out
1556 }
1557 _ => Vec::new(),
1558 }
1559 }
1560
1561 fn enum_field(nt: &NtEnum, name: &str, is_be: bool) -> Vec<u8> {
1562 match name {
1563 "value" => encode_enum(nt.index, &nt.choices, is_be),
1564 "alarm" => encode_nt_alarm(&nt.alarm, is_be),
1565 "timeStamp" => encode_nt_timestamp(&nt.time_stamp, is_be),
1566 _ => Vec::new(),
1567 }
1568 }
1569
1570 full_fields
1571 .iter()
1572 .map(|f| {
1573 let name = f.name.as_str();
1574 let bytes = match payload {
1575 NtPayload::Scalar(nt) => scalar_field(nt, name, is_be),
1576 NtPayload::ScalarArray(nt) => scalar_array_field(nt, name, is_be),
1577 NtPayload::Table(nt) => encode_nt_table_field(nt, name, is_be),
1578 NtPayload::NdArray(nt) => encode_nt_ndarray_field(nt, name, is_be),
1579 NtPayload::Enum(nt) => enum_field(nt, name, is_be),
1580 NtPayload::Generic { fields, .. } => {
1581 if let Some((_, v)) = fields.iter().find(|(n, _)| n == name) {
1582 encode_pv_value(v, is_be)
1583 } else {
1584 Vec::new()
1585 }
1586 }
1587 };
1588 (name, bytes)
1589 })
1590 .collect()
1591}
1592
1593pub fn encode_pv_request(fields: &[&str], is_be: bool) -> Vec<u8> {
1607 let inner_fields: Vec<FieldDesc> = fields
1610 .iter()
1611 .map(|name| FieldDesc {
1612 name: name.to_string(),
1613 field_type: FieldType::Structure(StructureDesc {
1614 struct_id: None,
1615 fields: Vec::new(),
1616 }),
1617 })
1618 .collect();
1619
1620 let field_desc = StructureDesc {
1621 struct_id: None,
1622 fields: inner_fields,
1623 };
1624
1625 let pv_request_desc = StructureDesc {
1626 struct_id: None,
1627 fields: vec![FieldDesc {
1628 name: "field".to_string(),
1629 field_type: FieldType::Structure(field_desc),
1630 }],
1631 };
1632
1633 let mut out = Vec::new();
1634 out.push(0x80); out.extend_from_slice(&encode_structure_desc(&pv_request_desc, is_be));
1636 out
1639}
1640
1641#[cfg(test)]
1642mod tests {
1643 use super::*;
1644 use crate::spvd_decode::PvdDecoder;
1645
1646 #[test]
1647 fn nt_scalar_roundtrip() {
1648 let nt = NtScalar::from_value(ScalarValue::F64(12.5));
1649 let desc = nt_scalar_desc(&nt.value);
1650 let desc_bytes = encode_structure_desc(&desc, false);
1651 let mut pvd = Vec::new();
1652 pvd.push(0x80);
1653 pvd.extend_from_slice(&desc_bytes);
1654 pvd.extend_from_slice(&encode_nt_scalar_full(&nt, false));
1655
1656 let decoder = PvdDecoder::new(false);
1657 let parsed_desc = decoder.parse_introspection(&pvd).expect("desc");
1658 let (_, consumed) = decoder
1659 .decode_structure(&pvd[1 + desc_bytes.len()..], &parsed_desc)
1660 .expect("value");
1661 assert!(consumed > 0);
1662 }
1663
1664 #[test]
1665 fn nt_ndarray_roundtrip() {
1666 use spvirit_types::{
1667 NdCodec, NdDimension, NtAlarm, NtNdArray, NtTimeStamp, ScalarArrayValue,
1668 };
1669 use std::collections::HashMap;
1670
1671 let nt = NtNdArray {
1672 value: ScalarArrayValue::U8(vec![1, 2, 3, 4]),
1673 codec: NdCodec {
1674 name: String::new(),
1675 parameters: HashMap::new(),
1676 },
1677 compressed_size: 4,
1678 uncompressed_size: 4,
1679 dimension: vec![NdDimension {
1680 size: 2,
1681 offset: 0,
1682 full_size: 2,
1683 binning: 1,
1684 reverse: false,
1685 }],
1686 unique_id: 42,
1687 data_time_stamp: NtTimeStamp {
1688 seconds_past_epoch: 1000,
1689 nanoseconds: 500,
1690 user_tag: 0,
1691 },
1692 attribute: Vec::new(),
1693 descriptor: Some("test".to_string()),
1694 alarm: Some(NtAlarm::default()),
1695 time_stamp: Some(NtTimeStamp::default()),
1696 display: None,
1697 };
1698
1699 let desc = nt_ndarray_desc(&nt);
1700 let desc_bytes = encode_structure_desc(&desc, false);
1701 let data_bytes = encode_nt_ndarray_full(&nt, false);
1702
1703 let mut pvd = Vec::new();
1705 pvd.push(0x80);
1706 pvd.extend_from_slice(&desc_bytes);
1707 pvd.extend_from_slice(&data_bytes);
1708
1709 let decoder = PvdDecoder::new(false);
1710 let parsed_desc = decoder
1711 .parse_introspection(&pvd)
1712 .expect("desc parse failed");
1713 let data_start = 1 + desc_bytes.len();
1714 let (_decoded, consumed) = decoder
1715 .decode_structure(&pvd[data_start..], &parsed_desc)
1716 .expect("data decode failed");
1717 assert!(consumed > 0, "consumed should be > 0");
1718 assert_eq!(
1719 consumed,
1720 data_bytes.len(),
1721 "consumed should match data_bytes.len()"
1722 );
1723 }
1724}