1use std::collections::VecDeque;
2use std::sync::Arc;
3
4use ad_core_rs::ndarray::NDArray;
5use ad_core_rs::ndarray_pool::NDArrayPool;
6use ad_core_rs::plugin::runtime::{NDPluginProcess, ProcessResult};
7use epics_base_rs::calc;
8
9#[derive(Debug, Clone)]
18pub struct CalcExpression {
19 compiled: calc::CompiledExpr,
20}
21
22impl CalcExpression {
23 pub fn parse(expr: &str) -> Option<CalcExpression> {
27 calc::compile(expr)
28 .ok()
29 .map(|compiled| CalcExpression { compiled })
30 }
31
32 pub fn evaluate(&self, a: f64, b: f64) -> f64 {
35 let mut inputs = calc::NumericInputs::new();
36 inputs.vars[0] = a; inputs.vars[1] = b; calc::eval(&self.compiled, &mut inputs).unwrap_or(0.0)
39 }
40
41 pub fn evaluate_vars(&self, vars: &[f64; calc::CALC_NARGS]) -> f64 {
45 let mut inputs = calc::NumericInputs::with_vars(*vars);
46 calc::eval(&self.compiled, &mut inputs).unwrap_or(0.0)
47 }
48}
49
50#[derive(Debug, Clone)]
52pub enum TriggerCondition {
53 AttributeThreshold { name: String, threshold: f64 },
55 External,
57 Calc {
62 attr_a: String,
63 attr_b: String,
64 expression: CalcExpression,
65 },
66}
67
68#[derive(Debug, Clone, Copy, PartialEq, Eq)]
70pub enum BufferStatus {
71 Idle,
72 BufferFilling,
73 Flushing,
74 AcquisitionCompleted,
75}
76
77#[derive(Debug, Default)]
80pub struct PushResult {
81 pub forward: Vec<Arc<NDArray>>,
83 pub sequence_done: bool,
85}
86
87pub struct CircularBuffer {
89 pub(crate) pre_count: usize,
90 pub(crate) post_count: usize,
91 buffer: VecDeque<Arc<NDArray>>,
92 pub(crate) trigger_condition: TriggerCondition,
93 triggered: bool,
94 post_done: usize,
96 pre_flushed: bool,
98 captured: Vec<Arc<NDArray>>,
101 preset_trigger_count: usize,
103 trigger_count: usize,
105 flush_on_soft_trigger: bool,
107 pub(crate) status: BufferStatus,
109}
110
111impl CircularBuffer {
112 pub fn new(pre_count: usize, post_count: usize, condition: TriggerCondition) -> Self {
113 Self {
114 pre_count,
115 post_count,
116 buffer: VecDeque::with_capacity(pre_count + 1),
117 trigger_condition: condition,
118 triggered: false,
119 post_done: 0,
120 pre_flushed: false,
121 captured: Vec::new(),
122 preset_trigger_count: 0,
123 trigger_count: 0,
124 flush_on_soft_trigger: false,
125 status: BufferStatus::Idle,
126 }
127 }
128
129 pub fn set_preset_trigger_count(&mut self, count: usize) {
131 self.preset_trigger_count = count;
132 }
133
134 pub fn trigger_count(&self) -> usize {
136 self.trigger_count
137 }
138
139 pub fn status(&self) -> BufferStatus {
141 self.status
142 }
143
144 pub fn set_flush_on_soft_trigger(&mut self, flush: bool) {
146 self.flush_on_soft_trigger = flush;
147 }
148
149 pub fn push(&mut self, array: Arc<NDArray>) -> PushResult {
157 let mut result = PushResult::default();
158
159 if self.status == BufferStatus::AcquisitionCompleted {
161 return result;
162 }
163
164 if self.status == BufferStatus::Idle {
166 self.status = BufferStatus::BufferFilling;
167 }
168
169 if self.triggered {
170 if !self.pre_flushed {
173 self.pre_flushed = true;
174 let pre: Vec<_> = self.buffer.drain(..).collect();
175 self.captured.extend(pre.iter().cloned());
176 result.forward.extend(pre);
177 }
178 self.captured.push(Arc::clone(&array));
181 result.forward.push(array);
182 self.post_done += 1;
183 if self.post_done >= self.post_count {
184 self.complete_sequence(&mut result);
185 }
186 return result;
187 }
188
189 let trigger = match &self.trigger_condition {
192 TriggerCondition::AttributeThreshold { name, threshold } => array
193 .attributes
194 .get(name)
195 .and_then(|a| a.value.as_f64())
196 .map(|v| v >= *threshold)
197 .unwrap_or(false),
198 TriggerCondition::External => false,
199 TriggerCondition::Calc {
200 attr_a,
201 attr_b,
202 expression,
203 } => {
204 let a = array
205 .attributes
206 .get(attr_a)
207 .and_then(|a| a.value.as_f64())
208 .unwrap_or(f64::NAN);
209 let b = array
210 .attributes
211 .get(attr_b)
212 .and_then(|a| a.value.as_f64())
213 .unwrap_or(f64::NAN);
214 let mut vars = [0.0f64; calc::CALC_NARGS];
217 vars[0] = a; vars[1] = b; vars[2] = self.pre_count as f64; vars[3] = self.post_count as f64; vars[4] = self.buffer.len() as f64; vars[5] = if self.triggered { 1.0 } else { 0.0 }; expression.evaluate_vars(&vars) != 0.0
224 }
225 };
226
227 if trigger {
228 self.trigger();
231 self.pre_flushed = true;
235 let pre: Vec<_> = self.buffer.drain(..).collect();
236 self.captured.extend(pre.iter().cloned());
237 result.forward.extend(pre);
238 self.captured.push(Arc::clone(&array));
239 result.forward.push(array);
240 self.post_done += 1;
241 if self.post_done >= self.post_count {
242 self.complete_sequence(&mut result);
243 }
244 return result;
245 }
246
247 self.buffer.push_back(array);
249 if self.buffer.len() > self.pre_count {
250 self.buffer.pop_front();
251 }
252
253 result
254 }
255
256 fn complete_sequence(&mut self, result: &mut PushResult) {
260 self.triggered = false;
261 self.pre_flushed = false;
262 self.post_done = 0;
263 if self.preset_trigger_count > 0 && self.trigger_count >= self.preset_trigger_count {
264 self.status = BufferStatus::AcquisitionCompleted;
265 } else {
266 self.status = BufferStatus::BufferFilling;
267 }
268 result.sequence_done = true;
269 }
270
271 pub fn trigger(&mut self) {
273 if self.status == BufferStatus::AcquisitionCompleted {
275 return;
276 }
277
278 self.triggered = true;
279 self.post_done = 0;
280 self.pre_flushed = false;
281 self.trigger_count += 1;
282 self.status = BufferStatus::Flushing;
283 self.captured.clear();
286 }
287
288 pub fn take_captured(&mut self) -> Vec<Arc<NDArray>> {
290 std::mem::take(&mut self.captured)
291 }
292
293 pub fn is_triggered(&self) -> bool {
294 self.triggered
295 }
296
297 pub fn pre_buffer_len(&self) -> usize {
298 self.buffer.len()
299 }
300
301 pub fn reset(&mut self) {
302 self.buffer.clear();
303 self.captured.clear();
304 self.triggered = false;
305 self.post_done = 0;
306 self.pre_flushed = false;
307 self.trigger_count = 0;
308 self.status = BufferStatus::Idle;
309 }
310}
311
312#[derive(Default)]
316struct CBParamIndices {
317 control: Option<usize>,
318 status: Option<usize>,
319 trigger_a: Option<usize>,
320 trigger_b: Option<usize>,
321 trigger_a_val: Option<usize>,
322 trigger_b_val: Option<usize>,
323 trigger_calc: Option<usize>,
324 trigger_calc_val: Option<usize>,
325 pre_trigger: Option<usize>,
326 post_trigger: Option<usize>,
327 current_image: Option<usize>,
328 post_count: Option<usize>,
329 soft_trigger: Option<usize>,
330 triggered: Option<usize>,
331 preset_trigger_count: Option<usize>,
332 actual_trigger_count: Option<usize>,
333 flush_on_soft_trigger: Option<usize>,
334}
335
336pub struct CircularBuffProcessor {
337 buffer: CircularBuffer,
338 params: CBParamIndices,
339 trigger_a_name: String,
341 trigger_b_name: String,
342 trigger_calc_expr: String,
343}
344
345impl CircularBuffProcessor {
346 pub fn new(pre_count: usize, post_count: usize, condition: TriggerCondition) -> Self {
347 Self {
348 buffer: CircularBuffer::new(pre_count, post_count, condition),
349 params: CBParamIndices::default(),
350 trigger_a_name: String::new(),
351 trigger_b_name: String::new(),
352 trigger_calc_expr: String::new(),
353 }
354 }
355
356 pub fn trigger(&mut self) {
357 self.buffer.trigger();
358 }
359
360 pub fn buffer(&self) -> &CircularBuffer {
361 &self.buffer
362 }
363
364 fn rebuild_trigger_condition(&mut self) {
366 if !self.trigger_calc_expr.is_empty() {
367 if let Some(expr) = CalcExpression::parse(&self.trigger_calc_expr) {
368 self.buffer.trigger_condition = TriggerCondition::Calc {
369 attr_a: self.trigger_a_name.clone(),
370 attr_b: self.trigger_b_name.clone(),
371 expression: expr,
372 };
373 return;
374 }
375 }
376 if !self.trigger_a_name.is_empty() {
377 self.buffer.trigger_condition = TriggerCondition::AttributeThreshold {
378 name: self.trigger_a_name.clone(),
379 threshold: 0.5,
380 };
381 } else {
382 self.buffer.trigger_condition = TriggerCondition::External;
383 }
384 }
385}
386
387impl NDPluginProcess for CircularBuffProcessor {
388 fn process_array(&mut self, array: &NDArray, _pool: &NDArrayPool) -> ProcessResult {
389 use ad_core_rs::plugin::runtime::ParamUpdate;
390
391 let push_result = self.buffer.push(Arc::new(array.clone()));
392
393 let mut updates = Vec::new();
394 if let Some(idx) = self.params.status {
395 let status_val = match self.buffer.status() {
396 BufferStatus::Idle => 0,
397 BufferStatus::BufferFilling => 1,
398 BufferStatus::Flushing => 2,
399 BufferStatus::AcquisitionCompleted => 3,
400 };
401 updates.push(ParamUpdate::int32(idx, status_val));
402 }
403 if let Some(idx) = self.params.current_image {
404 updates.push(ParamUpdate::int32(idx, self.buffer.pre_buffer_len() as i32));
405 }
406 if let Some(idx) = self.params.triggered {
407 updates.push(ParamUpdate::int32(
408 idx,
409 if self.buffer.is_triggered() { 1 } else { 0 },
410 ));
411 }
412 if let Some(idx) = self.params.actual_trigger_count {
413 updates.push(ParamUpdate::int32(idx, self.buffer.trigger_count() as i32));
414 }
415
416 if push_result.forward.is_empty() {
420 ProcessResult::sink(updates)
421 } else {
422 let mut result = ProcessResult::arrays(push_result.forward);
423 result.param_updates = updates;
424 result
425 }
426 }
427
428 fn plugin_type(&self) -> &str {
429 "NDPluginCircularBuff"
430 }
431
432 fn register_params(
433 &mut self,
434 base: &mut asyn_rs::port::PortDriverBase,
435 ) -> asyn_rs::error::AsynResult<()> {
436 use asyn_rs::param::ParamType;
437 base.create_param("CIRC_BUFF_CONTROL", ParamType::Int32)?;
438 base.create_param("CIRC_BUFF_STATUS", ParamType::Int32)?;
439 base.create_param("CIRC_BUFF_TRIGGER_A", ParamType::Octet)?;
440 base.create_param("CIRC_BUFF_TRIGGER_B", ParamType::Octet)?;
441 base.create_param("CIRC_BUFF_TRIGGER_A_VAL", ParamType::Float64)?;
442 base.create_param("CIRC_BUFF_TRIGGER_B_VAL", ParamType::Float64)?;
443 base.create_param("CIRC_BUFF_TRIGGER_CALC", ParamType::Octet)?;
444 base.create_param("CIRC_BUFF_TRIGGER_CALC_VAL", ParamType::Float64)?;
445 base.create_param("CIRC_BUFF_PRE_TRIGGER", ParamType::Int32)?;
446 base.create_param("CIRC_BUFF_POST_TRIGGER", ParamType::Int32)?;
447 base.create_param("CIRC_BUFF_CURRENT_IMAGE", ParamType::Int32)?;
448 base.create_param("CIRC_BUFF_POST_COUNT", ParamType::Int32)?;
449 base.create_param("CIRC_BUFF_SOFT_TRIGGER", ParamType::Int32)?;
450 base.create_param("CIRC_BUFF_TRIGGERED", ParamType::Int32)?;
451 base.create_param("CIRC_BUFF_PRESET_TRIGGER_COUNT", ParamType::Int32)?;
452 base.create_param("CIRC_BUFF_ACTUAL_TRIGGER_COUNT", ParamType::Int32)?;
453 base.create_param("CIRC_BUFF_FLUSH_ON_SOFTTRIGGER", ParamType::Int32)?;
454
455 self.params.control = base.find_param("CIRC_BUFF_CONTROL");
456 self.params.status = base.find_param("CIRC_BUFF_STATUS");
457 self.params.trigger_a = base.find_param("CIRC_BUFF_TRIGGER_A");
458 self.params.trigger_b = base.find_param("CIRC_BUFF_TRIGGER_B");
459 self.params.trigger_a_val = base.find_param("CIRC_BUFF_TRIGGER_A_VAL");
460 self.params.trigger_b_val = base.find_param("CIRC_BUFF_TRIGGER_B_VAL");
461 self.params.trigger_calc = base.find_param("CIRC_BUFF_TRIGGER_CALC");
462 self.params.trigger_calc_val = base.find_param("CIRC_BUFF_TRIGGER_CALC_VAL");
463 self.params.pre_trigger = base.find_param("CIRC_BUFF_PRE_TRIGGER");
464 self.params.post_trigger = base.find_param("CIRC_BUFF_POST_TRIGGER");
465 self.params.current_image = base.find_param("CIRC_BUFF_CURRENT_IMAGE");
466 self.params.post_count = base.find_param("CIRC_BUFF_POST_COUNT");
467 self.params.soft_trigger = base.find_param("CIRC_BUFF_SOFT_TRIGGER");
468 self.params.triggered = base.find_param("CIRC_BUFF_TRIGGERED");
469 self.params.preset_trigger_count = base.find_param("CIRC_BUFF_PRESET_TRIGGER_COUNT");
470 self.params.actual_trigger_count = base.find_param("CIRC_BUFF_ACTUAL_TRIGGER_COUNT");
471 self.params.flush_on_soft_trigger = base.find_param("CIRC_BUFF_FLUSH_ON_SOFTTRIGGER");
472 Ok(())
473 }
474
475 fn on_param_change(
476 &mut self,
477 reason: usize,
478 params: &ad_core_rs::plugin::runtime::PluginParamSnapshot,
479 ) -> ad_core_rs::plugin::runtime::ParamChangeResult {
480 use ad_core_rs::plugin::runtime::{ParamChangeResult, ParamChangeValue};
481
482 if Some(reason) == self.params.control {
483 let v = params.value.as_i32();
484 if v == 1 {
485 self.buffer.reset();
487 self.buffer.status = BufferStatus::BufferFilling;
488 } else {
489 self.buffer.status = BufferStatus::Idle;
491 }
492 } else if Some(reason) == self.params.pre_trigger {
493 self.buffer.pre_count = params.value.as_i32().max(0) as usize;
494 } else if Some(reason) == self.params.post_trigger {
495 self.buffer.post_count = params.value.as_i32().max(0) as usize;
496 } else if Some(reason) == self.params.preset_trigger_count {
497 self.buffer
498 .set_preset_trigger_count(params.value.as_i32().max(0) as usize);
499 } else if Some(reason) == self.params.flush_on_soft_trigger {
500 self.buffer
501 .set_flush_on_soft_trigger(params.value.as_i32() != 0);
502 } else if Some(reason) == self.params.soft_trigger {
503 if params.value.as_i32() != 0 {
504 self.buffer.trigger();
505 }
506 } else if Some(reason) == self.params.trigger_a {
507 if let ParamChangeValue::Octet(s) = ¶ms.value {
508 self.trigger_a_name = s.clone();
509 self.rebuild_trigger_condition();
510 }
511 } else if Some(reason) == self.params.trigger_b {
512 if let ParamChangeValue::Octet(s) = ¶ms.value {
513 self.trigger_b_name = s.clone();
514 self.rebuild_trigger_condition();
515 }
516 } else if Some(reason) == self.params.trigger_calc {
517 if let ParamChangeValue::Octet(s) = ¶ms.value {
518 self.trigger_calc_expr = s.clone();
519 self.rebuild_trigger_condition();
520 }
521 }
522
523 ParamChangeResult::updates(vec![])
524 }
525}
526
527#[cfg(test)]
528mod tests {
529 use super::*;
530 use ad_core_rs::attributes::{NDAttrSource, NDAttrValue, NDAttribute};
531 use ad_core_rs::ndarray::{NDDataType, NDDimension};
532
533 fn make_array(id: i32) -> Arc<NDArray> {
534 let mut arr = NDArray::new(vec![NDDimension::new(4)], NDDataType::UInt8);
535 arr.unique_id = id;
536 Arc::new(arr)
537 }
538
539 fn make_array_with_attr(id: i32, attr_val: f64) -> Arc<NDArray> {
540 let mut arr = NDArray::new(vec![NDDimension::new(4)], NDDataType::UInt8);
541 arr.unique_id = id;
542 arr.attributes.add(NDAttribute::new_static(
543 "trigger",
544 "",
545 NDAttrSource::Driver,
546 NDAttrValue::Float64(attr_val),
547 ));
548 Arc::new(arr)
549 }
550
551 fn make_array_with_attrs(id: i32, a_val: f64, b_val: f64) -> Arc<NDArray> {
552 let mut arr = NDArray::new(vec![NDDimension::new(4)], NDDataType::UInt8);
553 arr.unique_id = id;
554 arr.attributes.add(NDAttribute::new_static(
555 "attr_a",
556 "",
557 NDAttrSource::Driver,
558 NDAttrValue::Float64(a_val),
559 ));
560 arr.attributes.add(NDAttribute::new_static(
561 "attr_b",
562 "",
563 NDAttrSource::Driver,
564 NDAttrValue::Float64(b_val),
565 ));
566 Arc::new(arr)
567 }
568
569 #[test]
570 fn test_pre_trigger_buffering() {
571 let mut cb = CircularBuffer::new(3, 2, TriggerCondition::External);
572
573 for i in 0..5 {
574 cb.push(make_array(i));
575 }
576 assert_eq!(cb.pre_buffer_len(), 3);
578 }
579
580 #[test]
581 fn test_external_trigger() {
582 let mut cb = CircularBuffer::new(2, 2, TriggerCondition::External);
583
584 cb.push(make_array(1));
585 cb.push(make_array(2));
586 cb.push(make_array(3));
587 cb.trigger();
590 assert!(cb.is_triggered());
591
592 let r1 = cb.push(make_array(4));
594 assert!(!r1.sequence_done);
595 let ids1: Vec<_> = r1.forward.iter().map(|a| a.unique_id).collect();
596 assert_eq!(ids1, vec![2, 3, 4]); let r2 = cb.push(make_array(5));
600 assert!(r2.sequence_done);
601 let ids2: Vec<_> = r2.forward.iter().map(|a| a.unique_id).collect();
602 assert_eq!(ids2, vec![5]);
603
604 let captured = cb.take_captured();
605 assert_eq!(captured.len(), 4); assert_eq!(captured[0].unique_id, 2);
607 assert_eq!(captured[1].unique_id, 3);
608 assert_eq!(captured[2].unique_id, 4);
609 assert_eq!(captured[3].unique_id, 5);
610 }
611
612 #[test]
613 fn test_post_count_zero_no_underflow() {
614 let mut cb = CircularBuffer::new(2, 0, TriggerCondition::External);
617 cb.push(make_array(1));
618 cb.push(make_array(2));
619 cb.trigger();
620 assert!(cb.is_triggered());
621
622 let r = cb.push(make_array(3));
625 assert!(r.sequence_done);
626 let ids: Vec<_> = r.forward.iter().map(|a| a.unique_id).collect();
627 assert_eq!(ids, vec![1, 2, 3]);
628 assert!(!cb.is_triggered());
629 assert_eq!(cb.status(), BufferStatus::BufferFilling);
630
631 let r2 = cb.push(make_array(4));
633 assert!(!r2.sequence_done);
634 assert!(r2.forward.is_empty());
635 }
636
637 #[test]
638 fn test_attribute_trigger_post_count_zero() {
639 let mut cb = CircularBuffer::new(
642 1,
643 0,
644 TriggerCondition::AttributeThreshold {
645 name: "trigger".into(),
646 threshold: 5.0,
647 },
648 );
649 cb.push(make_array_with_attr(1, 1.0));
650 let r = cb.push(make_array_with_attr(2, 9.0));
651 assert!(r.sequence_done);
652 let ids: Vec<_> = r.forward.iter().map(|a| a.unique_id).collect();
653 assert_eq!(ids, vec![1, 2]); assert!(!cb.is_triggered());
655 }
656
657 #[test]
658 fn test_attribute_trigger() {
659 let mut cb = CircularBuffer::new(
660 1,
661 2,
662 TriggerCondition::AttributeThreshold {
663 name: "trigger".into(),
664 threshold: 5.0,
665 },
666 );
667
668 cb.push(make_array_with_attr(1, 1.0));
669 cb.push(make_array_with_attr(2, 2.0));
670 assert!(!cb.is_triggered());
671
672 let r3 = cb.push(make_array_with_attr(3, 5.0));
674 assert!(cb.is_triggered());
675 let ids3: Vec<_> = r3.forward.iter().map(|a| a.unique_id).collect();
677 assert_eq!(ids3, vec![2, 3]);
678
679 let r4 = cb.push(make_array(4));
680 assert!(r4.sequence_done);
681
682 let captured = cb.take_captured();
683 assert_eq!(captured.len(), 3);
685 assert_eq!(captured[0].unique_id, 2);
686 assert_eq!(captured[1].unique_id, 3);
687 assert_eq!(captured[2].unique_id, 4);
688 }
689
690 #[test]
693 fn test_calc_trigger() {
694 let expr = CalcExpression::parse("A>5").unwrap();
696 let mut cb = CircularBuffer::new(
697 1,
698 2,
699 TriggerCondition::Calc {
700 attr_a: "attr_a".into(),
701 attr_b: "attr_b".into(),
702 expression: expr,
703 },
704 );
705
706 cb.push(make_array_with_attrs(1, 3.0, 0.0));
708 assert!(!cb.is_triggered());
709
710 cb.push(make_array_with_attrs(2, 6.0, 0.0));
712 assert!(cb.is_triggered());
713
714 let done = cb.push(make_array(3));
715 assert!(done.sequence_done);
716
717 let captured = cb.take_captured();
718 assert_eq!(captured.len(), 3);
720 assert_eq!(captured[0].unique_id, 1);
721 assert_eq!(captured[1].unique_id, 2);
722 assert_eq!(captured[2].unique_id, 3);
723 }
724
725 #[test]
726 fn test_calc_expression_parse() {
727 let expr = CalcExpression::parse("A>5").unwrap();
729 assert_eq!(expr.evaluate(6.0, 0.0), 1.0);
730 assert_eq!(expr.evaluate(4.0, 0.0), 0.0);
731 assert_eq!(expr.evaluate(5.0, 0.0), 0.0); let expr = CalcExpression::parse("A>=5").unwrap();
735 assert_eq!(expr.evaluate(5.0, 0.0), 1.0);
736 assert_eq!(expr.evaluate(4.9, 0.0), 0.0);
737
738 let expr = CalcExpression::parse("A>3&&B<10").unwrap();
740 assert_eq!(expr.evaluate(4.0, 5.0), 1.0);
741 assert_eq!(expr.evaluate(2.0, 5.0), 0.0);
742 assert_eq!(expr.evaluate(4.0, 15.0), 0.0);
743
744 let expr = CalcExpression::parse("(A>10)||(B>10)").unwrap();
746 assert_eq!(expr.evaluate(11.0, 0.0), 1.0);
747 assert_eq!(expr.evaluate(0.0, 11.0), 1.0);
748 assert_eq!(expr.evaluate(0.0, 0.0), 0.0);
749
750 let expr = CalcExpression::parse("A!=0").unwrap();
752 assert_eq!(expr.evaluate(1.0, 0.0), 1.0);
753 assert_eq!(expr.evaluate(0.0, 0.0), 0.0);
754
755 let expr = CalcExpression::parse("A==B").unwrap();
757 assert_eq!(expr.evaluate(5.0, 5.0), 1.0);
758 assert_eq!(expr.evaluate(5.0, 6.0), 0.0);
759
760 let expr = CalcExpression::parse("!A").unwrap();
762 assert_eq!(expr.evaluate(0.0, 0.0), 1.0);
763 assert_eq!(expr.evaluate(1.0, 0.0), 0.0);
764
765 let expr = CalcExpression::parse("A=5").unwrap();
768 assert_eq!(expr.evaluate(5.0, 0.0), 1.0);
769 assert_eq!(expr.evaluate(4.0, 0.0), 0.0);
770
771 let expr = CalcExpression::parse("A&B").unwrap();
772 assert_eq!(expr.evaluate(3.0, 1.0), 1.0);
774
775 let expr = CalcExpression::parse("ABS(A)").unwrap();
777 assert_eq!(expr.evaluate(-5.0, 0.0), 5.0);
778
779 let expr = CalcExpression::parse("SQRT(A)").unwrap();
780 assert!((expr.evaluate(9.0, 0.0) - 3.0).abs() < 1e-10);
781
782 let expr = CalcExpression::parse("A+B").unwrap();
783 assert_eq!(expr.evaluate(3.0, 4.0), 7.0);
784
785 let expr = CalcExpression::parse("A-B").unwrap();
786 assert_eq!(expr.evaluate(10.0, 3.0), 7.0);
787
788 let expr = CalcExpression::parse("A*B").unwrap();
789 assert_eq!(expr.evaluate(3.0, 4.0), 12.0);
790
791 let expr = CalcExpression::parse("A/B").unwrap();
792 assert_eq!(expr.evaluate(12.0, 4.0), 3.0);
793
794 let expr = CalcExpression::parse("A>5&&C>0").unwrap();
796 let mut vars = [0.0f64; calc::CALC_NARGS];
797 vars[0] = 6.0; vars[2] = 1.0; assert_eq!(expr.evaluate_vars(&vars), 1.0);
800 vars[2] = 0.0; assert_eq!(expr.evaluate_vars(&vars), 0.0);
802
803 assert!(CalcExpression::parse("@@@").is_none());
805 }
806
807 #[test]
808 fn test_preset_trigger_count() {
809 let mut cb = CircularBuffer::new(1, 1, TriggerCondition::External);
810 cb.set_preset_trigger_count(2);
811
812 assert_eq!(cb.status(), BufferStatus::Idle);
813
814 cb.push(make_array(1));
816 assert_eq!(cb.status(), BufferStatus::BufferFilling);
817
818 cb.trigger();
820 assert_eq!(cb.trigger_count(), 1);
821 assert_eq!(cb.status(), BufferStatus::Flushing);
822
823 let done = cb.push(make_array(2));
824 assert!(done.sequence_done);
825 assert_eq!(cb.status(), BufferStatus::BufferFilling); cb.take_captured();
828
829 cb.push(make_array(3));
831
832 cb.trigger();
834 assert_eq!(cb.trigger_count(), 2);
835 assert_eq!(cb.status(), BufferStatus::Flushing);
836
837 let done = cb.push(make_array(4));
838 assert!(done.sequence_done);
839 assert_eq!(cb.status(), BufferStatus::AcquisitionCompleted);
840
841 cb.take_captured();
842
843 let done = cb.push(make_array(5));
845 assert!(!done.sequence_done);
846 assert_eq!(cb.status(), BufferStatus::AcquisitionCompleted);
847
848 cb.trigger();
850 assert_eq!(cb.trigger_count(), 2); }
852
853 #[test]
854 fn test_buffer_status_transitions() {
855 let mut cb = CircularBuffer::new(2, 1, TriggerCondition::External);
856
857 assert_eq!(cb.status(), BufferStatus::Idle);
859
860 cb.push(make_array(1));
862 assert_eq!(cb.status(), BufferStatus::BufferFilling);
863
864 cb.push(make_array(2));
865 assert_eq!(cb.status(), BufferStatus::BufferFilling);
866
867 cb.trigger();
869 assert_eq!(cb.status(), BufferStatus::Flushing);
870
871 let done = cb.push(make_array(3));
873 assert!(done.sequence_done);
874 assert_eq!(cb.status(), BufferStatus::BufferFilling);
875
876 cb.reset();
878 assert_eq!(cb.status(), BufferStatus::Idle);
879 assert_eq!(cb.trigger_count(), 0);
880 }
881}