1use std::time::{SystemTime, UNIX_EPOCH};
6
7use crate::spvd_decode::{FieldDesc, FieldType, StructureDesc, TypeCode};
8
9use spvirit_types::{
10 NdDimension, NtAlarm, NtAttribute, NtDisplay, NtNdArray, NtPayload, NtScalar, NtScalarArray,
11 NtTable, NtTableColumn, NtTimeStamp, 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 { v.to_be_bytes().to_vec() } else { v.to_le_bytes().to_vec() }
100 }
101 ScalarValue::I32(v) => {
102 if is_be {
103 v.to_be_bytes().to_vec()
104 } else {
105 v.to_le_bytes().to_vec()
106 }
107 }
108 ScalarValue::I64(v) => {
109 if is_be { v.to_be_bytes().to_vec() } else { v.to_le_bytes().to_vec() }
110 }
111 ScalarValue::U8(v) => vec![*v],
112 ScalarValue::U16(v) => {
113 if is_be { v.to_be_bytes().to_vec() } else { v.to_le_bytes().to_vec() }
114 }
115 ScalarValue::U32(v) => {
116 if is_be { v.to_be_bytes().to_vec() } else { v.to_le_bytes().to_vec() }
117 }
118 ScalarValue::U64(v) => {
119 if is_be { v.to_be_bytes().to_vec() } else { v.to_le_bytes().to_vec() }
120 }
121 ScalarValue::F32(v) => {
122 if is_be { v.to_be_bytes().to_vec() } else { v.to_le_bytes().to_vec() }
123 }
124 ScalarValue::F64(v) => {
125 if is_be {
126 v.to_be_bytes().to_vec()
127 } else {
128 v.to_le_bytes().to_vec()
129 }
130 }
131 ScalarValue::Str(v) => encode_string_pvd(v, is_be),
132 }
133}
134
135fn encode_alarm(nt: &NtScalar, is_be: bool) -> Vec<u8> {
136 let mut out = Vec::new();
137 out.extend_from_slice(&encode_i32(nt.alarm_severity, is_be));
138 out.extend_from_slice(&encode_i32(nt.alarm_status, is_be));
139 out.extend_from_slice(&encode_string_pvd(&nt.alarm_message, is_be));
140 out
141}
142
143fn encode_bool(value: bool) -> Vec<u8> {
144 vec![if value { 1 } else { 0 }]
145}
146
147fn encode_string_array(values: &[String], is_be: bool) -> Vec<u8> {
148 let mut out = Vec::new();
149 out.extend_from_slice(&encode_size_pvd(values.len(), is_be));
150 for v in values {
151 out.extend_from_slice(&encode_string_pvd(v, is_be));
152 }
153 out
154}
155
156fn encode_enum(index: i32, choices: &[String], is_be: bool) -> Vec<u8> {
157 let mut out = Vec::new();
158 out.extend_from_slice(&encode_i32(index, is_be));
159 out.extend_from_slice(&encode_string_array(choices, is_be));
160 out
161}
162
163fn encode_timestamp(_nt: &NtScalar, is_be: bool) -> Vec<u8> {
164 let mut out = Vec::new();
165 let now = SystemTime::now()
166 .duration_since(UNIX_EPOCH)
167 .unwrap_or_default();
168 let seconds_past_epoch = now.as_secs() as i64;
169 let nanos = now.subsec_nanos() as i32;
170
171 out.extend_from_slice(&encode_i64(seconds_past_epoch, is_be));
172 out.extend_from_slice(&encode_i32(nanos, is_be));
173 out.extend_from_slice(&encode_i32(0, is_be)); out
175}
176
177fn encode_display(nt: &NtScalar, is_be: bool) -> Vec<u8> {
178 let mut out = Vec::new();
179 out.extend_from_slice(&encode_f64(nt.display_low, is_be));
180 out.extend_from_slice(&encode_f64(nt.display_high, is_be));
181 out.extend_from_slice(&encode_string_pvd(&nt.display_description, is_be));
182 out.extend_from_slice(&encode_string_pvd(&nt.units, is_be));
183 out.extend_from_slice(&encode_i32(nt.display_precision, is_be));
184 out.extend_from_slice(&encode_enum(
185 nt.display_form_index,
186 &nt.display_form_choices,
187 is_be,
188 ));
189 out
190}
191
192fn encode_control(nt: &NtScalar, is_be: bool) -> Vec<u8> {
193 let mut out = Vec::new();
194 out.extend_from_slice(&encode_f64(nt.control_low, is_be));
195 out.extend_from_slice(&encode_f64(nt.control_high, is_be));
196 out.extend_from_slice(&encode_f64(nt.control_min_step, is_be));
197 out
198}
199
200fn encode_value_alarm(nt: &NtScalar, is_be: bool) -> Vec<u8> {
201 let mut out = Vec::new();
202 out.extend_from_slice(&encode_bool(nt.value_alarm_active));
203 out.extend_from_slice(&encode_f64(nt.value_alarm_low_alarm_limit, is_be));
204 out.extend_from_slice(&encode_f64(nt.value_alarm_low_warning_limit, is_be));
205 out.extend_from_slice(&encode_f64(nt.value_alarm_high_warning_limit, is_be));
206 out.extend_from_slice(&encode_f64(nt.value_alarm_high_alarm_limit, is_be));
207 out.extend_from_slice(&encode_i32(nt.value_alarm_low_alarm_severity, is_be));
208 out.extend_from_slice(&encode_i32(nt.value_alarm_low_warning_severity, is_be));
209 out.extend_from_slice(&encode_i32(nt.value_alarm_high_warning_severity, is_be));
210 out.extend_from_slice(&encode_i32(nt.value_alarm_high_alarm_severity, is_be));
211 out.push(nt.value_alarm_hysteresis);
212 out
213}
214
215fn encode_i32(value: i32, is_be: bool) -> Vec<u8> {
216 if is_be {
217 value.to_be_bytes().to_vec()
218 } else {
219 value.to_le_bytes().to_vec()
220 }
221}
222
223fn encode_i64(value: i64, is_be: bool) -> Vec<u8> {
224 if is_be {
225 value.to_be_bytes().to_vec()
226 } else {
227 value.to_le_bytes().to_vec()
228 }
229}
230
231fn encode_f64(value: f64, is_be: bool) -> Vec<u8> {
232 if is_be {
233 value.to_be_bytes().to_vec()
234 } else {
235 value.to_le_bytes().to_vec()
236 }
237}
238
239pub fn nt_scalar_desc(value: &ScalarValue) -> StructureDesc {
240 let value_type = match value {
241 ScalarValue::Bool(_) => FieldType::Scalar(TypeCode::Boolean),
242 ScalarValue::I8(_) => FieldType::Scalar(TypeCode::Int8),
243 ScalarValue::I16(_) => FieldType::Scalar(TypeCode::Int16),
244 ScalarValue::I32(_) => FieldType::Scalar(TypeCode::Int32),
245 ScalarValue::I64(_) => FieldType::Scalar(TypeCode::Int64),
246 ScalarValue::U8(_) => FieldType::Scalar(TypeCode::UInt8),
247 ScalarValue::U16(_) => FieldType::Scalar(TypeCode::UInt16),
248 ScalarValue::U32(_) => FieldType::Scalar(TypeCode::UInt32),
249 ScalarValue::U64(_) => FieldType::Scalar(TypeCode::UInt64),
250 ScalarValue::F32(_) => FieldType::Scalar(TypeCode::Float32),
251 ScalarValue::F64(_) => FieldType::Scalar(TypeCode::Float64),
252 ScalarValue::Str(_) => FieldType::String,
253 };
254
255 StructureDesc {
256 struct_id: Some("epics:nt/NTScalar:1.0".to_string()),
257 fields: vec![
258 FieldDesc {
259 name: "value".to_string(),
260 field_type: value_type,
261 },
262 FieldDesc {
263 name: "alarm".to_string(),
264 field_type: FieldType::Structure(StructureDesc {
265 struct_id: Some("alarm_t".to_string()),
266 fields: vec![
267 FieldDesc {
268 name: "severity".to_string(),
269 field_type: FieldType::Scalar(TypeCode::Int32),
270 },
271 FieldDesc {
272 name: "status".to_string(),
273 field_type: FieldType::Scalar(TypeCode::Int32),
274 },
275 FieldDesc {
276 name: "message".to_string(),
277 field_type: FieldType::String,
278 },
279 ],
280 }),
281 },
282 FieldDesc {
283 name: "timeStamp".to_string(),
284 field_type: FieldType::Structure(StructureDesc {
285 struct_id: None,
286 fields: vec![
287 FieldDesc {
288 name: "secondsPastEpoch".to_string(),
289 field_type: FieldType::Scalar(TypeCode::Int64),
290 },
291 FieldDesc {
292 name: "nanoseconds".to_string(),
293 field_type: FieldType::Scalar(TypeCode::Int32),
294 },
295 FieldDesc {
296 name: "userTag".to_string(),
297 field_type: FieldType::Scalar(TypeCode::Int32),
298 },
299 ],
300 }),
301 },
302 FieldDesc {
303 name: "display".to_string(),
304 field_type: FieldType::Structure(StructureDesc {
305 struct_id: None,
306 fields: vec![
307 FieldDesc {
308 name: "limitLow".to_string(),
309 field_type: FieldType::Scalar(TypeCode::Float64),
310 },
311 FieldDesc {
312 name: "limitHigh".to_string(),
313 field_type: FieldType::Scalar(TypeCode::Float64),
314 },
315 FieldDesc {
316 name: "description".to_string(),
317 field_type: FieldType::String,
318 },
319 FieldDesc {
320 name: "units".to_string(),
321 field_type: FieldType::String,
322 },
323 FieldDesc {
324 name: "precision".to_string(),
325 field_type: FieldType::Scalar(TypeCode::Int32),
326 },
327 FieldDesc {
328 name: "form".to_string(),
329 field_type: FieldType::Structure(StructureDesc {
330 struct_id: Some("enum_t".to_string()),
331 fields: vec![
332 FieldDesc {
333 name: "index".to_string(),
334 field_type: FieldType::Scalar(TypeCode::Int32),
335 },
336 FieldDesc {
337 name: "choices".to_string(),
338 field_type: FieldType::StringArray,
339 },
340 ],
341 }),
342 },
343 ],
344 }),
345 },
346 FieldDesc {
347 name: "control".to_string(),
348 field_type: FieldType::Structure(StructureDesc {
349 struct_id: Some("control_t".to_string()),
350 fields: vec![
351 FieldDesc {
352 name: "limitLow".to_string(),
353 field_type: FieldType::Scalar(TypeCode::Float64),
354 },
355 FieldDesc {
356 name: "limitHigh".to_string(),
357 field_type: FieldType::Scalar(TypeCode::Float64),
358 },
359 FieldDesc {
360 name: "minStep".to_string(),
361 field_type: FieldType::Scalar(TypeCode::Float64),
362 },
363 ],
364 }),
365 },
366 FieldDesc {
367 name: "valueAlarm".to_string(),
368 field_type: FieldType::Structure(StructureDesc {
369 struct_id: Some("valueAlarm_t".to_string()),
370 fields: vec![
371 FieldDesc {
372 name: "active".to_string(),
373 field_type: FieldType::Scalar(TypeCode::Boolean),
374 },
375 FieldDesc {
376 name: "lowAlarmLimit".to_string(),
377 field_type: FieldType::Scalar(TypeCode::Float64),
378 },
379 FieldDesc {
380 name: "lowWarningLimit".to_string(),
381 field_type: FieldType::Scalar(TypeCode::Float64),
382 },
383 FieldDesc {
384 name: "highWarningLimit".to_string(),
385 field_type: FieldType::Scalar(TypeCode::Float64),
386 },
387 FieldDesc {
388 name: "highAlarmLimit".to_string(),
389 field_type: FieldType::Scalar(TypeCode::Float64),
390 },
391 FieldDesc {
392 name: "lowAlarmSeverity".to_string(),
393 field_type: FieldType::Scalar(TypeCode::Int32),
394 },
395 FieldDesc {
396 name: "lowWarningSeverity".to_string(),
397 field_type: FieldType::Scalar(TypeCode::Int32),
398 },
399 FieldDesc {
400 name: "highWarningSeverity".to_string(),
401 field_type: FieldType::Scalar(TypeCode::Int32),
402 },
403 FieldDesc {
404 name: "highAlarmSeverity".to_string(),
405 field_type: FieldType::Scalar(TypeCode::Int32),
406 },
407 FieldDesc {
408 name: "hysteresis".to_string(),
409 field_type: FieldType::Scalar(TypeCode::UInt8),
410 },
411 ],
412 }),
413 },
414 ],
415 }
416}
417
418pub fn encode_nt_scalar_full(nt: &NtScalar, is_be: bool) -> Vec<u8> {
419 let mut out = Vec::new();
420 out.extend_from_slice(&encode_scalar_value(&nt.value, is_be));
421 out.extend_from_slice(&encode_alarm(nt, is_be));
422 out.extend_from_slice(&encode_timestamp(nt, is_be));
423 out.extend_from_slice(&encode_display(nt, is_be));
424 out.extend_from_slice(&encode_control(nt, is_be));
425 out.extend_from_slice(&encode_value_alarm(nt, is_be));
426 out
427}
428
429fn encode_structure_bitset(desc: &StructureDesc, is_be: bool) -> Vec<u8> {
430 let total_bits = 1 + count_structure_fields(desc);
431 let bitset_size = (total_bits + 7) / 8;
432 let mut bitset = vec![0u8; bitset_size];
433 for bit in 0..total_bits {
434 let byte_idx = bit / 8;
435 let bit_idx = bit % 8;
436 bitset[byte_idx] |= 1 << bit_idx;
437 }
438 let mut out = Vec::new();
439 out.extend_from_slice(&encode_size_pvd(bitset_size, is_be));
440 out.extend_from_slice(&bitset);
441 out
442}
443
444fn encode_structure_with_bitset(desc: &StructureDesc, nt: &NtScalar, is_be: bool) -> Vec<u8> {
445 let mut out = Vec::new();
446 out.extend_from_slice(&encode_structure_bitset(desc, is_be));
447 out.extend_from_slice(&encode_nt_scalar_full(nt, is_be));
448 out
449}
450
451pub fn encode_nt_scalar_bitset(nt: &NtScalar, is_be: bool) -> Vec<u8> {
452 let desc = nt_scalar_desc(&nt.value);
453 encode_structure_with_bitset(&desc, nt, is_be)
454}
455
456pub fn encode_nt_scalar_bitset_parts(nt: &NtScalar, is_be: bool) -> (Vec<u8>, Vec<u8>) {
457 let desc = nt_scalar_desc(&nt.value);
458 let bitset = encode_structure_bitset(&desc, is_be);
459 let values = encode_nt_scalar_full(nt, is_be);
460 (bitset, values)
461}
462
463fn alarm_desc() -> StructureDesc {
464 StructureDesc {
465 struct_id: Some("alarm_t".to_string()),
466 fields: vec![
467 FieldDesc {
468 name: "severity".to_string(),
469 field_type: FieldType::Scalar(TypeCode::Int32),
470 },
471 FieldDesc {
472 name: "status".to_string(),
473 field_type: FieldType::Scalar(TypeCode::Int32),
474 },
475 FieldDesc {
476 name: "message".to_string(),
477 field_type: FieldType::String,
478 },
479 ],
480 }
481}
482
483fn timestamp_desc() -> StructureDesc {
484 StructureDesc {
485 struct_id: Some("time_t".to_string()),
486 fields: vec![
487 FieldDesc {
488 name: "secondsPastEpoch".to_string(),
489 field_type: FieldType::Scalar(TypeCode::Int64),
490 },
491 FieldDesc {
492 name: "nanoseconds".to_string(),
493 field_type: FieldType::Scalar(TypeCode::Int32),
494 },
495 FieldDesc {
496 name: "userTag".to_string(),
497 field_type: FieldType::Scalar(TypeCode::Int32),
498 },
499 ],
500 }
501}
502
503fn display_desc() -> StructureDesc {
504 StructureDesc {
505 struct_id: Some("display_t".to_string()),
506 fields: vec![
507 FieldDesc {
508 name: "limitLow".to_string(),
509 field_type: FieldType::Scalar(TypeCode::Float64),
510 },
511 FieldDesc {
512 name: "limitHigh".to_string(),
513 field_type: FieldType::Scalar(TypeCode::Float64),
514 },
515 FieldDesc {
516 name: "description".to_string(),
517 field_type: FieldType::String,
518 },
519 FieldDesc {
520 name: "units".to_string(),
521 field_type: FieldType::String,
522 },
523 FieldDesc {
524 name: "precision".to_string(),
525 field_type: FieldType::Scalar(TypeCode::Int32),
526 },
527 ],
528 }
529}
530
531fn scalar_array_field_type(value: &ScalarArrayValue) -> FieldType {
532 match value {
533 ScalarArrayValue::Bool(_) => FieldType::ScalarArray(TypeCode::Boolean),
534 ScalarArrayValue::I8(_) => FieldType::ScalarArray(TypeCode::Int8),
535 ScalarArrayValue::I16(_) => FieldType::ScalarArray(TypeCode::Int16),
536 ScalarArrayValue::I32(_) => FieldType::ScalarArray(TypeCode::Int32),
537 ScalarArrayValue::I64(_) => FieldType::ScalarArray(TypeCode::Int64),
538 ScalarArrayValue::U8(_) => FieldType::ScalarArray(TypeCode::UInt8),
539 ScalarArrayValue::U16(_) => FieldType::ScalarArray(TypeCode::UInt16),
540 ScalarArrayValue::U32(_) => FieldType::ScalarArray(TypeCode::UInt32),
541 ScalarArrayValue::U64(_) => FieldType::ScalarArray(TypeCode::UInt64),
542 ScalarArrayValue::F32(_) => FieldType::ScalarArray(TypeCode::Float32),
543 ScalarArrayValue::F64(_) => FieldType::ScalarArray(TypeCode::Float64),
544 ScalarArrayValue::Str(_) => FieldType::StringArray,
545 }
546}
547
548fn encode_scalar_array_value_pvd(value: &ScalarArrayValue, is_be: bool) -> Vec<u8> {
549 let mut out = Vec::new();
550 match value {
551 ScalarArrayValue::Bool(v) => {
552 out.extend_from_slice(&encode_size_pvd(v.len(), is_be));
553 for i in v {
554 out.push(if *i { 1 } else { 0 });
555 }
556 }
557 ScalarArrayValue::I8(v) => {
558 out.extend_from_slice(&encode_size_pvd(v.len(), is_be));
559 for i in v {
560 out.push(*i as u8);
561 }
562 }
563 ScalarArrayValue::I16(v) => {
564 out.extend_from_slice(&encode_size_pvd(v.len(), is_be));
565 for i in v {
566 let b = if is_be {
567 i.to_be_bytes()
568 } else {
569 i.to_le_bytes()
570 };
571 out.extend_from_slice(&b);
572 }
573 }
574 ScalarArrayValue::I32(v) => {
575 out.extend_from_slice(&encode_size_pvd(v.len(), is_be));
576 for i in v {
577 out.extend_from_slice(&encode_i32(*i, is_be));
578 }
579 }
580 ScalarArrayValue::I64(v) => {
581 out.extend_from_slice(&encode_size_pvd(v.len(), is_be));
582 for i in v {
583 out.extend_from_slice(&encode_i64(*i, is_be));
584 }
585 }
586 ScalarArrayValue::U8(v) => {
587 out.extend_from_slice(&encode_size_pvd(v.len(), is_be));
588 out.extend_from_slice(v);
589 }
590 ScalarArrayValue::U16(v) => {
591 out.extend_from_slice(&encode_size_pvd(v.len(), is_be));
592 for i in v {
593 let b = if is_be {
594 i.to_be_bytes()
595 } else {
596 i.to_le_bytes()
597 };
598 out.extend_from_slice(&b);
599 }
600 }
601 ScalarArrayValue::U32(v) => {
602 out.extend_from_slice(&encode_size_pvd(v.len(), is_be));
603 for i in v {
604 let b = if is_be {
605 i.to_be_bytes()
606 } else {
607 i.to_le_bytes()
608 };
609 out.extend_from_slice(&b);
610 }
611 }
612 ScalarArrayValue::U64(v) => {
613 out.extend_from_slice(&encode_size_pvd(v.len(), is_be));
614 for i in v {
615 let b = if is_be {
616 i.to_be_bytes()
617 } else {
618 i.to_le_bytes()
619 };
620 out.extend_from_slice(&b);
621 }
622 }
623 ScalarArrayValue::F32(v) => {
624 out.extend_from_slice(&encode_size_pvd(v.len(), is_be));
625 for i in v {
626 let b = if is_be {
627 i.to_be_bytes()
628 } else {
629 i.to_le_bytes()
630 };
631 out.extend_from_slice(&b);
632 }
633 }
634 ScalarArrayValue::F64(v) => {
635 out.extend_from_slice(&encode_size_pvd(v.len(), is_be));
636 for i in v {
637 out.extend_from_slice(&encode_f64(*i, is_be));
638 }
639 }
640 ScalarArrayValue::Str(v) => {
641 out.extend_from_slice(&encode_string_array(v, is_be));
642 }
643 }
644 out
645}
646
647fn encode_nt_alarm(alarm: &NtAlarm, is_be: bool) -> Vec<u8> {
648 let mut out = Vec::new();
649 out.extend_from_slice(&encode_i32(alarm.severity, is_be));
650 out.extend_from_slice(&encode_i32(alarm.status, is_be));
651 out.extend_from_slice(&encode_string_pvd(&alarm.message, is_be));
652 out
653}
654
655fn encode_nt_timestamp(ts: &NtTimeStamp, is_be: bool) -> Vec<u8> {
656 let mut out = Vec::new();
657 out.extend_from_slice(&encode_i64(ts.seconds_past_epoch, is_be));
658 out.extend_from_slice(&encode_i32(ts.nanoseconds, is_be));
659 out.extend_from_slice(&encode_i32(ts.user_tag, is_be));
660 out
661}
662
663fn encode_nt_display(display: &NtDisplay, is_be: bool) -> Vec<u8> {
664 let mut out = Vec::new();
665 out.extend_from_slice(&encode_f64(display.limit_low, is_be));
666 out.extend_from_slice(&encode_f64(display.limit_high, is_be));
667 out.extend_from_slice(&encode_string_pvd(&display.description, is_be));
668 out.extend_from_slice(&encode_string_pvd(&display.units, is_be));
669 out.extend_from_slice(&encode_i32(display.precision, is_be));
670 out
671}
672
673pub fn nt_scalar_array_desc(value: &ScalarArrayValue) -> StructureDesc {
674 StructureDesc {
675 struct_id: Some("epics:nt/NTScalarArray:1.0".to_string()),
676 fields: vec![
677 FieldDesc {
678 name: "value".to_string(),
679 field_type: scalar_array_field_type(value),
680 },
681 FieldDesc {
682 name: "alarm".to_string(),
683 field_type: FieldType::Structure(alarm_desc()),
684 },
685 FieldDesc {
686 name: "timeStamp".to_string(),
687 field_type: FieldType::Structure(timestamp_desc()),
688 },
689 FieldDesc {
690 name: "display".to_string(),
691 field_type: FieldType::Structure(display_desc()),
692 },
693 FieldDesc {
694 name: "control".to_string(),
695 field_type: FieldType::Structure(StructureDesc {
696 struct_id: Some("control_t".to_string()),
697 fields: vec![
698 FieldDesc {
699 name: "limitLow".to_string(),
700 field_type: FieldType::Scalar(TypeCode::Float64),
701 },
702 FieldDesc {
703 name: "limitHigh".to_string(),
704 field_type: FieldType::Scalar(TypeCode::Float64),
705 },
706 FieldDesc {
707 name: "minStep".to_string(),
708 field_type: FieldType::Scalar(TypeCode::Float64),
709 },
710 ],
711 }),
712 },
713 ],
714 }
715}
716
717pub fn encode_nt_scalar_array_full(nt: &NtScalarArray, is_be: bool) -> Vec<u8> {
718 let mut out = Vec::new();
719 out.extend_from_slice(&encode_scalar_array_value_pvd(&nt.value, is_be));
720 out.extend_from_slice(&encode_nt_alarm(&nt.alarm, is_be));
721 out.extend_from_slice(&encode_nt_timestamp(&nt.time_stamp, is_be));
722 out.extend_from_slice(&encode_nt_display(&nt.display, is_be));
723 out.extend_from_slice(&encode_f64(nt.control.limit_low, is_be));
724 out.extend_from_slice(&encode_f64(nt.control.limit_high, is_be));
725 out.extend_from_slice(&encode_f64(nt.control.min_step, is_be));
726 out
727}
728
729pub fn nt_table_desc(nt: &NtTable) -> StructureDesc {
730 let mut value_fields: Vec<FieldDesc> = Vec::new();
731 for col in &nt.columns {
732 value_fields.push(FieldDesc {
733 name: col.name.clone(),
734 field_type: scalar_array_field_type(&col.values),
735 });
736 }
737 StructureDesc {
738 struct_id: Some("epics:nt/NTTable:1.0".to_string()),
739 fields: vec![
740 FieldDesc {
741 name: "labels".to_string(),
742 field_type: FieldType::StringArray,
743 },
744 FieldDesc {
745 name: "value".to_string(),
746 field_type: FieldType::Structure(StructureDesc {
747 struct_id: None,
748 fields: value_fields,
749 }),
750 },
751 ],
752 }
753}
754
755pub fn encode_nt_table_full(nt: &NtTable, is_be: bool) -> Vec<u8> {
756 let mut out = Vec::new();
757 out.extend_from_slice(&encode_string_array(&nt.labels, is_be));
758 for NtTableColumn { values, .. } in &nt.columns {
759 out.extend_from_slice(&encode_scalar_array_value_pvd(values, is_be));
760 }
761 out
762}
763
764fn nt_ndarray_value_union_fields() -> Vec<FieldDesc> {
765 vec![
766 FieldDesc {
767 name: "booleanValue".to_string(),
768 field_type: FieldType::ScalarArray(TypeCode::Boolean),
769 },
770 FieldDesc {
771 name: "byteValue".to_string(),
772 field_type: FieldType::ScalarArray(TypeCode::Int8),
773 },
774 FieldDesc {
775 name: "shortValue".to_string(),
776 field_type: FieldType::ScalarArray(TypeCode::Int16),
777 },
778 FieldDesc {
779 name: "intValue".to_string(),
780 field_type: FieldType::ScalarArray(TypeCode::Int32),
781 },
782 FieldDesc {
783 name: "longValue".to_string(),
784 field_type: FieldType::ScalarArray(TypeCode::Int64),
785 },
786 FieldDesc {
787 name: "ubyteValue".to_string(),
788 field_type: FieldType::ScalarArray(TypeCode::UInt8),
789 },
790 FieldDesc {
791 name: "ushortValue".to_string(),
792 field_type: FieldType::ScalarArray(TypeCode::UInt16),
793 },
794 FieldDesc {
795 name: "uintValue".to_string(),
796 field_type: FieldType::ScalarArray(TypeCode::UInt32),
797 },
798 FieldDesc {
799 name: "ulongValue".to_string(),
800 field_type: FieldType::ScalarArray(TypeCode::UInt64),
801 },
802 FieldDesc {
803 name: "floatValue".to_string(),
804 field_type: FieldType::ScalarArray(TypeCode::Float32),
805 },
806 FieldDesc {
807 name: "doubleValue".to_string(),
808 field_type: FieldType::ScalarArray(TypeCode::Float64),
809 },
810 FieldDesc {
811 name: "stringValue".to_string(),
812 field_type: FieldType::StringArray,
813 },
814 ]
815}
816
817fn ndarray_union_index(value: &ScalarArrayValue) -> usize {
818 match value {
819 ScalarArrayValue::Bool(_) => 0,
820 ScalarArrayValue::I8(_) => 1,
821 ScalarArrayValue::I16(_) => 2,
822 ScalarArrayValue::I32(_) => 3,
823 ScalarArrayValue::I64(_) => 4,
824 ScalarArrayValue::U8(_) => 5,
825 ScalarArrayValue::U16(_) => 6,
826 ScalarArrayValue::U32(_) => 7,
827 ScalarArrayValue::U64(_) => 8,
828 ScalarArrayValue::F32(_) => 9,
829 ScalarArrayValue::F64(_) => 10,
830 ScalarArrayValue::Str(_) => 11,
831 }
832}
833
834fn encode_ndarray_union(value: &ScalarArrayValue, is_be: bool) -> Vec<u8> {
835 let mut out = Vec::new();
836 out.extend_from_slice(&encode_size_pvd(ndarray_union_index(value), is_be));
837 out.extend_from_slice(&encode_scalar_array_value_pvd(value, is_be));
838 out
839}
840
841fn encode_codec_parameters(
842 parameters: &std::collections::HashMap<String, String>,
843 is_be: bool,
844) -> Vec<u8> {
845 if parameters.is_empty() {
846 return vec![0xFF];
847 }
848 let mut out = Vec::new();
849 out.push(0x80);
850 let mut fields = Vec::new();
851 for key in parameters.keys() {
852 fields.push(FieldDesc {
853 name: key.clone(),
854 field_type: FieldType::String,
855 });
856 }
857 let desc = StructureDesc {
858 struct_id: None,
859 fields,
860 };
861 out.extend_from_slice(&encode_structure_desc(&desc, is_be));
862 for value in parameters.values() {
863 out.extend_from_slice(&encode_string_pvd(value, is_be));
864 }
865 out
866}
867
868pub fn nt_ndarray_desc(_nt: &NtNdArray) -> StructureDesc {
869 StructureDesc {
870 struct_id: Some("epics:nt/NTNDArray:1.0".to_string()),
871 fields: vec![
872 FieldDesc {
873 name: "value".to_string(),
874 field_type: FieldType::Union(nt_ndarray_value_union_fields()),
875 },
876 FieldDesc {
877 name: "codec".to_string(),
878 field_type: FieldType::Structure(StructureDesc {
879 struct_id: Some("codec_t".to_string()),
880 fields: vec![
881 FieldDesc {
882 name: "name".to_string(),
883 field_type: FieldType::String,
884 },
885 FieldDesc {
886 name: "parameters".to_string(),
887 field_type: FieldType::Variant,
888 },
889 ],
890 }),
891 },
892 FieldDesc {
893 name: "compressedSize".to_string(),
894 field_type: FieldType::Scalar(TypeCode::Int64),
895 },
896 FieldDesc {
897 name: "uncompressedSize".to_string(),
898 field_type: FieldType::Scalar(TypeCode::Int64),
899 },
900 FieldDesc {
901 name: "dimension".to_string(),
902 field_type: FieldType::StructureArray(StructureDesc {
903 struct_id: Some("dimension_t".to_string()),
904 fields: vec![
905 FieldDesc {
906 name: "size".to_string(),
907 field_type: FieldType::Scalar(TypeCode::Int32),
908 },
909 FieldDesc {
910 name: "offset".to_string(),
911 field_type: FieldType::Scalar(TypeCode::Int32),
912 },
913 FieldDesc {
914 name: "fullSize".to_string(),
915 field_type: FieldType::Scalar(TypeCode::Int32),
916 },
917 FieldDesc {
918 name: "binning".to_string(),
919 field_type: FieldType::Scalar(TypeCode::Int32),
920 },
921 FieldDesc {
922 name: "reverse".to_string(),
923 field_type: FieldType::Scalar(TypeCode::Boolean),
924 },
925 ],
926 }),
927 },
928 FieldDesc {
929 name: "uniqueId".to_string(),
930 field_type: FieldType::Scalar(TypeCode::Int32),
931 },
932 FieldDesc {
933 name: "dataTimeStamp".to_string(),
934 field_type: FieldType::Structure(timestamp_desc()),
935 },
936 FieldDesc {
937 name: "attribute".to_string(),
938 field_type: FieldType::StructureArray(StructureDesc {
939 struct_id: Some("NTAttribute".to_string()),
940 fields: vec![
941 FieldDesc {
942 name: "name".to_string(),
943 field_type: FieldType::String,
944 },
945 FieldDesc {
946 name: "value".to_string(),
947 field_type: FieldType::Variant,
948 },
949 FieldDesc {
950 name: "descriptor".to_string(),
951 field_type: FieldType::String,
952 },
953 FieldDesc {
954 name: "sourceType".to_string(),
955 field_type: FieldType::Scalar(TypeCode::Int32),
956 },
957 FieldDesc {
958 name: "source".to_string(),
959 field_type: FieldType::String,
960 },
961 ],
962 }),
963 },
964 FieldDesc {
965 name: "descriptor".to_string(),
966 field_type: FieldType::String,
967 },
968 FieldDesc {
969 name: "alarm".to_string(),
970 field_type: FieldType::Structure(alarm_desc()),
971 },
972 FieldDesc {
973 name: "timeStamp".to_string(),
974 field_type: FieldType::Structure(timestamp_desc()),
975 },
976 FieldDesc {
977 name: "display".to_string(),
978 field_type: FieldType::Structure(display_desc()),
979 },
980 ],
981 }
982}
983
984fn encode_attribute_variant(attr: &NtAttribute, is_be: bool) -> Vec<u8> {
985 match &attr.value {
986 ScalarValue::Bool(v) => {
987 let mut out = vec![TypeCode::Boolean as u8];
988 out.push(if *v { 1 } else { 0 });
989 out
990 }
991 ScalarValue::I8(v) => {
992 let mut out = vec![TypeCode::Int8 as u8];
993 out.push(*v as u8);
994 out
995 }
996 ScalarValue::I16(v) => {
997 let mut out = vec![TypeCode::Int16 as u8];
998 out.extend_from_slice(&if is_be { v.to_be_bytes().to_vec() } else { v.to_le_bytes().to_vec() });
999 out
1000 }
1001 ScalarValue::I32(v) => {
1002 let mut out = vec![TypeCode::Int32 as u8];
1003 out.extend_from_slice(&encode_i32(*v, is_be));
1004 out
1005 }
1006 ScalarValue::I64(v) => {
1007 let mut out = vec![TypeCode::Int64 as u8];
1008 out.extend_from_slice(&encode_i64(*v, is_be));
1009 out
1010 }
1011 ScalarValue::U8(v) => {
1012 let mut out = vec![TypeCode::UInt8 as u8];
1013 out.push(*v);
1014 out
1015 }
1016 ScalarValue::U16(v) => {
1017 let mut out = vec![TypeCode::UInt16 as u8];
1018 out.extend_from_slice(&if is_be { v.to_be_bytes().to_vec() } else { v.to_le_bytes().to_vec() });
1019 out
1020 }
1021 ScalarValue::U32(v) => {
1022 let mut out = vec![TypeCode::UInt32 as u8];
1023 out.extend_from_slice(&if is_be { v.to_be_bytes().to_vec() } else { v.to_le_bytes().to_vec() });
1024 out
1025 }
1026 ScalarValue::U64(v) => {
1027 let mut out = vec![TypeCode::UInt64 as u8];
1028 out.extend_from_slice(&if is_be { v.to_be_bytes().to_vec() } else { v.to_le_bytes().to_vec() });
1029 out
1030 }
1031 ScalarValue::F32(v) => {
1032 let mut out = vec![TypeCode::Float32 as u8];
1033 out.extend_from_slice(&if is_be { v.to_be_bytes().to_vec() } else { v.to_le_bytes().to_vec() });
1034 out
1035 }
1036 ScalarValue::F64(v) => {
1037 let mut out = vec![TypeCode::Float64 as u8];
1038 out.extend_from_slice(&encode_f64(*v, is_be));
1039 out
1040 }
1041 ScalarValue::Str(v) => {
1042 let mut out = vec![TypeCode::String as u8];
1043 out.extend_from_slice(&encode_string_pvd(v, is_be));
1044 out
1045 }
1046 }
1047}
1048
1049pub fn encode_nt_ndarray_full(nt: &NtNdArray, is_be: bool) -> Vec<u8> {
1050 let mut out = Vec::new();
1051 out.extend_from_slice(&encode_ndarray_union(&nt.value, is_be));
1052 out.extend_from_slice(&encode_string_pvd(&nt.codec.name, is_be));
1053 out.extend_from_slice(&encode_codec_parameters(&nt.codec.parameters, is_be));
1054 out.extend_from_slice(&encode_i64(nt.compressed_size, is_be));
1055 out.extend_from_slice(&encode_i64(nt.uncompressed_size, is_be));
1056 out.extend_from_slice(&encode_size_pvd(nt.dimension.len(), is_be));
1057 for NdDimension {
1058 size,
1059 offset,
1060 full_size,
1061 binning,
1062 reverse,
1063 } in &nt.dimension
1064 {
1065 out.push(1); out.extend_from_slice(&encode_i32(*size, is_be));
1067 out.extend_from_slice(&encode_i32(*offset, is_be));
1068 out.extend_from_slice(&encode_i32(*full_size, is_be));
1069 out.extend_from_slice(&encode_i32(*binning, is_be));
1070 out.push(if *reverse { 1 } else { 0 });
1071 }
1072 out.extend_from_slice(&encode_i32(nt.unique_id, is_be));
1073 out.extend_from_slice(&encode_nt_timestamp(&nt.data_time_stamp, is_be));
1074 out.extend_from_slice(&encode_size_pvd(nt.attribute.len(), is_be));
1075 for attr in &nt.attribute {
1076 out.push(1); out.extend_from_slice(&encode_string_pvd(&attr.name, is_be));
1078 out.extend_from_slice(&encode_attribute_variant(attr, is_be));
1079 out.extend_from_slice(&encode_string_pvd(&attr.descriptor, is_be));
1080 out.extend_from_slice(&encode_i32(attr.source_type, is_be));
1081 out.extend_from_slice(&encode_string_pvd(&attr.source, is_be));
1082 }
1083 out.extend_from_slice(&encode_string_pvd(
1084 nt.descriptor.as_deref().unwrap_or(""),
1085 is_be,
1086 ));
1087 out.extend_from_slice(&encode_nt_alarm(
1088 nt.alarm.as_ref().unwrap_or(&NtAlarm::default()),
1089 is_be,
1090 ));
1091 out.extend_from_slice(&encode_nt_timestamp(
1092 nt.time_stamp.as_ref().unwrap_or(&NtTimeStamp::default()),
1093 is_be,
1094 ));
1095 out.extend_from_slice(&encode_nt_display(
1096 nt.display.as_ref().unwrap_or(&NtDisplay::default()),
1097 is_be,
1098 ));
1099 out
1100}
1101
1102pub fn nt_payload_desc(payload: &NtPayload) -> StructureDesc {
1103 match payload {
1104 NtPayload::Scalar(nt) => nt_scalar_desc(&nt.value),
1105 NtPayload::ScalarArray(nt) => nt_scalar_array_desc(&nt.value),
1106 NtPayload::Table(nt) => nt_table_desc(nt),
1107 NtPayload::NdArray(nt) => nt_ndarray_desc(nt),
1108 }
1109}
1110
1111pub fn encode_nt_payload_full(payload: &NtPayload, is_be: bool) -> Vec<u8> {
1112 match payload {
1113 NtPayload::Scalar(nt) => encode_nt_scalar_full(nt, is_be),
1114 NtPayload::ScalarArray(nt) => encode_nt_scalar_array_full(nt, is_be),
1115 NtPayload::Table(nt) => encode_nt_table_full(nt, is_be),
1116 NtPayload::NdArray(nt) => encode_nt_ndarray_full(nt, is_be),
1117 }
1118}
1119
1120pub fn encode_nt_payload_bitset(payload: &NtPayload, is_be: bool) -> Vec<u8> {
1121 let desc = nt_payload_desc(payload);
1122 let mut out = Vec::new();
1123 out.extend_from_slice(&encode_structure_bitset(&desc, is_be));
1124 out.extend_from_slice(&encode_nt_payload_full(payload, is_be));
1125 out
1126}
1127
1128pub fn encode_nt_payload_bitset_parts(payload: &NtPayload, is_be: bool) -> (Vec<u8>, Vec<u8>) {
1129 let desc = nt_payload_desc(payload);
1130 (
1131 encode_structure_bitset(&desc, is_be),
1132 encode_nt_payload_full(payload, is_be),
1133 )
1134}
1135
1136use crate::spvd_decode::DecodedValue;
1141
1142pub fn encode_decoded_value(val: &DecodedValue, is_be: bool) -> Vec<u8> {
1144 match val {
1145 DecodedValue::Null => Vec::new(),
1146 DecodedValue::Boolean(v) => vec![if *v { 1 } else { 0 }],
1147 DecodedValue::Int8(v) => vec![*v as u8],
1148 DecodedValue::Int16(v) => {
1149 if is_be { v.to_be_bytes().to_vec() } else { v.to_le_bytes().to_vec() }
1150 }
1151 DecodedValue::Int32(v) => encode_i32(*v, is_be),
1152 DecodedValue::Int64(v) => encode_i64(*v, is_be),
1153 DecodedValue::UInt8(v) => vec![*v],
1154 DecodedValue::UInt16(v) => {
1155 if is_be { v.to_be_bytes().to_vec() } else { v.to_le_bytes().to_vec() }
1156 }
1157 DecodedValue::UInt32(v) => {
1158 if is_be { v.to_be_bytes().to_vec() } else { v.to_le_bytes().to_vec() }
1159 }
1160 DecodedValue::UInt64(v) => {
1161 if is_be { v.to_be_bytes().to_vec() } else { v.to_le_bytes().to_vec() }
1162 }
1163 DecodedValue::Float32(v) => {
1164 if is_be { v.to_be_bytes().to_vec() } else { v.to_le_bytes().to_vec() }
1165 }
1166 DecodedValue::Float64(v) => encode_f64(*v, is_be),
1167 DecodedValue::String(v) => encode_string_pvd(v, is_be),
1168 DecodedValue::Array(arr) => {
1169 let mut out = encode_size_pvd(arr.len(), is_be);
1170 for item in arr {
1171 out.extend_from_slice(&encode_decoded_value(item, is_be));
1172 }
1173 out
1174 }
1175 DecodedValue::Structure(fields) => {
1176 let mut out = Vec::new();
1177 for (_name, value) in fields {
1178 out.extend_from_slice(&encode_decoded_value(value, is_be));
1179 }
1180 out
1181 }
1182 DecodedValue::Raw(data) => data.clone(),
1183 }
1184}
1185
1186pub fn encode_pv_request(fields: &[&str], is_be: bool) -> Vec<u8> {
1200 let inner_fields: Vec<FieldDesc> = fields
1203 .iter()
1204 .map(|name| FieldDesc {
1205 name: name.to_string(),
1206 field_type: FieldType::Structure(StructureDesc {
1207 struct_id: None,
1208 fields: Vec::new(),
1209 }),
1210 })
1211 .collect();
1212
1213 let field_desc = StructureDesc {
1214 struct_id: None,
1215 fields: inner_fields,
1216 };
1217
1218 let pv_request_desc = StructureDesc {
1219 struct_id: None,
1220 fields: vec![FieldDesc {
1221 name: "field".to_string(),
1222 field_type: FieldType::Structure(field_desc),
1223 }],
1224 };
1225
1226 let mut out = Vec::new();
1227 out.push(0x80); out.extend_from_slice(&encode_structure_desc(&pv_request_desc, is_be));
1229 out
1232}
1233
1234#[cfg(test)]
1235mod tests {
1236 use super::*;
1237 use crate::spvd_decode::PvdDecoder;
1238
1239 #[test]
1240 fn nt_scalar_roundtrip() {
1241 let nt = NtScalar::from_value(ScalarValue::F64(12.5));
1242 let desc = nt_scalar_desc(&nt.value);
1243 let desc_bytes = encode_structure_desc(&desc, false);
1244 let mut pvd = Vec::new();
1245 pvd.push(0x80);
1246 pvd.extend_from_slice(&desc_bytes);
1247 pvd.extend_from_slice(&encode_nt_scalar_full(&nt, false));
1248
1249 let decoder = PvdDecoder::new(false);
1250 let parsed_desc = decoder.parse_introspection(&pvd).expect("desc");
1251 let (_, consumed) = decoder
1252 .decode_structure(&pvd[1 + desc_bytes.len()..], &parsed_desc)
1253 .expect("value");
1254 assert!(consumed > 0);
1255 }
1256
1257 #[test]
1258 fn nt_ndarray_roundtrip() {
1259 use spvirit_types::{NdCodec, NdDimension, NtAlarm, NtNdArray, NtTimeStamp, ScalarArrayValue};
1260 use std::collections::HashMap;
1261
1262 let nt = NtNdArray {
1263 value: ScalarArrayValue::U8(vec![1, 2, 3, 4]),
1264 codec: NdCodec {
1265 name: String::new(),
1266 parameters: HashMap::new(),
1267 },
1268 compressed_size: 4,
1269 uncompressed_size: 4,
1270 dimension: vec![NdDimension {
1271 size: 2,
1272 offset: 0,
1273 full_size: 2,
1274 binning: 1,
1275 reverse: false,
1276 }],
1277 unique_id: 42,
1278 data_time_stamp: NtTimeStamp {
1279 seconds_past_epoch: 1000,
1280 nanoseconds: 500,
1281 user_tag: 0,
1282 },
1283 attribute: Vec::new(),
1284 descriptor: Some("test".to_string()),
1285 alarm: Some(NtAlarm::default()),
1286 time_stamp: Some(NtTimeStamp::default()),
1287 display: None,
1288 };
1289
1290 let desc = nt_ndarray_desc(&nt);
1291 let desc_bytes = encode_structure_desc(&desc, false);
1292 let data_bytes = encode_nt_ndarray_full(&nt, false);
1293
1294 let mut pvd = Vec::new();
1296 pvd.push(0x80);
1297 pvd.extend_from_slice(&desc_bytes);
1298 pvd.extend_from_slice(&data_bytes);
1299
1300 let decoder = PvdDecoder::new(false);
1301 let parsed_desc = decoder.parse_introspection(&pvd).expect("desc parse failed");
1302 let data_start = 1 + desc_bytes.len();
1303 let (_decoded, consumed) = decoder
1304 .decode_structure(&pvd[data_start..], &parsed_desc)
1305 .expect("data decode failed");
1306 assert!(consumed > 0, "consumed should be > 0");
1307 assert_eq!(consumed, data_bytes.len(), "consumed should match data_bytes.len()");
1308 }
1309}