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(_nt: &NtNdArray) -> StructureDesc {
893 StructureDesc {
894 struct_id: Some("epics:nt/NTNDArray:1.0".to_string()),
895 fields: vec![
896 FieldDesc {
897 name: "value".to_string(),
898 field_type: FieldType::Union(nt_ndarray_value_union_fields()),
899 },
900 FieldDesc {
901 name: "codec".to_string(),
902 field_type: FieldType::Structure(StructureDesc {
903 struct_id: Some("codec_t".to_string()),
904 fields: vec![
905 FieldDesc {
906 name: "name".to_string(),
907 field_type: FieldType::String,
908 },
909 FieldDesc {
910 name: "parameters".to_string(),
911 field_type: FieldType::Variant,
912 },
913 ],
914 }),
915 },
916 FieldDesc {
917 name: "compressedSize".to_string(),
918 field_type: FieldType::Scalar(TypeCode::Int64),
919 },
920 FieldDesc {
921 name: "uncompressedSize".to_string(),
922 field_type: FieldType::Scalar(TypeCode::Int64),
923 },
924 FieldDesc {
925 name: "dimension".to_string(),
926 field_type: FieldType::StructureArray(StructureDesc {
927 struct_id: Some("dimension_t".to_string()),
928 fields: vec![
929 FieldDesc {
930 name: "size".to_string(),
931 field_type: FieldType::Scalar(TypeCode::Int32),
932 },
933 FieldDesc {
934 name: "offset".to_string(),
935 field_type: FieldType::Scalar(TypeCode::Int32),
936 },
937 FieldDesc {
938 name: "fullSize".to_string(),
939 field_type: FieldType::Scalar(TypeCode::Int32),
940 },
941 FieldDesc {
942 name: "binning".to_string(),
943 field_type: FieldType::Scalar(TypeCode::Int32),
944 },
945 FieldDesc {
946 name: "reverse".to_string(),
947 field_type: FieldType::Scalar(TypeCode::Boolean),
948 },
949 ],
950 }),
951 },
952 FieldDesc {
953 name: "uniqueId".to_string(),
954 field_type: FieldType::Scalar(TypeCode::Int32),
955 },
956 FieldDesc {
957 name: "dataTimeStamp".to_string(),
958 field_type: FieldType::Structure(timestamp_desc()),
959 },
960 FieldDesc {
961 name: "attribute".to_string(),
962 field_type: FieldType::StructureArray(StructureDesc {
963 struct_id: Some("NTAttribute".to_string()),
964 fields: vec![
965 FieldDesc {
966 name: "name".to_string(),
967 field_type: FieldType::String,
968 },
969 FieldDesc {
970 name: "value".to_string(),
971 field_type: FieldType::Variant,
972 },
973 FieldDesc {
974 name: "descriptor".to_string(),
975 field_type: FieldType::String,
976 },
977 FieldDesc {
978 name: "sourceType".to_string(),
979 field_type: FieldType::Scalar(TypeCode::Int32),
980 },
981 FieldDesc {
982 name: "source".to_string(),
983 field_type: FieldType::String,
984 },
985 ],
986 }),
987 },
988 FieldDesc {
989 name: "descriptor".to_string(),
990 field_type: FieldType::String,
991 },
992 FieldDesc {
993 name: "alarm".to_string(),
994 field_type: FieldType::Structure(alarm_desc()),
995 },
996 FieldDesc {
997 name: "timeStamp".to_string(),
998 field_type: FieldType::Structure(timestamp_desc()),
999 },
1000 FieldDesc {
1001 name: "display".to_string(),
1002 field_type: FieldType::Structure(display_desc()),
1003 },
1004 ],
1005 }
1006}
1007
1008fn encode_attribute_variant(attr: &NtAttribute, is_be: bool) -> Vec<u8> {
1009 match &attr.value {
1010 ScalarValue::Bool(v) => {
1011 let mut out = vec![TypeCode::Boolean as u8];
1012 out.push(if *v { 1 } else { 0 });
1013 out
1014 }
1015 ScalarValue::I8(v) => {
1016 let mut out = vec![TypeCode::Int8 as u8];
1017 out.push(*v as u8);
1018 out
1019 }
1020 ScalarValue::I16(v) => {
1021 let mut out = vec![TypeCode::Int16 as u8];
1022 out.extend_from_slice(&if is_be {
1023 v.to_be_bytes().to_vec()
1024 } else {
1025 v.to_le_bytes().to_vec()
1026 });
1027 out
1028 }
1029 ScalarValue::I32(v) => {
1030 let mut out = vec![TypeCode::Int32 as u8];
1031 out.extend_from_slice(&encode_i32(*v, is_be));
1032 out
1033 }
1034 ScalarValue::I64(v) => {
1035 let mut out = vec![TypeCode::Int64 as u8];
1036 out.extend_from_slice(&encode_i64(*v, is_be));
1037 out
1038 }
1039 ScalarValue::U8(v) => {
1040 let mut out = vec![TypeCode::UInt8 as u8];
1041 out.push(*v);
1042 out
1043 }
1044 ScalarValue::U16(v) => {
1045 let mut out = vec![TypeCode::UInt16 as u8];
1046 out.extend_from_slice(&if is_be {
1047 v.to_be_bytes().to_vec()
1048 } else {
1049 v.to_le_bytes().to_vec()
1050 });
1051 out
1052 }
1053 ScalarValue::U32(v) => {
1054 let mut out = vec![TypeCode::UInt32 as u8];
1055 out.extend_from_slice(&if is_be {
1056 v.to_be_bytes().to_vec()
1057 } else {
1058 v.to_le_bytes().to_vec()
1059 });
1060 out
1061 }
1062 ScalarValue::U64(v) => {
1063 let mut out = vec![TypeCode::UInt64 as u8];
1064 out.extend_from_slice(&if is_be {
1065 v.to_be_bytes().to_vec()
1066 } else {
1067 v.to_le_bytes().to_vec()
1068 });
1069 out
1070 }
1071 ScalarValue::F32(v) => {
1072 let mut out = vec![TypeCode::Float32 as u8];
1073 out.extend_from_slice(&if is_be {
1074 v.to_be_bytes().to_vec()
1075 } else {
1076 v.to_le_bytes().to_vec()
1077 });
1078 out
1079 }
1080 ScalarValue::F64(v) => {
1081 let mut out = vec![TypeCode::Float64 as u8];
1082 out.extend_from_slice(&encode_f64(*v, is_be));
1083 out
1084 }
1085 ScalarValue::Str(v) => {
1086 let mut out = vec![TypeCode::String as u8];
1087 out.extend_from_slice(&encode_string_pvd(v, is_be));
1088 out
1089 }
1090 }
1091}
1092
1093pub fn encode_nt_ndarray_full(nt: &NtNdArray, is_be: bool) -> Vec<u8> {
1094 let mut out = Vec::new();
1095 out.extend_from_slice(&encode_ndarray_union(&nt.value, is_be));
1096 out.extend_from_slice(&encode_string_pvd(&nt.codec.name, is_be));
1097 out.extend_from_slice(&encode_codec_parameters(&nt.codec.parameters, is_be));
1098 out.extend_from_slice(&encode_i64(nt.compressed_size, is_be));
1099 out.extend_from_slice(&encode_i64(nt.uncompressed_size, is_be));
1100 out.extend_from_slice(&encode_size_pvd(nt.dimension.len(), is_be));
1101 for NdDimension {
1102 size,
1103 offset,
1104 full_size,
1105 binning,
1106 reverse,
1107 } in &nt.dimension
1108 {
1109 out.push(1); out.extend_from_slice(&encode_i32(*size, is_be));
1111 out.extend_from_slice(&encode_i32(*offset, is_be));
1112 out.extend_from_slice(&encode_i32(*full_size, is_be));
1113 out.extend_from_slice(&encode_i32(*binning, is_be));
1114 out.push(if *reverse { 1 } else { 0 });
1115 }
1116 out.extend_from_slice(&encode_i32(nt.unique_id, is_be));
1117 out.extend_from_slice(&encode_nt_timestamp(&nt.data_time_stamp, is_be));
1118 out.extend_from_slice(&encode_size_pvd(nt.attribute.len(), is_be));
1119 for attr in &nt.attribute {
1120 out.push(1); out.extend_from_slice(&encode_string_pvd(&attr.name, is_be));
1122 out.extend_from_slice(&encode_attribute_variant(attr, is_be));
1123 out.extend_from_slice(&encode_string_pvd(&attr.descriptor, is_be));
1124 out.extend_from_slice(&encode_i32(attr.source_type, is_be));
1125 out.extend_from_slice(&encode_string_pvd(&attr.source, is_be));
1126 }
1127 out.extend_from_slice(&encode_string_pvd(
1128 nt.descriptor.as_deref().unwrap_or(""),
1129 is_be,
1130 ));
1131 out.extend_from_slice(&encode_nt_alarm(
1132 nt.alarm.as_ref().unwrap_or(&NtAlarm::default()),
1133 is_be,
1134 ));
1135 out.extend_from_slice(&encode_nt_timestamp(
1136 nt.time_stamp.as_ref().unwrap_or(&NtTimeStamp::default()),
1137 is_be,
1138 ));
1139 out.extend_from_slice(&encode_nt_display(
1140 nt.display.as_ref().unwrap_or(&NtDisplay::default()),
1141 is_be,
1142 ));
1143 out
1144}
1145
1146pub fn nt_enum_desc() -> StructureDesc {
1151 StructureDesc {
1152 struct_id: Some("epics:nt/NTEnum:1.0".to_string()),
1153 fields: vec![
1154 FieldDesc {
1155 name: "value".to_string(),
1156 field_type: FieldType::Structure(StructureDesc {
1157 struct_id: Some("enum_t".to_string()),
1158 fields: vec![
1159 FieldDesc {
1160 name: "index".to_string(),
1161 field_type: FieldType::Scalar(TypeCode::Int32),
1162 },
1163 FieldDesc {
1164 name: "choices".to_string(),
1165 field_type: FieldType::StringArray,
1166 },
1167 ],
1168 }),
1169 },
1170 FieldDesc {
1171 name: "alarm".to_string(),
1172 field_type: FieldType::Structure(alarm_desc()),
1173 },
1174 FieldDesc {
1175 name: "timeStamp".to_string(),
1176 field_type: FieldType::Structure(timestamp_desc()),
1177 },
1178 ],
1179 }
1180}
1181
1182pub fn encode_nt_enum_full(nt: &NtEnum, is_be: bool) -> Vec<u8> {
1183 let mut out = Vec::new();
1184 out.extend_from_slice(&encode_enum(nt.index, &nt.choices, is_be));
1186 out.extend_from_slice(&encode_nt_alarm(&nt.alarm, is_be));
1188 out.extend_from_slice(&encode_nt_timestamp(&nt.time_stamp, is_be));
1190 out
1191}
1192
1193fn scalar_value_type_code(v: &ScalarValue) -> TypeCode {
1198 match v {
1199 ScalarValue::Bool(_) => TypeCode::Boolean,
1200 ScalarValue::I8(_) => TypeCode::Int8,
1201 ScalarValue::I16(_) => TypeCode::Int16,
1202 ScalarValue::I32(_) => TypeCode::Int32,
1203 ScalarValue::I64(_) => TypeCode::Int64,
1204 ScalarValue::U8(_) => TypeCode::UInt8,
1205 ScalarValue::U16(_) => TypeCode::UInt16,
1206 ScalarValue::U32(_) => TypeCode::UInt32,
1207 ScalarValue::U64(_) => TypeCode::UInt64,
1208 ScalarValue::F32(_) => TypeCode::Float32,
1209 ScalarValue::F64(_) => TypeCode::Float64,
1210 ScalarValue::Str(_) => TypeCode::String,
1211 }
1212}
1213
1214pub fn pv_value_desc(struct_id: &str, fields: &[(String, PvValue)]) -> StructureDesc {
1216 StructureDesc {
1217 struct_id: if struct_id.is_empty() {
1218 None
1219 } else {
1220 Some(struct_id.to_string())
1221 },
1222 fields: fields
1223 .iter()
1224 .map(|(name, val)| FieldDesc {
1225 name: name.clone(),
1226 field_type: pv_value_field_type(val),
1227 })
1228 .collect(),
1229 }
1230}
1231
1232fn pv_value_field_type(val: &PvValue) -> FieldType {
1233 match val {
1234 PvValue::Scalar(sv) => {
1235 if matches!(sv, ScalarValue::Str(_)) {
1236 FieldType::String
1237 } else {
1238 FieldType::Scalar(scalar_value_type_code(sv))
1239 }
1240 }
1241 PvValue::ScalarArray(sa) => scalar_array_field_type(sa),
1242 PvValue::Structure { struct_id, fields } => {
1243 FieldType::Structure(pv_value_desc(struct_id, fields))
1244 }
1245 }
1246}
1247
1248pub fn encode_pv_value(val: &PvValue, is_be: bool) -> Vec<u8> {
1250 match val {
1251 PvValue::Scalar(sv) => encode_scalar_value(sv, is_be),
1252 PvValue::ScalarArray(sa) => encode_scalar_array_value_pvd(sa, is_be),
1253 PvValue::Structure { fields, .. } => {
1254 let mut out = Vec::new();
1255 for (_, v) in fields {
1256 out.extend_from_slice(&encode_pv_value(v, is_be));
1257 }
1258 out
1259 }
1260 }
1261}
1262
1263pub fn nt_payload_desc(payload: &NtPayload) -> StructureDesc {
1264 match payload {
1265 NtPayload::Scalar(nt) => nt_scalar_desc(&nt.value),
1266 NtPayload::ScalarArray(nt) => nt_scalar_array_desc(&nt.value),
1267 NtPayload::Table(nt) => nt_table_desc(nt),
1268 NtPayload::NdArray(nt) => nt_ndarray_desc(nt),
1269 NtPayload::Enum(_) => nt_enum_desc(),
1270 NtPayload::Generic { struct_id, fields } => pv_value_desc(struct_id, fields),
1271 }
1272}
1273
1274pub fn encode_nt_payload_full(payload: &NtPayload, is_be: bool) -> Vec<u8> {
1275 match payload {
1276 NtPayload::Scalar(nt) => encode_nt_scalar_full(nt, is_be),
1277 NtPayload::ScalarArray(nt) => encode_nt_scalar_array_full(nt, is_be),
1278 NtPayload::Table(nt) => encode_nt_table_full(nt, is_be),
1279 NtPayload::NdArray(nt) => encode_nt_ndarray_full(nt, is_be),
1280 NtPayload::Enum(nt) => encode_nt_enum_full(nt, is_be),
1281 NtPayload::Generic { fields, .. } => {
1282 let mut out = Vec::new();
1283 for (_, v) in fields {
1284 out.extend_from_slice(&encode_pv_value(v, is_be));
1285 }
1286 out
1287 }
1288 }
1289}
1290
1291pub fn encode_nt_payload_bitset(payload: &NtPayload, is_be: bool) -> Vec<u8> {
1292 let desc = nt_payload_desc(payload);
1293 let mut out = Vec::new();
1294 out.extend_from_slice(&encode_structure_bitset(&desc, is_be));
1295 out.extend_from_slice(&encode_nt_payload_full(payload, is_be));
1296 out
1297}
1298
1299pub fn encode_nt_payload_bitset_parts(payload: &NtPayload, is_be: bool) -> (Vec<u8>, Vec<u8>) {
1300 let desc = nt_payload_desc(payload);
1301 (
1302 encode_structure_bitset(&desc, is_be),
1303 encode_nt_payload_full(payload, is_be),
1304 )
1305}
1306
1307use crate::spvd_decode::DecodedValue;
1312
1313pub fn encode_decoded_value(val: &DecodedValue, is_be: bool) -> Vec<u8> {
1315 match val {
1316 DecodedValue::Null => Vec::new(),
1317 DecodedValue::Boolean(v) => vec![if *v { 1 } else { 0 }],
1318 DecodedValue::Int8(v) => vec![*v as u8],
1319 DecodedValue::Int16(v) => {
1320 if is_be {
1321 v.to_be_bytes().to_vec()
1322 } else {
1323 v.to_le_bytes().to_vec()
1324 }
1325 }
1326 DecodedValue::Int32(v) => encode_i32(*v, is_be),
1327 DecodedValue::Int64(v) => encode_i64(*v, is_be),
1328 DecodedValue::UInt8(v) => vec![*v],
1329 DecodedValue::UInt16(v) => {
1330 if is_be {
1331 v.to_be_bytes().to_vec()
1332 } else {
1333 v.to_le_bytes().to_vec()
1334 }
1335 }
1336 DecodedValue::UInt32(v) => {
1337 if is_be {
1338 v.to_be_bytes().to_vec()
1339 } else {
1340 v.to_le_bytes().to_vec()
1341 }
1342 }
1343 DecodedValue::UInt64(v) => {
1344 if is_be {
1345 v.to_be_bytes().to_vec()
1346 } else {
1347 v.to_le_bytes().to_vec()
1348 }
1349 }
1350 DecodedValue::Float32(v) => {
1351 if is_be {
1352 v.to_be_bytes().to_vec()
1353 } else {
1354 v.to_le_bytes().to_vec()
1355 }
1356 }
1357 DecodedValue::Float64(v) => encode_f64(*v, is_be),
1358 DecodedValue::String(v) => encode_string_pvd(v, is_be),
1359 DecodedValue::Array(arr) => {
1360 let mut out = encode_size_pvd(arr.len(), is_be);
1361 for item in arr {
1362 out.extend_from_slice(&encode_decoded_value(item, is_be));
1363 }
1364 out
1365 }
1366 DecodedValue::Structure(fields) => {
1367 let mut out = Vec::new();
1368 for (_name, value) in fields {
1369 out.extend_from_slice(&encode_decoded_value(value, is_be));
1370 }
1371 out
1372 }
1373 DecodedValue::Raw(data) => data.clone(),
1374 }
1375}
1376
1377pub fn decode_pv_request_fields(body: &[u8], is_be: bool) -> Option<Vec<String>> {
1387 if body.is_empty() {
1388 return None;
1389 }
1390 let decoder = crate::spvd_decode::PvdDecoder::new(is_be);
1391 let desc = decoder.parse_introspection(body)?;
1392 for field in &desc.fields {
1394 if field.name == "field" {
1395 if let FieldType::Structure(ref inner) = field.field_type {
1396 if inner.fields.is_empty() {
1397 return None;
1399 }
1400 let names: Vec<String> = inner.fields.iter().map(|f| f.name.clone()).collect();
1401 return Some(names);
1402 }
1403 }
1404 }
1405 None
1406}
1407
1408pub fn filter_structure_desc(desc: &StructureDesc, requested: &[String]) -> StructureDesc {
1412 if requested.is_empty() {
1413 return desc.clone();
1414 }
1415 StructureDesc {
1416 struct_id: desc.struct_id.clone(),
1417 fields: desc
1418 .fields
1419 .iter()
1420 .filter(|f| requested.iter().any(|r| r == &f.name))
1421 .cloned()
1422 .collect(),
1423 }
1424}
1425
1426pub fn encode_nt_payload_filtered(
1431 payload: &NtPayload,
1432 filtered_desc: &StructureDesc,
1433 is_be: bool,
1434) -> (Vec<u8>, Vec<u8>) {
1435 let requested: Vec<&str> = filtered_desc
1436 .fields
1437 .iter()
1438 .map(|f| f.name.as_str())
1439 .collect();
1440 let full_desc = nt_payload_desc(payload);
1441 let full_fields = &full_desc.fields;
1442
1443 let field_bytes: Vec<(&str, Vec<u8>)> = encode_nt_payload_fields(payload, full_fields, is_be);
1445
1446 let mut values = Vec::new();
1448 for (name, bytes) in &field_bytes {
1449 if requested.iter().any(|r| *r == *name) {
1450 values.extend_from_slice(bytes);
1451 }
1452 }
1453
1454 let bitset = encode_structure_bitset(filtered_desc, is_be);
1455 (bitset, values)
1456}
1457
1458fn encode_nt_table_field(nt: &NtTable, name: &str, is_be: bool) -> Vec<u8> {
1461 match name {
1462 "labels" => encode_string_array(&nt.labels, is_be),
1463 "value" => {
1464 let mut out = Vec::new();
1465 for NtTableColumn { values, .. } in &nt.columns {
1466 out.extend_from_slice(&encode_scalar_array_value_pvd(values, is_be));
1467 }
1468 out
1469 }
1470 _ => Vec::new(),
1471 }
1472}
1473
1474fn encode_nt_ndarray_field(nt: &NtNdArray, name: &str, is_be: bool) -> Vec<u8> {
1475 match name {
1476 "value" => encode_ndarray_union(&nt.value, is_be),
1477 "codec" => {
1478 let mut out = Vec::new();
1479 out.extend_from_slice(&encode_string_pvd(&nt.codec.name, is_be));
1480 out.extend_from_slice(&encode_codec_parameters(&nt.codec.parameters, is_be));
1481 out
1482 }
1483 "compressedSize" => encode_i64(nt.compressed_size, is_be),
1484 "uncompressedSize" => encode_i64(nt.uncompressed_size, is_be),
1485 "dimension" => {
1486 let mut out = encode_size_pvd(nt.dimension.len(), is_be);
1487 for d in &nt.dimension {
1488 out.push(1);
1489 out.extend_from_slice(&encode_i32(d.size, is_be));
1490 out.extend_from_slice(&encode_i32(d.offset, is_be));
1491 out.extend_from_slice(&encode_i32(d.full_size, is_be));
1492 out.extend_from_slice(&encode_i32(d.binning, is_be));
1493 out.push(if d.reverse { 1 } else { 0 });
1494 }
1495 out
1496 }
1497 "uniqueId" => encode_i32(nt.unique_id, is_be),
1498 "dataTimeStamp" => encode_nt_timestamp(&nt.data_time_stamp, is_be),
1499 "attribute" => {
1500 let mut out = encode_size_pvd(nt.attribute.len(), is_be);
1501 for attr in &nt.attribute {
1502 out.push(1);
1503 out.extend_from_slice(&encode_string_pvd(&attr.name, is_be));
1504 out.extend_from_slice(&encode_attribute_variant(attr, is_be));
1505 out.extend_from_slice(&encode_string_pvd(&attr.descriptor, is_be));
1506 out.extend_from_slice(&encode_i32(attr.source_type, is_be));
1507 out.extend_from_slice(&encode_string_pvd(&attr.source, is_be));
1508 }
1509 out
1510 }
1511 "descriptor" => encode_string_pvd(nt.descriptor.as_deref().unwrap_or(""), is_be),
1512 "alarm" => encode_nt_alarm(nt.alarm.as_ref().unwrap_or(&NtAlarm::default()), is_be),
1513 "timeStamp" => encode_nt_timestamp(
1514 nt.time_stamp.as_ref().unwrap_or(&NtTimeStamp::default()),
1515 is_be,
1516 ),
1517 "display" => encode_nt_display(nt.display.as_ref().unwrap_or(&NtDisplay::default()), is_be),
1518 _ => Vec::new(),
1519 }
1520}
1521
1522fn encode_nt_payload_fields<'a>(
1523 payload: &'a NtPayload,
1524 full_fields: &'a [FieldDesc],
1525 is_be: bool,
1526) -> Vec<(&'a str, Vec<u8>)> {
1527 fn scalar_field(nt: &NtScalar, name: &str, is_be: bool) -> Vec<u8> {
1529 match name {
1530 "value" => encode_scalar_value(&nt.value, is_be),
1531 "alarm" => encode_alarm(nt, is_be),
1532 "timeStamp" => encode_timestamp(nt, is_be),
1533 "display" => encode_display(nt, is_be),
1534 "control" => encode_control(nt, is_be),
1535 "valueAlarm" => encode_value_alarm(nt, is_be),
1536 _ => Vec::new(),
1537 }
1538 }
1539
1540 fn scalar_array_field(nt: &NtScalarArray, name: &str, is_be: bool) -> Vec<u8> {
1541 match name {
1542 "value" => encode_scalar_array_value_pvd(&nt.value, is_be),
1543 "alarm" => encode_nt_alarm(&nt.alarm, is_be),
1544 "timeStamp" => encode_nt_timestamp(&nt.time_stamp, is_be),
1545 "display" => encode_nt_display(&nt.display, is_be),
1546 "control" => {
1547 let mut out = Vec::new();
1548 out.extend_from_slice(&encode_f64(nt.control.limit_low, is_be));
1549 out.extend_from_slice(&encode_f64(nt.control.limit_high, is_be));
1550 out.extend_from_slice(&encode_f64(nt.control.min_step, is_be));
1551 out
1552 }
1553 _ => Vec::new(),
1554 }
1555 }
1556
1557 fn enum_field(nt: &NtEnum, name: &str, is_be: bool) -> Vec<u8> {
1558 match name {
1559 "value" => encode_enum(nt.index, &nt.choices, is_be),
1560 "alarm" => encode_nt_alarm(&nt.alarm, is_be),
1561 "timeStamp" => encode_nt_timestamp(&nt.time_stamp, is_be),
1562 _ => Vec::new(),
1563 }
1564 }
1565
1566 full_fields
1567 .iter()
1568 .map(|f| {
1569 let name = f.name.as_str();
1570 let bytes = match payload {
1571 NtPayload::Scalar(nt) => scalar_field(nt, name, is_be),
1572 NtPayload::ScalarArray(nt) => scalar_array_field(nt, name, is_be),
1573 NtPayload::Table(nt) => encode_nt_table_field(nt, name, is_be),
1574 NtPayload::NdArray(nt) => encode_nt_ndarray_field(nt, name, is_be),
1575 NtPayload::Enum(nt) => enum_field(nt, name, is_be),
1576 NtPayload::Generic { fields, .. } => {
1577 if let Some((_, v)) = fields.iter().find(|(n, _)| n == name) {
1578 encode_pv_value(v, is_be)
1579 } else {
1580 Vec::new()
1581 }
1582 }
1583 };
1584 (name, bytes)
1585 })
1586 .collect()
1587}
1588
1589pub fn encode_pv_request(fields: &[&str], is_be: bool) -> Vec<u8> {
1603 let inner_fields: Vec<FieldDesc> = fields
1606 .iter()
1607 .map(|name| FieldDesc {
1608 name: name.to_string(),
1609 field_type: FieldType::Structure(StructureDesc {
1610 struct_id: None,
1611 fields: Vec::new(),
1612 }),
1613 })
1614 .collect();
1615
1616 let field_desc = StructureDesc {
1617 struct_id: None,
1618 fields: inner_fields,
1619 };
1620
1621 let pv_request_desc = StructureDesc {
1622 struct_id: None,
1623 fields: vec![FieldDesc {
1624 name: "field".to_string(),
1625 field_type: FieldType::Structure(field_desc),
1626 }],
1627 };
1628
1629 let mut out = Vec::new();
1630 out.push(0x80); out.extend_from_slice(&encode_structure_desc(&pv_request_desc, is_be));
1632 out
1635}
1636
1637#[cfg(test)]
1638mod tests {
1639 use super::*;
1640 use crate::spvd_decode::PvdDecoder;
1641
1642 #[test]
1643 fn nt_scalar_roundtrip() {
1644 let nt = NtScalar::from_value(ScalarValue::F64(12.5));
1645 let desc = nt_scalar_desc(&nt.value);
1646 let desc_bytes = encode_structure_desc(&desc, false);
1647 let mut pvd = Vec::new();
1648 pvd.push(0x80);
1649 pvd.extend_from_slice(&desc_bytes);
1650 pvd.extend_from_slice(&encode_nt_scalar_full(&nt, false));
1651
1652 let decoder = PvdDecoder::new(false);
1653 let parsed_desc = decoder.parse_introspection(&pvd).expect("desc");
1654 let (_, consumed) = decoder
1655 .decode_structure(&pvd[1 + desc_bytes.len()..], &parsed_desc)
1656 .expect("value");
1657 assert!(consumed > 0);
1658 }
1659
1660 #[test]
1661 fn nt_ndarray_roundtrip() {
1662 use spvirit_types::{
1663 NdCodec, NdDimension, NtAlarm, NtNdArray, NtTimeStamp, ScalarArrayValue,
1664 };
1665 use std::collections::HashMap;
1666
1667 let nt = NtNdArray {
1668 value: ScalarArrayValue::U8(vec![1, 2, 3, 4]),
1669 codec: NdCodec {
1670 name: String::new(),
1671 parameters: HashMap::new(),
1672 },
1673 compressed_size: 4,
1674 uncompressed_size: 4,
1675 dimension: vec![NdDimension {
1676 size: 2,
1677 offset: 0,
1678 full_size: 2,
1679 binning: 1,
1680 reverse: false,
1681 }],
1682 unique_id: 42,
1683 data_time_stamp: NtTimeStamp {
1684 seconds_past_epoch: 1000,
1685 nanoseconds: 500,
1686 user_tag: 0,
1687 },
1688 attribute: Vec::new(),
1689 descriptor: Some("test".to_string()),
1690 alarm: Some(NtAlarm::default()),
1691 time_stamp: Some(NtTimeStamp::default()),
1692 display: None,
1693 };
1694
1695 let desc = nt_ndarray_desc(&nt);
1696 let desc_bytes = encode_structure_desc(&desc, false);
1697 let data_bytes = encode_nt_ndarray_full(&nt, false);
1698
1699 let mut pvd = Vec::new();
1701 pvd.push(0x80);
1702 pvd.extend_from_slice(&desc_bytes);
1703 pvd.extend_from_slice(&data_bytes);
1704
1705 let decoder = PvdDecoder::new(false);
1706 let parsed_desc = decoder
1707 .parse_introspection(&pvd)
1708 .expect("desc parse failed");
1709 let data_start = 1 + desc_bytes.len();
1710 let (_decoded, consumed) = decoder
1711 .decode_structure(&pvd[data_start..], &parsed_desc)
1712 .expect("data decode failed");
1713 assert!(consumed > 0, "consumed should be > 0");
1714 assert_eq!(
1715 consumed,
1716 data_bytes.len(),
1717 "consumed should match data_bytes.len()"
1718 );
1719 }
1720}