1use std::any::Any;
2use std::collections::HashMap;
3use std::fmt;
4use std::sync::Arc;
5use std::time::SystemTime;
6
7use crate::error::{AsynError, AsynResult, AsynStatus};
8
9#[derive(Debug, Clone, PartialEq, Eq)]
11pub struct EnumEntry {
12 pub string: String,
13 pub value: i32,
14 pub severity: u16,
15}
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25pub enum InterruptReason {
26 ZeroToOne,
27 OneToZero,
28 Both,
29}
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq)]
33pub enum ParamType {
34 Int32,
35 Int64,
36 UInt64,
37 Float64,
38 Octet,
39 UInt32Digital,
40 Int8Array,
41 Int16Array,
42 Int32Array,
43 Int64Array,
44 UInt64Array,
45 Float32Array,
46 Float64Array,
47 Enum,
48 GenericPointer,
49}
50
51#[derive(Clone)]
54pub enum ParamValue {
55 Int32(i32),
56 Int64(i64),
57 UInt64(u64),
59 Float64(f64),
60 Octet(String),
61 UInt32Digital(u32),
62 Int8Array(Arc<[i8]>),
63 Int16Array(Arc<[i16]>),
64 Int32Array(Arc<[i32]>),
65 Int64Array(Arc<[i64]>),
66 UInt64Array(Arc<[u64]>),
68 Float32Array(Arc<[f32]>),
69 Float64Array(Arc<[f64]>),
70 Enum {
71 index: usize,
72 choices: Arc<[EnumEntry]>,
73 },
74 GenericPointer(Arc<dyn Any + Send + Sync>),
75 Undefined,
76}
77
78impl fmt::Debug for ParamValue {
79 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
80 match self {
81 Self::Int32(v) => write!(f, "Int32({v:?})"),
82 Self::Int64(v) => write!(f, "Int64({v:?})"),
83 Self::UInt64(v) => write!(f, "UInt64({v:?})"),
84 Self::Float64(v) => write!(f, "Float64({v:?})"),
85 Self::Octet(v) => write!(f, "Octet({v:?})"),
86 Self::UInt32Digital(v) => write!(f, "UInt32Digital({v:?})"),
87 Self::Int8Array(v) => write!(f, "Int8Array({v:?})"),
88 Self::Int16Array(v) => write!(f, "Int16Array({v:?})"),
89 Self::Int32Array(v) => write!(f, "Int32Array({v:?})"),
90 Self::Int64Array(v) => write!(f, "Int64Array({v:?})"),
91 Self::UInt64Array(v) => write!(f, "UInt64Array({v:?})"),
92 Self::Float32Array(v) => write!(f, "Float32Array({v:?})"),
93 Self::Float64Array(v) => write!(f, "Float64Array({v:?})"),
94 Self::Enum { index, choices } => write!(f, "Enum(index={index}, choices={choices:?})"),
95 Self::GenericPointer(v) => write!(f, "GenericPointer(<{:?}>)", (*v).type_id()),
96 Self::Undefined => write!(f, "Undefined"),
97 }
98 }
99}
100
101impl ParamValue {
102 pub fn type_name(&self) -> &'static str {
103 match self {
104 Self::Int32(_) => "Int32",
105 Self::Int64(_) => "Int64",
106 Self::UInt64(_) => "UInt64",
107 Self::Float64(_) => "Float64",
108 Self::Octet(_) => "Octet",
109 Self::UInt32Digital(_) => "UInt32Digital",
110 Self::Int8Array(_) => "Int8Array",
111 Self::Int16Array(_) => "Int16Array",
112 Self::Int32Array(_) => "Int32Array",
113 Self::Int64Array(_) => "Int64Array",
114 Self::UInt64Array(_) => "UInt64Array",
115 Self::Float32Array(_) => "Float32Array",
116 Self::Float64Array(_) => "Float64Array",
117 Self::Enum { .. } => "Enum",
118 Self::GenericPointer(_) => "GenericPointer",
119 Self::Undefined => "Undefined",
120 }
121 }
122
123 pub fn as_int32(&self) -> AsynResult<i32> {
135 match self {
136 Self::Int32(v) => Ok(*v),
137 Self::Enum { index, .. } => Ok(*index as i32),
138 other => Err(AsynError::TypeMismatch {
139 expected: "Int32",
140 actual: other.type_name(),
141 }),
142 }
143 }
144
145 pub fn as_int64(&self) -> AsynResult<i64> {
147 match self {
148 Self::Int64(v) => Ok(*v),
149 other => Err(AsynError::TypeMismatch {
150 expected: "Int64",
151 actual: other.type_name(),
152 }),
153 }
154 }
155
156 pub fn as_float64(&self) -> AsynResult<f64> {
158 match self {
159 Self::Float64(v) => Ok(*v),
160 other => Err(AsynError::TypeMismatch {
161 expected: "Float64",
162 actual: other.type_name(),
163 }),
164 }
165 }
166
167 pub fn as_octet(&self) -> AsynResult<&str> {
169 match self {
170 Self::Octet(s) => Ok(s),
171 other => Err(AsynError::TypeMismatch {
172 expected: "Octet",
173 actual: other.type_name(),
174 }),
175 }
176 }
177
178 pub fn as_uint32(&self) -> AsynResult<u32> {
181 match self {
182 Self::UInt32Digital(v) => Ok(*v),
183 other => Err(AsynError::TypeMismatch {
184 expected: "UInt32Digital",
185 actual: other.type_name(),
186 }),
187 }
188 }
189
190 pub fn as_enum(&self) -> AsynResult<(usize, Arc<[EnumEntry]>)> {
192 match self {
193 Self::Enum { index, choices } => Ok((*index, choices.clone())),
194 other => Err(AsynError::TypeMismatch {
195 expected: "Enum",
196 actual: other.type_name(),
197 }),
198 }
199 }
200
201 pub fn is_array(&self) -> bool {
209 matches!(
210 self,
211 Self::Int8Array(_)
212 | Self::Int16Array(_)
213 | Self::Int32Array(_)
214 | Self::Int64Array(_)
215 | Self::UInt64Array(_)
216 | Self::Float32Array(_)
217 | Self::Float64Array(_)
218 | Self::GenericPointer(_)
219 )
220 }
221}
222
223#[derive(Debug, Clone)]
224struct ParamEntry {
225 name: String,
226 param_type: ParamType,
227 value: ParamValue,
228 defined: bool,
230 status: AsynStatus,
231 alarm_status: u16,
232 alarm_severity: u16,
233 value_changed: bool,
234 timestamp: Option<SystemTime>,
235 uint32_interrupt_mask: u32,
238 uint32_rising_mask: u32,
244 uint32_falling_mask: u32,
248}
249
250impl ParamEntry {
251 fn new(name: String, param_type: ParamType) -> Self {
252 let value = match param_type {
257 ParamType::Int32 => ParamValue::Int32(0),
258 ParamType::Int64 => ParamValue::Int64(0),
259 ParamType::UInt64 => ParamValue::UInt64(0),
260 ParamType::Float64 => ParamValue::Float64(0.0),
261 ParamType::Octet => ParamValue::Octet(String::new()),
262 ParamType::UInt32Digital => ParamValue::UInt32Digital(0),
263 ParamType::Int8Array => ParamValue::Int8Array(Arc::from([] as [i8; 0])),
264 ParamType::Int16Array => ParamValue::Int16Array(Arc::from([] as [i16; 0])),
265 ParamType::Int32Array => ParamValue::Int32Array(Arc::from([] as [i32; 0])),
266 ParamType::Int64Array => ParamValue::Int64Array(Arc::from([] as [i64; 0])),
267 ParamType::UInt64Array => ParamValue::UInt64Array(Arc::from([] as [u64; 0])),
268 ParamType::Float32Array => ParamValue::Float32Array(Arc::from([] as [f32; 0])),
269 ParamType::Float64Array => ParamValue::Float64Array(Arc::from([] as [f64; 0])),
270 ParamType::Enum => ParamValue::Enum {
271 index: 0,
272 choices: Arc::from([EnumEntry {
273 string: String::new(),
274 value: 0,
275 severity: 0,
276 }]),
277 },
278 ParamType::GenericPointer => ParamValue::GenericPointer(Arc::new(())),
279 };
280 Self {
281 name,
282 param_type,
283 value,
284 defined: false,
285 status: AsynStatus::Success,
286 alarm_status: 0,
287 alarm_severity: 0,
288 value_changed: false,
289 timestamp: None,
290 uint32_interrupt_mask: 0,
291 uint32_rising_mask: 0,
292 uint32_falling_mask: 0,
293 }
294 }
295}
296
297pub struct ParamList {
300 max_addr: usize,
301 multi_device: bool,
302 params: Vec<Vec<ParamEntry>>,
304 name_to_index: HashMap<String, usize>,
305}
306
307impl ParamList {
308 pub fn new(max_addr: usize, multi_device: bool) -> Self {
309 let max_addr = max_addr.max(1);
310 Self {
311 max_addr,
312 multi_device,
313 params: (0..max_addr).map(|_| Vec::new()).collect(),
314 name_to_index: HashMap::new(),
315 }
316 }
317
318 fn validate_addr(&self, addr: i32) -> AsynResult<usize> {
322 if !self.multi_device {
323 return Ok(0);
324 }
325 if addr < 0 || (addr as usize) >= self.max_addr {
326 return Err(AsynError::AddressOutOfRange(addr));
327 }
328 Ok(addr as usize)
329 }
330
331 fn get_entry(&self, index: usize, addr: i32) -> AsynResult<&ParamEntry> {
332 let a = self.validate_addr(addr)?;
333 self.params[a]
334 .get(index)
335 .ok_or(AsynError::ParamIndexOutOfRange(index))
336 }
337
338 fn get_entry_mut(&mut self, index: usize, addr: i32) -> AsynResult<&mut ParamEntry> {
339 let a = self.validate_addr(addr)?;
340 self.params[a]
341 .get_mut(index)
342 .ok_or(AsynError::ParamIndexOutOfRange(index))
343 }
344
345 pub fn create_param(&mut self, name: &str, param_type: ParamType) -> AsynResult<usize> {
363 if let Some(&idx) = self.name_to_index.get(name) {
364 return Ok(idx);
365 }
366 self.append_param(name, param_type)
367 }
368
369 pub fn create_param_strict(&mut self, name: &str, param_type: ParamType) -> AsynResult<usize> {
381 if self.name_to_index.contains_key(name) {
382 return Err(AsynError::ParamAlreadyExists(name.to_string()));
383 }
384 self.append_param(name, param_type)
385 }
386
387 fn append_param(&mut self, name: &str, param_type: ParamType) -> AsynResult<usize> {
388 let index = self.params[0].len();
389 for addr_params in &mut self.params {
390 addr_params.push(ParamEntry::new(name.to_string(), param_type));
391 }
392 self.name_to_index.insert(name.to_string(), index);
393 Ok(index)
394 }
395
396 pub fn find_param(&self, name: &str) -> Option<usize> {
398 self.name_to_index.get(name).copied()
399 }
400
401 pub fn param_name(&self, index: usize) -> Option<&str> {
403 self.params[0].get(index).map(|e| e.name.as_str())
404 }
405
406 pub fn param_type(&self, index: usize) -> Option<ParamType> {
408 self.params[0].get(index).map(|e| e.param_type)
409 }
410
411 pub fn get_value(&self, index: usize, addr: i32) -> AsynResult<&ParamValue> {
413 Ok(&self.get_entry(index, addr)?.value)
414 }
415
416 pub fn get_int32(&self, index: usize, addr: i32) -> AsynResult<i32> {
426 self.get_entry(index, addr)?.value.as_int32()
427 }
428
429 pub fn get_int32_strict(&self, index: usize, addr: i32) -> AsynResult<i32> {
437 let entry = self.get_entry(index, addr)?;
438 let value = entry.value.as_int32()?;
440 if !entry.defined {
441 return Err(AsynError::ParamUndefined(index));
442 }
443 Ok(value)
444 }
445
446 pub fn set_int32(&mut self, index: usize, addr: i32, value: i32) -> AsynResult<()> {
447 let entry = self.get_entry_mut(index, addr)?;
448 match entry.value {
449 ParamValue::Int32(ref old) => {
450 if !entry.defined || *old != value {
454 entry.value = ParamValue::Int32(value);
455 entry.value_changed = true;
456 entry.defined = true;
457 }
458 }
459 ParamValue::Enum {
461 ref choices,
462 ref mut index,
463 } => {
464 let new_idx = value as usize;
465 if new_idx >= choices.len() {
466 return Err(AsynError::Status {
467 status: AsynStatus::Error,
468 message: format!(
469 "enum index {new_idx} out of range (0..{})",
470 choices.len()
471 ),
472 });
473 }
474 if !entry.defined || *index != new_idx {
475 *index = new_idx;
476 entry.value_changed = true;
477 entry.defined = true;
478 }
479 }
480 _ => {
481 return Err(AsynError::TypeMismatch {
482 expected: "Int32",
483 actual: entry.value.type_name(),
484 });
485 }
486 }
487 Ok(())
488 }
489
490 pub fn get_float64(&self, index: usize, addr: i32) -> AsynResult<f64> {
493 self.get_entry(index, addr)?.value.as_float64()
494 }
495
496 pub fn get_float64_strict(&self, index: usize, addr: i32) -> AsynResult<f64> {
501 let entry = self.get_entry(index, addr)?;
502 let value = entry.value.as_float64()?;
503 if !entry.defined {
504 return Err(AsynError::ParamUndefined(index));
505 }
506 Ok(value)
507 }
508
509 pub fn set_float64(&mut self, index: usize, addr: i32, value: f64) -> AsynResult<()> {
510 let entry = self.get_entry_mut(index, addr)?;
511 if let ParamValue::Float64(ref old) = entry.value {
512 if !entry.defined || *old != value {
516 entry.value = ParamValue::Float64(value);
517 entry.value_changed = true;
518 entry.defined = true;
519 }
520 } else {
521 return Err(AsynError::TypeMismatch {
522 expected: "Float64",
523 actual: entry.value.type_name(),
524 });
525 }
526 Ok(())
527 }
528
529 pub fn get_int64(&self, index: usize, addr: i32) -> AsynResult<i64> {
532 self.get_entry(index, addr)?.value.as_int64()
533 }
534
535 pub fn get_int64_strict(&self, index: usize, addr: i32) -> AsynResult<i64> {
540 let entry = self.get_entry(index, addr)?;
541 let value = entry.value.as_int64()?;
542 if !entry.defined {
543 return Err(AsynError::ParamUndefined(index));
544 }
545 Ok(value)
546 }
547
548 pub fn set_int64(&mut self, index: usize, addr: i32, value: i64) -> AsynResult<()> {
549 let entry = self.get_entry_mut(index, addr)?;
550 if let ParamValue::Int64(ref old) = entry.value {
551 if !entry.defined || *old != value {
553 entry.value = ParamValue::Int64(value);
554 entry.value_changed = true;
555 entry.defined = true;
556 }
557 } else {
558 return Err(AsynError::TypeMismatch {
559 expected: "Int64",
560 actual: entry.value.type_name(),
561 });
562 }
563 Ok(())
564 }
565
566 pub fn get_string(&self, index: usize, addr: i32) -> AsynResult<&str> {
569 self.get_entry(index, addr)?.value.as_octet()
570 }
571
572 pub fn get_string_strict(&self, index: usize, addr: i32) -> AsynResult<&str> {
577 let entry = self.get_entry(index, addr)?;
578 let value = entry.value.as_octet()?;
579 if !entry.defined {
580 return Err(AsynError::ParamUndefined(index));
581 }
582 Ok(value)
583 }
584
585 pub fn set_string(&mut self, index: usize, addr: i32, value: String) -> AsynResult<()> {
586 let entry = self.get_entry_mut(index, addr)?;
587 if let ParamValue::Octet(ref old) = entry.value {
588 if !entry.defined || *old != value {
591 entry.value = ParamValue::Octet(value);
592 entry.value_changed = true;
593 entry.defined = true;
594 }
595 } else {
596 return Err(AsynError::TypeMismatch {
597 expected: "Octet",
598 actual: entry.value.type_name(),
599 });
600 }
601 Ok(())
602 }
603
604 pub fn get_uint32(&self, index: usize, addr: i32) -> AsynResult<u32> {
607 self.get_entry(index, addr)?.value.as_uint32()
608 }
609
610 pub fn get_uint32_strict(&self, index: usize, addr: i32) -> AsynResult<u32> {
615 let entry = self.get_entry(index, addr)?;
616 let value = entry.value.as_uint32()?;
617 if !entry.defined {
618 return Err(AsynError::ParamUndefined(index));
619 }
620 Ok(value)
621 }
622
623 pub fn set_uint32(
624 &mut self,
625 index: usize,
626 addr: i32,
627 value: u32,
628 mask: u32,
629 interrupt_mask: u32,
630 ) -> AsynResult<()> {
631 let entry = self.get_entry_mut(index, addr)?;
632 if let ParamValue::UInt32Digital(ref old) = entry.value {
633 let was_defined = entry.defined;
642 let starting = if was_defined { *old } else { 0 };
643 let new_val = (starting & !mask) | (value & mask);
644 let changed_bits = if was_defined {
645 starting ^ new_val
646 } else {
647 new_val
648 };
649 if !was_defined || starting != new_val {
650 entry.uint32_interrupt_mask |= changed_bits;
656 entry.value = ParamValue::UInt32Digital(new_val);
657 entry.value_changed = true;
658 entry.defined = true;
659 }
660 if interrupt_mask != 0 {
667 entry.uint32_interrupt_mask |= interrupt_mask;
668 entry.value_changed = true;
669 }
670 } else {
671 return Err(AsynError::TypeMismatch {
672 expected: "UInt32Digital",
673 actual: entry.value.type_name(),
674 });
675 }
676 Ok(())
677 }
678
679 pub fn get_uint32_interrupt_mask(&self, index: usize, addr: i32) -> AsynResult<u32> {
681 Ok(self.get_entry(index, addr)?.uint32_interrupt_mask)
682 }
683
684 pub fn take_uint32_interrupt_mask(&mut self, index: usize, addr: i32) -> AsynResult<u32> {
692 let entry = self.get_entry_mut(index, addr)?;
693 let mask = entry.uint32_interrupt_mask;
694 entry.uint32_interrupt_mask = 0;
695 Ok(mask)
696 }
697
698 pub fn set_uint32_interrupt(
706 &mut self,
707 index: usize,
708 addr: i32,
709 mask: u32,
710 reason: InterruptReason,
711 ) -> AsynResult<()> {
712 let entry = self.get_entry_mut(index, addr)?;
713 if entry.param_type != ParamType::UInt32Digital {
714 return Err(AsynError::TypeMismatch {
715 expected: "UInt32Digital",
716 actual: entry.value.type_name(),
717 });
718 }
719 match reason {
720 InterruptReason::ZeroToOne => entry.uint32_rising_mask = mask,
721 InterruptReason::OneToZero => entry.uint32_falling_mask = mask,
722 InterruptReason::Both => {
723 entry.uint32_rising_mask = mask;
724 entry.uint32_falling_mask = mask;
725 }
726 }
727 Ok(())
728 }
729
730 pub fn clear_uint32_interrupt(&mut self, index: usize, addr: i32, mask: u32) -> AsynResult<()> {
737 let entry = self.get_entry_mut(index, addr)?;
738 if entry.param_type != ParamType::UInt32Digital {
739 return Err(AsynError::TypeMismatch {
740 expected: "UInt32Digital",
741 actual: entry.value.type_name(),
742 });
743 }
744 entry.uint32_rising_mask &= !mask;
745 entry.uint32_falling_mask &= !mask;
746 Ok(())
747 }
748
749 pub fn get_uint32_interrupt(
755 &self,
756 index: usize,
757 addr: i32,
758 reason: InterruptReason,
759 ) -> AsynResult<u32> {
760 let entry = self.get_entry(index, addr)?;
761 if entry.param_type != ParamType::UInt32Digital {
762 return Err(AsynError::TypeMismatch {
763 expected: "UInt32Digital",
764 actual: entry.value.type_name(),
765 });
766 }
767 Ok(match reason {
768 InterruptReason::ZeroToOne => entry.uint32_rising_mask,
769 InterruptReason::OneToZero => entry.uint32_falling_mask,
770 InterruptReason::Both => entry.uint32_rising_mask | entry.uint32_falling_mask,
771 })
772 }
773
774 pub fn get_float64_array(&self, index: usize, addr: i32) -> AsynResult<Arc<[f64]>> {
777 match &self.get_entry(index, addr)?.value {
778 ParamValue::Float64Array(v) => Ok(v.clone()),
779 other => Err(AsynError::TypeMismatch {
780 expected: "Float64Array",
781 actual: other.type_name(),
782 }),
783 }
784 }
785
786 pub fn set_float64_array(&mut self, index: usize, addr: i32, data: Vec<f64>) -> AsynResult<()> {
787 let entry = self.get_entry_mut(index, addr)?;
788 if matches!(entry.value, ParamValue::Float64Array(_)) {
789 entry.value = ParamValue::Float64Array(Arc::from(data));
790 entry.value_changed = true;
791 entry.defined = true;
792 Ok(())
793 } else {
794 Err(AsynError::TypeMismatch {
795 expected: "Float64Array",
796 actual: entry.value.type_name(),
797 })
798 }
799 }
800
801 pub fn get_int32_array(&self, index: usize, addr: i32) -> AsynResult<Arc<[i32]>> {
802 match &self.get_entry(index, addr)?.value {
803 ParamValue::Int32Array(v) => Ok(v.clone()),
804 other => Err(AsynError::TypeMismatch {
805 expected: "Int32Array",
806 actual: other.type_name(),
807 }),
808 }
809 }
810
811 pub fn set_int32_array(&mut self, index: usize, addr: i32, data: Vec<i32>) -> AsynResult<()> {
812 let entry = self.get_entry_mut(index, addr)?;
813 if matches!(entry.value, ParamValue::Int32Array(_)) {
814 entry.value = ParamValue::Int32Array(Arc::from(data));
815 entry.value_changed = true;
816 entry.defined = true;
817 Ok(())
818 } else {
819 Err(AsynError::TypeMismatch {
820 expected: "Int32Array",
821 actual: entry.value.type_name(),
822 })
823 }
824 }
825
826 pub fn get_int8_array(&self, index: usize, addr: i32) -> AsynResult<Arc<[i8]>> {
827 match &self.get_entry(index, addr)?.value {
828 ParamValue::Int8Array(v) => Ok(v.clone()),
829 other => Err(AsynError::TypeMismatch {
830 expected: "Int8Array",
831 actual: other.type_name(),
832 }),
833 }
834 }
835
836 pub fn set_int8_array(&mut self, index: usize, addr: i32, data: Vec<i8>) -> AsynResult<()> {
837 let entry = self.get_entry_mut(index, addr)?;
838 if matches!(entry.value, ParamValue::Int8Array(_)) {
839 entry.value = ParamValue::Int8Array(Arc::from(data));
840 entry.value_changed = true;
841 entry.defined = true;
842 Ok(())
843 } else {
844 Err(AsynError::TypeMismatch {
845 expected: "Int8Array",
846 actual: entry.value.type_name(),
847 })
848 }
849 }
850
851 pub fn get_int16_array(&self, index: usize, addr: i32) -> AsynResult<Arc<[i16]>> {
852 match &self.get_entry(index, addr)?.value {
853 ParamValue::Int16Array(v) => Ok(v.clone()),
854 other => Err(AsynError::TypeMismatch {
855 expected: "Int16Array",
856 actual: other.type_name(),
857 }),
858 }
859 }
860
861 pub fn set_int16_array(&mut self, index: usize, addr: i32, data: Vec<i16>) -> AsynResult<()> {
862 let entry = self.get_entry_mut(index, addr)?;
863 if matches!(entry.value, ParamValue::Int16Array(_)) {
864 entry.value = ParamValue::Int16Array(Arc::from(data));
865 entry.value_changed = true;
866 entry.defined = true;
867 Ok(())
868 } else {
869 Err(AsynError::TypeMismatch {
870 expected: "Int16Array",
871 actual: entry.value.type_name(),
872 })
873 }
874 }
875
876 pub fn get_int64_array(&self, index: usize, addr: i32) -> AsynResult<Arc<[i64]>> {
877 match &self.get_entry(index, addr)?.value {
878 ParamValue::Int64Array(v) => Ok(v.clone()),
879 other => Err(AsynError::TypeMismatch {
880 expected: "Int64Array",
881 actual: other.type_name(),
882 }),
883 }
884 }
885
886 pub fn set_int64_array(&mut self, index: usize, addr: i32, data: Vec<i64>) -> AsynResult<()> {
887 let entry = self.get_entry_mut(index, addr)?;
888 if matches!(entry.value, ParamValue::Int64Array(_)) {
889 entry.value = ParamValue::Int64Array(Arc::from(data));
890 entry.value_changed = true;
891 entry.defined = true;
892 Ok(())
893 } else {
894 Err(AsynError::TypeMismatch {
895 expected: "Int64Array",
896 actual: entry.value.type_name(),
897 })
898 }
899 }
900
901 pub fn get_float32_array(&self, index: usize, addr: i32) -> AsynResult<Arc<[f32]>> {
902 match &self.get_entry(index, addr)?.value {
903 ParamValue::Float32Array(v) => Ok(v.clone()),
904 other => Err(AsynError::TypeMismatch {
905 expected: "Float32Array",
906 actual: other.type_name(),
907 }),
908 }
909 }
910
911 pub fn set_float32_array(&mut self, index: usize, addr: i32, data: Vec<f32>) -> AsynResult<()> {
912 let entry = self.get_entry_mut(index, addr)?;
913 if matches!(entry.value, ParamValue::Float32Array(_)) {
914 entry.value = ParamValue::Float32Array(Arc::from(data));
915 entry.value_changed = true;
916 entry.defined = true;
917 Ok(())
918 } else {
919 Err(AsynError::TypeMismatch {
920 expected: "Float32Array",
921 actual: entry.value.type_name(),
922 })
923 }
924 }
925
926 pub fn get_enum(&self, index: usize, addr: i32) -> AsynResult<(usize, Arc<[EnumEntry]>)> {
929 self.get_entry(index, addr)?.value.as_enum()
930 }
931
932 pub fn set_enum_index(&mut self, index: usize, addr: i32, value: usize) -> AsynResult<()> {
933 let entry = self.get_entry_mut(index, addr)?;
934 if let ParamValue::Enum {
935 ref choices,
936 index: ref mut idx,
937 } = entry.value
938 {
939 if value >= choices.len() {
940 return Err(AsynError::Status {
941 status: AsynStatus::Error,
942 message: format!("enum index {value} out of range (0..{})", choices.len()),
943 });
944 }
945 if !entry.defined || *idx != value {
949 *idx = value;
950 entry.value_changed = true;
951 entry.defined = true;
952 }
953 } else {
954 return Err(AsynError::TypeMismatch {
955 expected: "Enum",
956 actual: entry.value.type_name(),
957 });
958 }
959 Ok(())
960 }
961
962 pub fn set_enum_choices(
963 &mut self,
964 index: usize,
965 addr: i32,
966 choices: Arc<[EnumEntry]>,
967 ) -> AsynResult<()> {
968 let entry = self.get_entry_mut(index, addr)?;
969 if let ParamValue::Enum {
970 index: ref mut idx,
971 choices: ref mut ch,
972 } = entry.value
973 {
974 *ch = choices;
975 if *idx >= ch.len() {
977 *idx = 0;
978 }
979 entry.value_changed = true;
980 entry.defined = true;
981 } else {
982 return Err(AsynError::TypeMismatch {
983 expected: "Enum",
984 actual: entry.value.type_name(),
985 });
986 }
987 Ok(())
988 }
989
990 pub fn get_generic_pointer(
993 &self,
994 index: usize,
995 addr: i32,
996 ) -> AsynResult<Arc<dyn Any + Send + Sync>> {
997 match &self.get_entry(index, addr)?.value {
998 ParamValue::GenericPointer(v) => Ok(v.clone()),
999 other => Err(AsynError::TypeMismatch {
1000 expected: "GenericPointer",
1001 actual: other.type_name(),
1002 }),
1003 }
1004 }
1005
1006 pub fn set_generic_pointer(
1007 &mut self,
1008 index: usize,
1009 addr: i32,
1010 value: Arc<dyn Any + Send + Sync>,
1011 ) -> AsynResult<()> {
1012 let entry = self.get_entry_mut(index, addr)?;
1013 if matches!(entry.value, ParamValue::GenericPointer(_)) {
1014 entry.value = ParamValue::GenericPointer(value);
1015 entry.value_changed = true;
1016 entry.defined = true; Ok(())
1018 } else {
1019 Err(AsynError::TypeMismatch {
1020 expected: "GenericPointer",
1021 actual: entry.value.type_name(),
1022 })
1023 }
1024 }
1025
1026 pub fn set_value(&mut self, index: usize, addr: i32, value: ParamValue) -> AsynResult<()> {
1039 let type_name = value.type_name();
1040 match value {
1041 ParamValue::Int32(v) => self.set_int32(index, addr, v),
1042 ParamValue::Int64(v) => self.set_int64(index, addr, v),
1043 ParamValue::Float64(v) => self.set_float64(index, addr, v),
1044 ParamValue::Octet(s) => self.set_string(index, addr, s),
1045 ParamValue::UInt32Digital(v) => self.set_uint32(index, addr, v, u32::MAX, 0),
1046 ParamValue::Int8Array(a) => self.set_int8_array(index, addr, a.to_vec()),
1047 ParamValue::Int16Array(a) => self.set_int16_array(index, addr, a.to_vec()),
1048 ParamValue::Int32Array(a) => self.set_int32_array(index, addr, a.to_vec()),
1049 ParamValue::Int64Array(a) => self.set_int64_array(index, addr, a.to_vec()),
1050 ParamValue::Float32Array(a) => self.set_float32_array(index, addr, a.to_vec()),
1051 ParamValue::Float64Array(a) => self.set_float64_array(index, addr, a.to_vec()),
1052 ParamValue::Enum {
1053 index: idx,
1054 choices,
1055 } => {
1056 if !choices.is_empty() {
1060 self.set_enum_choices(index, addr, choices)?;
1061 }
1062 self.set_enum_index(index, addr, idx)
1063 }
1064 ParamValue::GenericPointer(p) => self.set_generic_pointer(index, addr, p),
1065 ParamValue::UInt64(_) | ParamValue::UInt64Array(_) => Err(AsynError::Status {
1072 status: AsynStatus::Error,
1073 message: format!("{type_name} parameters have no store accessor; nothing to set"),
1074 }),
1075 ParamValue::Undefined => Err(AsynError::Status {
1076 status: AsynStatus::Error,
1077 message: "cannot set a parameter to Undefined".into(),
1078 }),
1079 }
1080 }
1081
1082 pub fn is_param_defined(&self, index: usize, addr: i32) -> AsynResult<bool> {
1084 Ok(self.get_entry(index, addr)?.defined)
1085 }
1086
1087 pub fn set_param_status(
1090 &mut self,
1091 index: usize,
1092 addr: i32,
1093 status: AsynStatus,
1094 alarm_status: u16,
1095 alarm_severity: u16,
1096 ) -> AsynResult<()> {
1097 let entry = self.get_entry_mut(index, addr)?;
1098 let changed = entry.status != status
1111 || entry.alarm_status != alarm_status
1112 || entry.alarm_severity != alarm_severity;
1113 entry.status = status;
1114 entry.alarm_status = alarm_status;
1115 entry.alarm_severity = alarm_severity;
1116 if changed {
1117 entry.value_changed = true;
1118 if entry.param_type == ParamType::UInt32Digital {
1119 entry.uint32_interrupt_mask = 0xFFFF_FFFF;
1120 }
1121 }
1122 Ok(())
1123 }
1124
1125 pub fn get_param_status(&self, index: usize, addr: i32) -> AsynResult<(AsynStatus, u16, u16)> {
1126 let entry = self.get_entry(index, addr)?;
1127 Ok((entry.status, entry.alarm_status, entry.alarm_severity))
1128 }
1129
1130 pub fn set_timestamp(&mut self, index: usize, addr: i32, ts: SystemTime) -> AsynResult<()> {
1133 self.get_entry_mut(index, addr)?.timestamp = Some(ts);
1134 Ok(())
1135 }
1136
1137 pub fn get_timestamp(&self, index: usize, addr: i32) -> AsynResult<Option<SystemTime>> {
1138 Ok(self.get_entry(index, addr)?.timestamp)
1139 }
1140
1141 pub fn take_changed_single(&mut self, index: usize, addr: i32) -> AsynResult<bool> {
1145 let entry = self.get_entry_mut(index, addr)?;
1146 let was_changed = entry.value_changed;
1147 entry.value_changed = false;
1148 Ok(was_changed)
1149 }
1150
1151 pub fn mark_changed(&mut self, index: usize, addr: i32) -> AsynResult<()> {
1155 self.get_entry_mut(index, addr)?.value_changed = true;
1156 Ok(())
1157 }
1158
1159 pub fn take_changed(&mut self, addr: i32) -> AsynResult<Vec<usize>> {
1161 let a = self.validate_addr(addr)?;
1162 let mut changed = Vec::new();
1163 for (i, entry) in self.params[a].iter_mut().enumerate() {
1164 if entry.value_changed {
1165 entry.value_changed = false;
1166 changed.push(i);
1167 }
1168 }
1169 Ok(changed)
1170 }
1171
1172 pub fn len(&self) -> usize {
1174 self.params[0].len()
1175 }
1176
1177 pub fn is_empty(&self) -> bool {
1178 self.params[0].is_empty()
1179 }
1180
1181 pub fn report(&self, out: &mut dyn std::fmt::Write, addr: i32) {
1191 use std::fmt::Write as _;
1192 let _ = writeln!(out, "Number of parameters is: {}", self.len());
1193 let a = self.validate_addr(addr).unwrap_or(0);
1197 for (i, entry) in self.params[a].iter().enumerate() {
1198 entry.report(out, i);
1199 }
1200 }
1201}
1202
1203impl ParamEntry {
1204 fn report(&self, out: &mut dyn std::fmt::Write, id: usize) {
1208 use std::fmt::Write as _;
1209 let name = &self.name;
1210 let status = self.status as i32;
1213
1214 if self.param_type == ParamType::GenericPointer {
1218 let _ = writeln!(out, "Parameter {id} is undefined, name={name}");
1219 return;
1220 }
1221 let type_name = c_param_type_name(self.param_type);
1222 if !self.defined {
1223 let _ = writeln!(
1224 out,
1225 "Parameter {id} type={type_name}, name={name}, value is undefined"
1226 );
1227 return;
1228 }
1229 match &self.value {
1230 ParamValue::Int32(v) => {
1231 let _ = writeln!(
1232 out,
1233 "Parameter {id} type={type_name}, name={name}, value={v}, status={status}"
1234 );
1235 }
1236 ParamValue::Int64(v) => {
1237 let _ = writeln!(
1238 out,
1239 "Parameter {id} type={type_name}, name={name}, value={v}, status={status}"
1240 );
1241 }
1242 ParamValue::UInt64(v) => {
1243 let _ = writeln!(
1244 out,
1245 "Parameter {id} type={type_name}, name={name}, value={v}, status={status}"
1246 );
1247 }
1248 ParamValue::UInt32Digital(v) => {
1251 let _ = writeln!(
1252 out,
1253 "Parameter {id} type={type_name}, name={name}, value=0x{v:x}, \
1254 status={status}, risingMask=0x{:x}, fallingMask=0x{:x}, callbackMask=0x{:x}",
1255 self.uint32_rising_mask, self.uint32_falling_mask, self.uint32_interrupt_mask
1256 );
1257 }
1258 ParamValue::Float64(v) => {
1259 let _ = writeln!(
1260 out,
1261 "Parameter {id} type={type_name}, name={name}, value={}, status={status}",
1262 format_g(*v)
1263 );
1264 }
1265 ParamValue::Octet(v) => {
1266 let _ = writeln!(
1267 out,
1268 "Parameter {id} type={type_name}, name={name}, value={v}, status={status}"
1269 );
1270 }
1271 ParamValue::Int8Array(v) => report_array(out, id, type_name, name, v.as_ptr(), status),
1275 ParamValue::Int16Array(v) => report_array(out, id, type_name, name, v.as_ptr(), status),
1276 ParamValue::Int32Array(v) => report_array(out, id, type_name, name, v.as_ptr(), status),
1277 ParamValue::Int64Array(v) => report_array(out, id, type_name, name, v.as_ptr(), status),
1278 ParamValue::UInt64Array(v) => {
1279 report_array(out, id, type_name, name, v.as_ptr(), status)
1280 }
1281 ParamValue::Float32Array(v) => {
1282 report_array(out, id, type_name, name, v.as_ptr(), status)
1283 }
1284 ParamValue::Float64Array(v) => {
1285 report_array(out, id, type_name, name, v.as_ptr(), status)
1286 }
1287 ParamValue::Enum { index, .. } => {
1288 let _ = writeln!(
1289 out,
1290 "Parameter {id} type={type_name}, name={name}, value={index}, status={status}"
1291 );
1292 }
1293 ParamValue::Undefined | ParamValue::GenericPointer(_) => {
1297 let _ = writeln!(out, "Parameter {id} is undefined, name={name}");
1298 }
1299 }
1300 }
1301}
1302
1303fn report_array<T>(
1304 out: &mut dyn std::fmt::Write,
1305 id: usize,
1306 type_name: &str,
1307 name: &str,
1308 ptr: *const T,
1309 status: i32,
1310) {
1311 use std::fmt::Write as _;
1312 let _ = writeln!(
1313 out,
1314 "Parameter {id} type={type_name}, name={name}, value={ptr:p}, status={status}"
1315 );
1316}
1317
1318fn c_param_type_name(t: ParamType) -> &'static str {
1326 match t {
1327 ParamType::Int32 => "asynInt32",
1328 ParamType::Int64 => "asynInt64",
1329 ParamType::UInt64 => "asynUInt64",
1330 ParamType::Float64 => "asynFloat64",
1331 ParamType::Octet => "string",
1332 ParamType::UInt32Digital => "asynUInt32Digital",
1333 ParamType::Int8Array => "asynInt8Array",
1334 ParamType::Int16Array => "asynInt16Array",
1335 ParamType::Int32Array => "asynInt32Array",
1336 ParamType::Int64Array => "asynInt64Array",
1337 ParamType::UInt64Array => "asynUInt64Array",
1338 ParamType::Float32Array => "asynFloat32Array",
1339 ParamType::Float64Array => "asynFloat64Array",
1340 ParamType::Enum => "asynEnum",
1341 ParamType::GenericPointer => "asynGenericPointer",
1342 }
1343}
1344
1345fn format_g(v: f64) -> String {
1351 if v.is_nan() {
1352 return "nan".to_string();
1353 }
1354 if v.is_infinite() {
1355 return if v < 0.0 { "-inf" } else { "inf" }.to_string();
1356 }
1357 const PRECISION: i32 = 6;
1358 let sci = format!("{:.*e}", (PRECISION - 1) as usize, v);
1362 let (mantissa, exp) = sci.split_once('e').expect("Rust {:e} always emits one");
1363 let exp: i32 = exp.parse().expect("…followed by a decimal exponent");
1364 let strip = |s: &str| -> String {
1365 if s.contains('.') {
1366 s.trim_end_matches('0').trim_end_matches('.').to_string()
1367 } else {
1368 s.to_string()
1369 }
1370 };
1371 if exp < -4 || exp >= PRECISION {
1372 format!(
1374 "{}e{}{:02}",
1375 strip(mantissa),
1376 if exp < 0 { '-' } else { '+' },
1377 exp.abs()
1378 )
1379 } else {
1380 strip(&format!("{:.*}", (PRECISION - 1 - exp).max(0) as usize, v))
1381 }
1382}
1383
1384#[derive(Default, Debug, Clone)]
1417pub struct AsynParamSet {
1418 defs: Vec<(String, ParamType)>,
1419}
1420
1421impl AsynParamSet {
1422 pub fn new() -> Self {
1423 Self { defs: Vec::new() }
1424 }
1425
1426 pub fn add(&mut self, name: &str, ty: ParamType) -> usize {
1430 let slot = self.defs.len();
1431 self.defs.push((name.to_string(), ty));
1432 slot
1433 }
1434
1435 pub fn create_all(&self, params: &mut ParamList) -> AsynResult<Vec<usize>> {
1442 let mut indices = Vec::with_capacity(self.defs.len());
1443 for (name, ty) in &self.defs {
1444 indices.push(params.create_param(name, *ty)?);
1445 }
1446 Ok(indices)
1447 }
1448
1449 pub fn len(&self) -> usize {
1451 self.defs.len()
1452 }
1453
1454 pub fn is_empty(&self) -> bool {
1455 self.defs.is_empty()
1456 }
1457
1458 pub fn iter(&self) -> impl Iterator<Item = (&str, ParamType)> {
1460 self.defs.iter().map(|(n, t)| (n.as_str(), *t))
1461 }
1462}
1463
1464#[cfg(test)]
1465mod tests {
1466 use super::*;
1467
1468 #[test]
1473 fn format_g_matches_c_printf_g() {
1474 for (v, expect) in [
1475 (0.0f64, "0"),
1476 (7.0, "7"),
1477 (0.1 + 0.2, "0.3"),
1478 (1.5, "1.5"),
1479 (1.0 / 3.0, "0.333333"),
1481 (123456.0, "123456"),
1482 (1234567.0, "1.23457e+06"),
1484 (0.000123456789, "0.000123457"),
1486 (0.0000123456789, "1.23457e-05"),
1487 (-2.5e-9, "-2.5e-09"),
1488 (f64::INFINITY, "inf"),
1489 (f64::NAN, "nan"),
1490 ] {
1491 assert_eq!(format_g(v), expect, "%g of {v}");
1492 }
1493 }
1494
1495 #[test]
1496 fn test_create_and_find() {
1497 let mut pl = ParamList::new(1, false);
1498 let i0 = pl.create_param("TEMP", ParamType::Float64).unwrap();
1499 let i1 = pl.create_param("COUNT", ParamType::Int32).unwrap();
1500 assert_eq!(i0, 0);
1501 assert_eq!(i1, 1);
1502 assert_eq!(pl.find_param("TEMP"), Some(0));
1503 assert_eq!(pl.find_param("COUNT"), Some(1));
1504 assert_eq!(pl.find_param("NOPE"), None);
1505 assert_eq!(pl.create_param("TEMP", ParamType::Float64).unwrap(), 0);
1507 }
1508
1509 #[test]
1510 fn test_create_param_strict_duplicate_returns_already_exists() {
1511 let mut pl = ParamList::new(1, false);
1520 let idx = pl.create_param_strict("VAL", ParamType::Int32).unwrap();
1521 assert_eq!(idx, 0);
1522 match pl.create_param_strict("VAL", ParamType::Int32) {
1523 Err(AsynError::ParamAlreadyExists(name)) => assert_eq!(name, "VAL"),
1524 other => panic!("expected ParamAlreadyExists, got {other:?}"),
1525 }
1526 match pl.create_param_strict("VAL", ParamType::Int32) {
1530 Err(AsynError::ParamAlreadyExists(_)) => {}
1531 other => panic!("strict must observe lax-created names, got {other:?}"),
1532 }
1533 assert_eq!(pl.create_param("VAL", ParamType::Int32).unwrap(), 0);
1535 }
1536
1537 #[test]
1538 fn test_create_param_strict_distinct_names_succeed() {
1539 let mut pl = ParamList::new(1, false);
1540 let a = pl.create_param_strict("A", ParamType::Int32).unwrap();
1541 let b = pl.create_param_strict("B", ParamType::Float64).unwrap();
1542 assert_eq!(a, 0);
1543 assert_eq!(b, 1);
1544 assert_eq!(pl.find_param("A"), Some(0));
1545 assert_eq!(pl.find_param("B"), Some(1));
1546 }
1547
1548 #[test]
1549 fn test_uint32_set_get_clear_interrupt_masks() {
1550 let mut pl = ParamList::new(1, false);
1557 let idx = pl
1558 .create_param_strict("BITS", ParamType::UInt32Digital)
1559 .unwrap();
1560
1561 pl.set_uint32_interrupt(idx, 0, 0xF0, InterruptReason::ZeroToOne)
1562 .unwrap();
1563 assert_eq!(
1564 pl.get_uint32_interrupt(idx, 0, InterruptReason::ZeroToOne)
1565 .unwrap(),
1566 0xF0
1567 );
1568 assert_eq!(
1569 pl.get_uint32_interrupt(idx, 0, InterruptReason::OneToZero)
1570 .unwrap(),
1571 0x00
1572 );
1573
1574 pl.set_uint32_interrupt(idx, 0, 0x0F, InterruptReason::OneToZero)
1575 .unwrap();
1576 assert_eq!(
1577 pl.get_uint32_interrupt(idx, 0, InterruptReason::Both)
1578 .unwrap(),
1579 0xFF
1580 );
1581
1582 pl.clear_uint32_interrupt(idx, 0, 0x10).unwrap();
1584 assert_eq!(
1585 pl.get_uint32_interrupt(idx, 0, InterruptReason::ZeroToOne)
1586 .unwrap(),
1587 0xE0
1588 );
1589 assert_eq!(
1591 pl.get_uint32_interrupt(idx, 0, InterruptReason::OneToZero)
1592 .unwrap(),
1593 0x0F
1594 );
1595
1596 pl.set_uint32_interrupt(idx, 0, 0xAA, InterruptReason::Both)
1598 .unwrap();
1599 assert_eq!(
1600 pl.get_uint32_interrupt(idx, 0, InterruptReason::ZeroToOne)
1601 .unwrap(),
1602 0xAA
1603 );
1604 assert_eq!(
1605 pl.get_uint32_interrupt(idx, 0, InterruptReason::OneToZero)
1606 .unwrap(),
1607 0xAA
1608 );
1609 assert_eq!(
1610 pl.get_uint32_interrupt(idx, 0, InterruptReason::Both)
1611 .unwrap(),
1612 0xAA
1613 );
1614 }
1615
1616 #[test]
1617 fn test_uint32_interrupt_type_mismatch_rejects_non_uint32() {
1618 let mut pl = ParamList::new(1, false);
1622 let idx = pl.create_param("VAL", ParamType::Int32).unwrap();
1623 match pl.set_uint32_interrupt(idx, 0, 0xFF, InterruptReason::Both) {
1624 Err(AsynError::TypeMismatch { expected, .. }) => {
1625 assert_eq!(expected, "UInt32Digital")
1626 }
1627 other => panic!("expected TypeMismatch, got {other:?}"),
1628 }
1629 match pl.clear_uint32_interrupt(idx, 0, 0xFF) {
1630 Err(AsynError::TypeMismatch { .. }) => {}
1631 other => panic!("expected TypeMismatch, got {other:?}"),
1632 }
1633 match pl.get_uint32_interrupt(idx, 0, InterruptReason::Both) {
1634 Err(AsynError::TypeMismatch { .. }) => {}
1635 other => panic!("expected TypeMismatch, got {other:?}"),
1636 }
1637 }
1638
1639 #[test]
1640 fn test_get_set_int32() {
1641 let mut pl = ParamList::new(1, false);
1642 let idx = pl.create_param("VAL", ParamType::Int32).unwrap();
1643 assert_eq!(pl.get_int32(idx, 0).unwrap(), 0);
1644 pl.set_int32(idx, 0, 42).unwrap();
1645 assert_eq!(pl.get_int32(idx, 0).unwrap(), 42);
1646 }
1647
1648 #[test]
1649 fn test_get_set_float64() {
1650 let mut pl = ParamList::new(1, false);
1651 let idx = pl.create_param("TEMP", ParamType::Float64).unwrap();
1652 pl.set_float64(idx, 0, 3.14).unwrap();
1653 assert!((pl.get_float64(idx, 0).unwrap() - 3.14).abs() < 1e-10);
1654 }
1655
1656 #[test]
1657 fn test_get_set_string() {
1658 let mut pl = ParamList::new(1, false);
1659 let idx = pl.create_param("MSG", ParamType::Octet).unwrap();
1660 pl.set_string(idx, 0, "hello".into()).unwrap();
1661 assert_eq!(pl.get_string(idx, 0).unwrap(), "hello");
1662 }
1663
1664 #[test]
1665 fn test_get_set_uint32_mask() {
1666 let mut pl = ParamList::new(1, false);
1667 let idx = pl.create_param("BITS", ParamType::UInt32Digital).unwrap();
1668 pl.set_uint32(idx, 0, 0xFF, 0x0F, 0).unwrap();
1669 assert_eq!(pl.get_uint32(idx, 0).unwrap(), 0x0F);
1670 pl.set_uint32(idx, 0, 0xFF, 0xF0, 0).unwrap();
1671 assert_eq!(pl.get_uint32(idx, 0).unwrap(), 0xFF);
1672 }
1673
1674 #[test]
1675 fn test_multi_addr_isolation() {
1676 let mut pl = ParamList::new(3, true);
1677 let idx = pl.create_param("VAL", ParamType::Int32).unwrap();
1678 pl.set_int32(idx, 0, 10).unwrap();
1679 pl.set_int32(idx, 1, 20).unwrap();
1680 pl.set_int32(idx, 2, 30).unwrap();
1681 assert_eq!(pl.get_int32(idx, 0).unwrap(), 10);
1682 assert_eq!(pl.get_int32(idx, 1).unwrap(), 20);
1683 assert_eq!(pl.get_int32(idx, 2).unwrap(), 30);
1684 }
1685
1686 #[test]
1687 fn test_addr_out_of_range() {
1688 let pl = ParamList::new(2, true);
1689 assert!(pl.validate_addr(-1).is_err());
1690 assert!(pl.validate_addr(2).is_err());
1691 assert!(pl.validate_addr(0).is_ok());
1692 assert!(pl.validate_addr(1).is_ok());
1693 }
1694
1695 #[test]
1696 fn test_addr_normalize_single_device() {
1697 let mut pl = ParamList::new(1, false);
1698 let idx = pl.create_param("V", ParamType::Int32).unwrap();
1699 pl.set_int32(idx, 0, 99).unwrap();
1700 assert_eq!(pl.get_int32(idx, 5).unwrap(), 99);
1702 assert_eq!(pl.get_int32(idx, -1).unwrap(), 99);
1703 }
1704
1705 #[test]
1706 fn test_index_out_of_range() {
1707 let pl = ParamList::new(1, false);
1708 assert!(pl.get_int32(999, 0).is_err());
1709 }
1710
1711 #[test]
1712 fn test_type_mismatch() {
1713 let mut pl = ParamList::new(1, false);
1714 let idx = pl.create_param("VAL", ParamType::Int32).unwrap();
1715 assert!(pl.get_float64(idx, 0).is_err());
1716 assert!(pl.set_float64(idx, 0, 1.0).is_err());
1717 }
1718
1719 #[test]
1720 fn test_change_tracking() {
1721 let mut pl = ParamList::new(1, false);
1722 let i0 = pl.create_param("A", ParamType::Int32).unwrap();
1723 let i1 = pl.create_param("B", ParamType::Float64).unwrap();
1724
1725 pl.set_int32(i0, 0, 1).unwrap();
1726 pl.set_float64(i1, 0, 2.0).unwrap();
1727
1728 let changed = pl.take_changed(0).unwrap();
1729 assert_eq!(changed.len(), 2);
1730 assert_eq!(changed[0], 0);
1731 assert_eq!(changed[1], 1);
1732
1733 let changed2 = pl.take_changed(0).unwrap();
1735 assert!(changed2.is_empty());
1736 }
1737
1738 #[test]
1739 fn test_same_value_no_change() {
1740 let mut pl = ParamList::new(1, false);
1741 let idx = pl.create_param("V", ParamType::Int32).unwrap();
1742 pl.set_int32(idx, 0, 42).unwrap();
1743 let _ = pl.take_changed(0).unwrap(); pl.set_int32(idx, 0, 42).unwrap();
1747 let changed = pl.take_changed(0).unwrap();
1748 assert!(changed.is_empty());
1749 }
1750
1751 #[test]
1752 fn test_array_params() {
1753 let mut pl = ParamList::new(1, false);
1754 let idx = pl.create_param("WF", ParamType::Float64Array).unwrap();
1755 pl.set_float64_array(idx, 0, vec![1.0, 2.0, 3.0]).unwrap();
1756 let arr = pl.get_float64_array(idx, 0).unwrap();
1757 assert_eq!(&*arr, &[1.0, 2.0, 3.0]);
1758 }
1759
1760 #[test]
1761 fn test_param_status() {
1762 let mut pl = ParamList::new(1, false);
1763 let idx = pl.create_param("V", ParamType::Int32).unwrap();
1764 pl.set_param_status(idx, 0, AsynStatus::Timeout, 1, 2)
1765 .unwrap();
1766 let (st, as_, sev) = pl.get_param_status(idx, 0).unwrap();
1767 assert_eq!(st, AsynStatus::Timeout);
1768 assert_eq!(as_, 1);
1769 assert_eq!(sev, 2);
1770 }
1771
1772 #[test]
1773 fn test_param_name_and_type() {
1774 let mut pl = ParamList::new(1, false);
1775 pl.create_param("TEMP", ParamType::Float64).unwrap();
1776 assert_eq!(pl.param_name(0), Some("TEMP"));
1777 assert_eq!(pl.param_type(0), Some(ParamType::Float64));
1778 assert_eq!(pl.param_name(99), None);
1779 }
1780
1781 #[test]
1782 fn test_timestamp_none_by_default() {
1783 let mut pl = ParamList::new(1, false);
1784 pl.create_param("V", ParamType::Int32).unwrap();
1785 assert_eq!(pl.get_timestamp(0, 0).unwrap(), None);
1786 }
1787
1788 #[test]
1789 fn test_timestamp_set_get() {
1790 let mut pl = ParamList::new(1, false);
1791 pl.create_param("V", ParamType::Int32).unwrap();
1792 let ts = SystemTime::UNIX_EPOCH + std::time::Duration::from_secs(12345);
1793 pl.set_timestamp(0, 0, ts).unwrap();
1794 assert_eq!(pl.get_timestamp(0, 0).unwrap(), Some(ts));
1795 }
1796
1797 #[test]
1798 fn test_take_changed_returns_indices() {
1799 let mut pl = ParamList::new(1, false);
1800 pl.create_param("A", ParamType::Int32).unwrap();
1801 pl.create_param("B", ParamType::Float64).unwrap();
1802 pl.create_param("C", ParamType::Octet).unwrap();
1803
1804 pl.set_int32(0, 0, 1).unwrap();
1805 pl.set_string(2, 0, "x".into()).unwrap();
1806
1807 let changed = pl.take_changed(0).unwrap();
1808 assert_eq!(changed, vec![0, 2]);
1809 }
1810
1811 #[test]
1814 fn test_enum_default_sentinel() {
1815 let mut pl = ParamList::new(1, false);
1816 let idx = pl.create_param("MODE", ParamType::Enum).unwrap();
1817 let (index, choices) = pl.get_enum(idx, 0).unwrap();
1818 assert_eq!(index, 0);
1819 assert_eq!(choices.len(), 1);
1820 assert_eq!(choices[0].string, "");
1821 assert_eq!(choices[0].value, 0);
1822 assert_eq!(choices[0].severity, 0);
1823 }
1824
1825 #[test]
1826 fn test_enum_set_get_index() {
1827 let mut pl = ParamList::new(1, false);
1828 let idx = pl.create_param("MODE", ParamType::Enum).unwrap();
1829 let choices: Arc<[EnumEntry]> = Arc::from(vec![
1830 EnumEntry {
1831 string: "Off".into(),
1832 value: 0,
1833 severity: 0,
1834 },
1835 EnumEntry {
1836 string: "On".into(),
1837 value: 1,
1838 severity: 0,
1839 },
1840 ]);
1841 pl.set_enum_choices(idx, 0, choices).unwrap();
1842 pl.set_enum_index(idx, 0, 1).unwrap();
1843 let (index, _) = pl.get_enum(idx, 0).unwrap();
1844 assert_eq!(index, 1);
1845 }
1846
1847 #[test]
1848 fn test_enum_index_out_of_range() {
1849 let mut pl = ParamList::new(1, false);
1850 let idx = pl.create_param("MODE", ParamType::Enum).unwrap();
1851 assert!(pl.set_enum_index(idx, 0, 1).is_err());
1853 }
1854
1855 #[test]
1856 fn test_enum_type_mismatch() {
1857 let mut pl = ParamList::new(1, false);
1858 let idx = pl.create_param("VAL", ParamType::Int32).unwrap();
1859 assert!(pl.get_enum(idx, 0).is_err());
1860 }
1861
1862 #[test]
1863 fn test_enum_choices_update_resets_index() {
1864 let mut pl = ParamList::new(1, false);
1865 let idx = pl.create_param("MODE", ParamType::Enum).unwrap();
1866 let choices: Arc<[EnumEntry]> = Arc::from(vec![
1867 EnumEntry {
1868 string: "A".into(),
1869 value: 0,
1870 severity: 0,
1871 },
1872 EnumEntry {
1873 string: "B".into(),
1874 value: 1,
1875 severity: 0,
1876 },
1877 EnumEntry {
1878 string: "C".into(),
1879 value: 2,
1880 severity: 0,
1881 },
1882 ]);
1883 pl.set_enum_choices(idx, 0, choices).unwrap();
1884 pl.set_enum_index(idx, 0, 2).unwrap();
1885 let new_choices: Arc<[EnumEntry]> = Arc::from(vec![EnumEntry {
1887 string: "X".into(),
1888 value: 0,
1889 severity: 0,
1890 }]);
1891 pl.set_enum_choices(idx, 0, new_choices).unwrap();
1892 let (index, choices) = pl.get_enum(idx, 0).unwrap();
1893 assert_eq!(index, 0);
1894 assert_eq!(choices.len(), 1);
1895 }
1896
1897 #[test]
1898 fn test_enum_set_choices_marks_changed() {
1899 let mut pl = ParamList::new(1, false);
1900 let idx = pl.create_param("MODE", ParamType::Enum).unwrap();
1901 let _ = pl.take_changed(0).unwrap(); let choices: Arc<[EnumEntry]> = Arc::from(vec![EnumEntry {
1903 string: "A".into(),
1904 value: 0,
1905 severity: 0,
1906 }]);
1907 pl.set_enum_choices(idx, 0, choices).unwrap();
1908 let changed = pl.take_changed(0).unwrap();
1909 assert!(changed.contains(&idx));
1910 }
1911
1912 #[test]
1915 fn test_generic_pointer_default() {
1916 let mut pl = ParamList::new(1, false);
1917 let idx = pl.create_param("PTR", ParamType::GenericPointer).unwrap();
1918 let val = pl.get_generic_pointer(idx, 0).unwrap();
1919 assert!(val.downcast_ref::<()>().is_some());
1920 }
1921
1922 #[test]
1923 fn test_generic_pointer_set_get_downcast() {
1924 let mut pl = ParamList::new(1, false);
1925 let idx = pl.create_param("PTR", ParamType::GenericPointer).unwrap();
1926 let data: Arc<dyn Any + Send + Sync> = Arc::new(42i32);
1927 pl.set_generic_pointer(idx, 0, data).unwrap();
1928 let val = pl.get_generic_pointer(idx, 0).unwrap();
1929 assert_eq!(*val.downcast_ref::<i32>().unwrap(), 42);
1930 }
1931
1932 #[test]
1933 fn test_generic_pointer_downcast_wrong_type() {
1934 let mut pl = ParamList::new(1, false);
1935 let idx = pl.create_param("PTR", ParamType::GenericPointer).unwrap();
1936 let data: Arc<dyn Any + Send + Sync> = Arc::new(42i32);
1937 pl.set_generic_pointer(idx, 0, data).unwrap();
1938 let val = pl.get_generic_pointer(idx, 0).unwrap();
1939 assert!(val.downcast_ref::<String>().is_none());
1940 }
1941
1942 #[test]
1943 fn test_generic_pointer_type_mismatch() {
1944 let mut pl = ParamList::new(1, false);
1945 let idx = pl.create_param("VAL", ParamType::Int32).unwrap();
1946 assert!(pl.get_generic_pointer(idx, 0).is_err());
1947 }
1948
1949 #[test]
1950 fn test_generic_pointer_debug_shows_type_id() {
1951 let mut pl = ParamList::new(1, false);
1952 let idx = pl.create_param("PTR", ParamType::GenericPointer).unwrap();
1953 let data: Arc<dyn Any + Send + Sync> = Arc::new(vec![1, 2, 3]);
1954 pl.set_generic_pointer(idx, 0, data).unwrap();
1955 let val = pl.get_value(idx, 0).unwrap();
1956 let s = format!("{val:?}");
1957 assert!(s.contains("GenericPointer"));
1958 assert!(s.contains("TypeId"));
1959 }
1960
1961 #[test]
1964 fn test_get_set_int64() {
1965 let mut pl = ParamList::new(1, false);
1966 let idx = pl.create_param("BIG", ParamType::Int64).unwrap();
1967 assert_eq!(pl.get_int64(idx, 0).unwrap(), 0);
1968 pl.set_int64(idx, 0, i64::MAX).unwrap();
1969 assert_eq!(pl.get_int64(idx, 0).unwrap(), i64::MAX);
1970 }
1971
1972 #[test]
1973 fn test_int64_type_mismatch() {
1974 let mut pl = ParamList::new(1, false);
1975 let idx = pl.create_param("V", ParamType::Int32).unwrap();
1976 assert!(pl.get_int64(idx, 0).is_err());
1977 assert!(pl.set_int64(idx, 0, 1).is_err());
1978 }
1979
1980 #[test]
1981 fn test_int64_same_value_no_change() {
1982 let mut pl = ParamList::new(1, false);
1983 let idx = pl.create_param("V", ParamType::Int64).unwrap();
1984 pl.set_int64(idx, 0, 42).unwrap();
1985 let _ = pl.take_changed(0).unwrap();
1986 pl.set_int64(idx, 0, 42).unwrap();
1987 let changed = pl.take_changed(0).unwrap();
1988 assert!(changed.is_empty());
1989 }
1990
1991 #[test]
1992 fn test_int64_change_tracking() {
1993 let mut pl = ParamList::new(1, false);
1994 let idx = pl.create_param("V", ParamType::Int64).unwrap();
1995 pl.set_int64(idx, 0, 100).unwrap();
1996 let changed = pl.take_changed(0).unwrap();
1997 assert_eq!(changed, vec![idx]);
1998 }
1999
2000 #[test]
2003 fn asyn_param_set_add_returns_slot_index_in_order() {
2004 let mut set = AsynParamSet::new();
2005 assert_eq!(set.add("Temperature", ParamType::Float64), 0);
2010 assert_eq!(set.add("Status", ParamType::Int32), 1);
2011 assert_eq!(set.add("Tag", ParamType::Octet), 2);
2012 assert_eq!(set.len(), 3);
2013 }
2014
2015 #[test]
2016 fn asyn_param_set_create_all_assigns_indices_in_order() {
2017 let mut set = AsynParamSet::new();
2018 let temp_slot = set.add("Temperature", ParamType::Float64);
2019 let status_slot = set.add("Status", ParamType::Int32);
2020 let tag_slot = set.add("Tag", ParamType::Octet);
2021
2022 let mut pl = ParamList::new(1, false);
2023 let indices = set.create_all(&mut pl).unwrap();
2024 assert_eq!(indices.len(), 3);
2025 assert_eq!(indices[temp_slot], 0);
2026 assert_eq!(indices[status_slot], 1);
2027 assert_eq!(indices[tag_slot], 2);
2028 assert_eq!(pl.find_param("Temperature"), Some(0));
2030 assert_eq!(pl.find_param("Status"), Some(1));
2031 assert_eq!(pl.find_param("Tag"), Some(2));
2032 }
2033
2034 #[test]
2035 fn asyn_param_set_iter_preserves_registration_order() {
2036 let mut set = AsynParamSet::new();
2037 set.add("A", ParamType::Int32);
2038 set.add("B", ParamType::Float64);
2039 set.add("C", ParamType::Octet);
2040 let names: Vec<&str> = set.iter().map(|(n, _)| n).collect();
2041 assert_eq!(names, vec!["A", "B", "C"]);
2042 }
2043
2044 #[test]
2045 fn asyn_param_set_empty_create_all_is_noop() {
2046 let set = AsynParamSet::new();
2047 let mut pl = ParamList::new(1, false);
2048 let idx = set.create_all(&mut pl).unwrap();
2049 assert!(idx.is_empty());
2050 assert!(pl.is_empty());
2051 }
2052
2053 #[test]
2054 fn asyn_param_set_duplicate_name_returns_existing_index() {
2055 let mut set = AsynParamSet::new();
2060 set.add("X", ParamType::Int32);
2061 set.add("X", ParamType::Int32);
2062 let mut pl = ParamList::new(1, false);
2063 let idx = set.create_all(&mut pl).unwrap();
2064 assert_eq!(idx, vec![0, 0]);
2065 }
2066
2067 #[test]
2076 fn get_int32_strict_undefined_returns_param_undefined() {
2077 let mut pl = ParamList::new(1, false);
2078 let idx = pl.create_param("U", ParamType::Int32).unwrap();
2079 assert_eq!(pl.get_int32(idx, 0).unwrap(), 0);
2081 let err = pl.get_int32_strict(idx, 0).unwrap_err();
2083 assert!(matches!(err, AsynError::ParamUndefined(i) if i == idx));
2084 pl.set_int32(idx, 0, 7).unwrap();
2085 assert_eq!(pl.get_int32_strict(idx, 0).unwrap(), 7);
2086 }
2087
2088 #[test]
2089 fn get_float64_strict_undefined_returns_param_undefined() {
2090 let mut pl = ParamList::new(1, false);
2091 let idx = pl.create_param("U", ParamType::Float64).unwrap();
2092 assert_eq!(pl.get_float64(idx, 0).unwrap(), 0.0);
2093 assert!(matches!(
2094 pl.get_float64_strict(idx, 0).unwrap_err(),
2095 AsynError::ParamUndefined(i) if i == idx
2096 ));
2097 }
2098
2099 #[test]
2100 fn get_int64_strict_undefined_returns_param_undefined() {
2101 let mut pl = ParamList::new(1, false);
2102 let idx = pl.create_param("U", ParamType::Int64).unwrap();
2103 assert!(matches!(
2104 pl.get_int64_strict(idx, 0).unwrap_err(),
2105 AsynError::ParamUndefined(i) if i == idx
2106 ));
2107 }
2108
2109 #[test]
2110 fn get_uint32_strict_undefined_returns_param_undefined() {
2111 let mut pl = ParamList::new(1, false);
2112 let idx = pl.create_param("U", ParamType::UInt32Digital).unwrap();
2113 assert!(matches!(
2114 pl.get_uint32_strict(idx, 0).unwrap_err(),
2115 AsynError::ParamUndefined(i) if i == idx
2116 ));
2117 }
2118
2119 #[test]
2120 fn get_string_strict_undefined_returns_param_undefined() {
2121 let mut pl = ParamList::new(1, false);
2122 let idx = pl.create_param("U", ParamType::Octet).unwrap();
2123 assert!(matches!(
2124 pl.get_string_strict(idx, 0).unwrap_err(),
2125 AsynError::ParamUndefined(i) if i == idx
2126 ));
2127 }
2128
2129 #[test]
2130 fn get_strict_checks_type_before_undefined_c_parity() {
2131 let mut pl = ParamList::new(1, false);
2136 let idx = pl.create_param("F", ParamType::Float64).unwrap();
2137 assert!(matches!(
2138 pl.get_int32_strict(idx, 0).unwrap_err(),
2139 AsynError::TypeMismatch {
2140 expected: "Int32",
2141 ..
2142 }
2143 ));
2144 }
2145
2146 #[test]
2147 fn set_int32_first_write_with_default_value_flips_defined() {
2148 let mut pl = ParamList::new(1, false);
2151 let idx = pl.create_param("V", ParamType::Int32).unwrap();
2152 assert!(!pl.is_param_defined(idx, 0).unwrap());
2153 pl.set_int32(idx, 0, 0).unwrap();
2154 assert!(pl.is_param_defined(idx, 0).unwrap());
2155 assert!(pl.take_changed_single(idx, 0).unwrap());
2156 assert_eq!(pl.get_int32_strict(idx, 0).unwrap(), 0);
2157 }
2158
2159 #[test]
2160 fn set_float64_first_write_with_default_value_flips_defined() {
2161 let mut pl = ParamList::new(1, false);
2162 let idx = pl.create_param("V", ParamType::Float64).unwrap();
2163 pl.set_float64(idx, 0, 0.0).unwrap();
2164 assert!(pl.is_param_defined(idx, 0).unwrap());
2165 assert_eq!(pl.get_float64_strict(idx, 0).unwrap(), 0.0);
2166 }
2167
2168 #[test]
2169 fn set_uint32_first_write_zero_mask_zero_flips_defined() {
2170 let mut pl = ParamList::new(1, false);
2175 let idx = pl.create_param("V", ParamType::UInt32Digital).unwrap();
2176 pl.set_uint32(idx, 0, 0, 0xFFFF, 0).unwrap();
2177 assert!(pl.is_param_defined(idx, 0).unwrap());
2178 assert!(pl.take_changed_single(idx, 0).unwrap());
2179 assert_eq!(pl.get_uint32_strict(idx, 0).unwrap(), 0);
2180 }
2181
2182 #[test]
2183 fn set_string_first_write_empty_flips_defined() {
2184 let mut pl = ParamList::new(1, false);
2185 let idx = pl.create_param("V", ParamType::Octet).unwrap();
2186 pl.set_string(idx, 0, String::new()).unwrap();
2187 assert!(pl.is_param_defined(idx, 0).unwrap());
2188 assert_eq!(pl.get_string_strict(idx, 0).unwrap(), "");
2189 }
2190
2191 #[test]
2192 fn set_enum_index_first_write_to_default_index_flips_defined() {
2193 let mut pl = ParamList::new(1, false);
2200 let idx = pl.create_param("MODE", ParamType::Enum).unwrap();
2201 assert!(!pl.is_param_defined(idx, 0).unwrap());
2202 pl.set_enum_index(idx, 0, 0).unwrap();
2203 assert!(pl.is_param_defined(idx, 0).unwrap());
2204 assert!(pl.take_changed_single(idx, 0).unwrap());
2205 }
2206
2207 #[test]
2208 fn set_param_status_only_change_marks_value_changed() {
2209 let mut pl = ParamList::new(1, false);
2214 let idx = pl.create_param("S", ParamType::Int32).unwrap();
2215 let _ = pl.take_changed(0).unwrap();
2216 pl.set_param_status(idx, 0, AsynStatus::Timeout, 0, 0)
2217 .unwrap();
2218 let changed = pl.take_changed(0).unwrap();
2219 assert!(
2220 changed.contains(&idx),
2221 "a status-only transition must mark the param value_changed"
2222 );
2223 }
2224
2225 #[test]
2226 fn set_param_alarm_only_change_marks_value_changed() {
2227 let mut pl = ParamList::new(1, false);
2230 let idx = pl.create_param("S", ParamType::Float64).unwrap();
2231 let _ = pl.take_changed(0).unwrap();
2232 pl.set_param_status(idx, 0, AsynStatus::Success, 7, 2)
2233 .unwrap();
2234 assert!(
2235 pl.take_changed_single(idx, 0).unwrap(),
2236 "an alarm-status/severity-only transition must mark the param value_changed"
2237 );
2238 }
2239
2240 #[test]
2241 fn set_param_status_change_forces_full_uint32_mask() {
2242 let mut pl = ParamList::new(1, false);
2245 let idx = pl.create_param("U", ParamType::UInt32Digital).unwrap();
2246 let _ = pl.take_changed(0).unwrap();
2247 pl.set_param_status(idx, 0, AsynStatus::Error, 0, 0)
2248 .unwrap();
2249 assert!(pl.take_changed_single(idx, 0).unwrap());
2250 assert_eq!(
2251 pl.get_uint32_interrupt_mask(idx, 0).unwrap(),
2252 0xFFFF_FFFF,
2253 "a UInt32Digital status change must force the full callback mask"
2254 );
2255 }
2256
2257 #[test]
2258 fn set_uint32_force_interrupt_mask_on_unchanged_value_notifies() {
2259 let mut pl = ParamList::new(1, false);
2266 let idx = pl.create_param("U", ParamType::UInt32Digital).unwrap();
2267 pl.set_uint32(idx, 0, 0x05, 0x0F, 0).unwrap();
2269 let _ = pl.take_changed(0).unwrap();
2270 let _ = pl.take_uint32_interrupt_mask(idx, 0).unwrap();
2271 pl.set_uint32(idx, 0, 0x05, 0x0F, 0x02).unwrap();
2273 assert_eq!(
2274 pl.get_uint32(idx, 0).unwrap(),
2275 0x05,
2276 "a forced-interrupt-only set must not change the stored value"
2277 );
2278 assert!(
2279 pl.take_changed(0).unwrap().contains(&idx),
2280 "a forced interruptMask must mark value_changed even on an unchanged value"
2281 );
2282 assert_eq!(
2283 pl.take_uint32_interrupt_mask(idx, 0).unwrap(),
2284 0x02,
2285 "the forced interruptMask bits must land in the callback mask"
2286 );
2287 }
2288
2289 #[test]
2290 fn set_uint32_accumulates_callback_mask_across_sets() {
2291 let mut pl = ParamList::new(1, false);
2295 let idx = pl.create_param("U", ParamType::UInt32Digital).unwrap();
2296 pl.set_uint32(idx, 0, 0x01, 0x01, 0).unwrap(); pl.set_uint32(idx, 0, 0x02, 0x02, 0).unwrap(); assert_eq!(
2299 pl.get_uint32_interrupt_mask(idx, 0).unwrap(),
2300 0x03,
2301 "callback mask must accumulate the union of changed bits since the last flush"
2302 );
2303 }
2304
2305 #[test]
2306 fn take_uint32_interrupt_mask_reads_and_resets() {
2307 let mut pl = ParamList::new(1, false);
2310 let idx = pl.create_param("U", ParamType::UInt32Digital).unwrap();
2311 pl.set_uint32(idx, 0, 0x05, 0x0F, 0).unwrap();
2312 assert_eq!(pl.take_uint32_interrupt_mask(idx, 0).unwrap(), 0x05);
2313 assert_eq!(
2314 pl.get_uint32_interrupt_mask(idx, 0).unwrap(),
2315 0,
2316 "callback mask must be reset to 0 after take"
2317 );
2318 }
2319
2320 #[test]
2321 fn set_param_status_no_change_does_not_mark_value_changed() {
2322 let mut pl = ParamList::new(1, false);
2326 let idx = pl.create_param("S", ParamType::Int32).unwrap();
2327 let _ = pl.take_changed(0).unwrap();
2328 pl.set_param_status(idx, 0, AsynStatus::Success, 0, 0)
2329 .unwrap();
2330 assert!(
2331 !pl.take_changed_single(idx, 0).unwrap(),
2332 "re-asserting the same status/alarm must not mark the param value_changed"
2333 );
2334 }
2335}