1use std::collections::BTreeMap;
8use std::future::Future;
9use std::sync::atomic::{self, Ordering};
10use std::sync::Arc;
11
12use delegate::delegate;
13
14use crate::output as tv;
15use crate::output::trait_ext::{MapExt, VecExt};
16use crate::spec;
17use tv::{dut, step, Ident};
18
19pub struct MeasurementSeries {
24 id: String,
25 detail: MeasurementSeriesDetail,
26
27 emitter: Arc<step::StepEmitter>,
28}
29
30impl MeasurementSeries {
31 pub(crate) fn new(
34 series_id: &str,
35 info: MeasurementSeriesDetail,
36 emitter: Arc<step::StepEmitter>,
37 ) -> Self {
38 Self {
39 id: series_id.to_owned(),
40 detail: info,
41 emitter,
42 }
43 }
44
45 pub async fn start(self) -> Result<StartedMeasurementSeries, tv::OcptvError> {
65 let info = &self.detail;
66
67 let start = spec::MeasurementSeriesStart {
68 name: info.name.clone(),
69 unit: info.unit.clone(),
70 series_id: self.id.clone(),
71 validators: info.validators.map_option(Validator::to_spec),
72 hardware_info: info
73 .hardware_info
74 .as_ref()
75 .map(dut::DutHardwareInfo::to_spec),
76 subcomponent: info.subcomponent.as_ref().map(dut::Subcomponent::to_spec),
77 metadata: info.metadata.option(),
78 };
79
80 self.emitter
81 .emit(&spec::TestStepArtifactImpl::MeasurementSeriesStart(start))
82 .await?;
83
84 Ok(StartedMeasurementSeries {
85 parent: self,
86 seqno: Arc::new(atomic::AtomicU64::new(0)),
87 })
88 }
89
90 pub async fn scope<F, R>(self, func: F) -> Result<(), tv::OcptvError>
121 where
122 R: Future<Output = Result<(), tv::OcptvError>> + Send + 'static,
123 F: FnOnce(ScopedMeasurementSeries) -> R + Send + 'static,
124 {
125 let series = Arc::new(self.start().await?);
126 func(ScopedMeasurementSeries {
127 series: Arc::clone(&series),
128 })
129 .await?;
130 series.end_impl().await?;
131
132 Ok(())
133 }
134}
135
136pub struct StartedMeasurementSeries {
138 parent: MeasurementSeries,
139
140 seqno: Arc<atomic::AtomicU64>,
141}
142
143impl StartedMeasurementSeries {
144 fn incr_seqno(&self) -> u64 {
145 self.seqno.fetch_add(1, Ordering::AcqRel)
146 }
147
148 async fn end_impl(&self) -> Result<(), tv::OcptvError> {
151 let end = spec::MeasurementSeriesEnd {
152 series_id: self.parent.id.clone(),
153 total_count: self.seqno.load(Ordering::Acquire),
154 };
155
156 self.parent
157 .emitter
158 .emit(&spec::TestStepArtifactImpl::MeasurementSeriesEnd(end))
159 .await?;
160
161 Ok(())
162 }
163
164 pub async fn end(self) -> Result<(), tv::OcptvError> {
184 self.end_impl().await
185 }
186
187 pub async fn add_measurement<V: Into<tv::Value>>(
207 &self,
208 value: V,
209 ) -> Result<(), tv::OcptvError> {
210 self.add_measurement_detail(MeasurementElementDetail {
211 value: value.into(),
212 ..Default::default()
213 })
214 .await
215 }
216
217 pub async fn add_measurement_detail(
239 &self,
240 element: MeasurementElementDetail,
241 ) -> Result<(), tv::OcptvError> {
242 let element = spec::MeasurementSeriesElement {
243 index: self.incr_seqno(),
244 value: element.value,
245 timestamp: element
246 .timestamp
247 .unwrap_or(self.parent.emitter.timestamp_provider().now()),
248 series_id: self.parent.id.clone(),
249 metadata: element.metadata.option(),
250 };
251
252 self.parent
253 .emitter
254 .emit(&spec::TestStepArtifactImpl::MeasurementSeriesElement(
255 element,
256 ))
257 .await?;
258
259 Ok(())
260 }
261}
262
263pub struct ScopedMeasurementSeries {
265 series: Arc<StartedMeasurementSeries>,
266}
267
268impl ScopedMeasurementSeries {
269 delegate! {
270 to self.series {
271 pub async fn add_measurement<V: Into<tv::Value>>(&self, value: V) -> Result<(), tv::OcptvError>;
272 pub async fn add_measurement_detail(
273 &self,
274 element: MeasurementElementDetail,
275 ) -> Result<(), tv::OcptvError>;
276 }
277 }
278}
279
280#[derive(Default)]
282pub struct MeasurementElementDetail {
283 value: tv::Value,
284 timestamp: Option<chrono::DateTime<chrono_tz::Tz>>,
285
286 metadata: BTreeMap<String, tv::Value>,
287}
288
289impl MeasurementElementDetail {
290 pub fn builder<V: Into<tv::Value>>(value: V) -> MeasurementElementDetailBuilder {
291 MeasurementElementDetailBuilder::new(value.into())
292 }
293}
294
295#[derive(Default)]
297pub struct MeasurementElementDetailBuilder {
298 value: tv::Value,
299 timestamp: Option<chrono::DateTime<chrono_tz::Tz>>,
300
301 metadata: BTreeMap<String, tv::Value>,
302}
303
304impl MeasurementElementDetailBuilder {
305 fn new(value: tv::Value) -> Self {
306 Self {
307 value,
308 ..Default::default()
309 }
310 }
311
312 pub fn timestamp(mut self, value: chrono::DateTime<chrono_tz::Tz>) -> Self {
313 self.timestamp = Some(value);
314 self
315 }
316
317 pub fn add_metadata<V: Into<tv::Value>>(mut self, key: &str, value: V) -> Self {
318 self.metadata.insert(key.to_string(), value.into());
319 self
320 }
321
322 pub fn build(self) -> MeasurementElementDetail {
323 MeasurementElementDetail {
324 value: self.value,
325 timestamp: self.timestamp,
326 metadata: self.metadata,
327 }
328 }
329}
330
331#[derive(Clone)]
333pub struct Validator {
334 name: Option<String>,
335 validator_type: spec::ValidatorType,
336 value: tv::Value,
337 metadata: BTreeMap<String, tv::Value>,
338}
339
340impl Validator {
341 pub fn builder<V: Into<tv::Value>>(
342 validator_type: spec::ValidatorType,
343 value: V,
344 ) -> ValidatorBuilder {
345 ValidatorBuilder::new(validator_type, value.into())
346 }
347
348 pub fn to_spec(&self) -> spec::Validator {
349 spec::Validator {
350 name: self.name.clone(),
351 validator_type: self.validator_type.clone(),
352 value: self.value.clone(),
353 metadata: self.metadata.option(),
354 }
355 }
356}
357
358#[derive(Debug)]
360pub struct ValidatorBuilder {
361 name: Option<String>,
362 validator_type: spec::ValidatorType,
363 value: tv::Value,
364
365 metadata: BTreeMap<String, tv::Value>,
366}
367
368impl ValidatorBuilder {
369 fn new(validator_type: spec::ValidatorType, value: tv::Value) -> Self {
370 ValidatorBuilder {
371 validator_type,
372 value,
373 name: None,
374 metadata: BTreeMap::new(),
375 }
376 }
377
378 pub fn name(mut self, value: &str) -> Self {
379 self.name = Some(value.to_string());
380 self
381 }
382
383 pub fn add_metadata<V: Into<tv::Value>>(mut self, key: &str, value: V) -> Self {
384 self.metadata.insert(key.to_string(), value.into());
385 self
386 }
387
388 pub fn build(self) -> Validator {
389 Validator {
390 name: self.name,
391 validator_type: self.validator_type,
392 value: self.value,
393 metadata: self.metadata,
394 }
395 }
396}
397
398#[derive(Default)]
425pub struct Measurement {
426 name: String,
427
428 value: tv::Value,
429 unit: Option<String>,
430 validators: Vec<Validator>,
431
432 hardware_info: Option<dut::DutHardwareInfo>,
433 subcomponent: Option<dut::Subcomponent>,
434
435 metadata: BTreeMap<String, tv::Value>,
436}
437
438impl Measurement {
439 pub fn new<V: Into<tv::Value>>(name: &str, value: V) -> Self {
448 Measurement {
449 name: name.to_string(),
450 value: value.into(),
451 ..Default::default()
452 }
453 }
454
455 pub fn builder<V: Into<tv::Value>>(name: &str, value: V) -> MeasurementBuilder {
473 MeasurementBuilder::new(name, value.into())
474 }
475
476 pub fn to_artifact(&self) -> spec::Measurement {
486 spec::Measurement {
487 name: self.name.clone(),
488 unit: self.unit.clone(),
489 value: self.value.clone(),
490 validators: self.validators.map_option(Validator::to_spec),
491 hardware_info: self
492 .hardware_info
493 .as_ref()
494 .map(dut::DutHardwareInfo::to_spec),
495 subcomponent: self
496 .subcomponent
497 .as_ref()
498 .map(|subcomponent| subcomponent.to_spec()),
499 metadata: self.metadata.option(),
500 }
501 }
502}
503
504#[derive(Default)]
521pub struct MeasurementBuilder {
522 name: String,
523
524 value: tv::Value,
525 unit: Option<String>,
526 validators: Vec<Validator>,
527
528 hardware_info: Option<dut::DutHardwareInfo>,
529 subcomponent: Option<dut::Subcomponent>,
530
531 metadata: BTreeMap<String, tv::Value>,
532}
533
534impl MeasurementBuilder {
535 fn new(name: &str, value: tv::Value) -> Self {
536 MeasurementBuilder {
537 name: name.to_string(),
538 value,
539 ..Default::default()
540 }
541 }
542
543 pub fn add_validator(mut self, validator: Validator) -> Self {
553 self.validators.push(validator.clone());
554 self
555 }
556
557 pub fn hardware_info(mut self, hardware_info: &dut::DutHardwareInfo) -> Self {
570 self.hardware_info = Some(hardware_info.clone());
571 self
572 }
573
574 pub fn subcomponent(mut self, subcomponent: dut::Subcomponent) -> Self {
584 self.subcomponent = Some(subcomponent);
585 self
586 }
587
588 pub fn add_metadata<V: Into<tv::Value>>(mut self, key: &str, value: V) -> Self {
598 self.metadata.insert(key.to_string(), value.into());
599 self
600 }
601
602 pub fn unit(mut self, unit: &str) -> MeasurementBuilder {
611 self.unit = Some(unit.to_string());
612 self
613 }
614
615 pub fn build(self) -> Measurement {
625 Measurement {
626 name: self.name,
627 value: self.value,
628 unit: self.unit,
629 validators: self.validators,
630 hardware_info: self.hardware_info,
631 subcomponent: self.subcomponent,
632 metadata: self.metadata,
633 }
634 }
635}
636
637pub struct MeasurementSeriesDetail {
639 pub(crate) id: tv::Ident,
642 name: String,
643
644 unit: Option<String>,
645 validators: Vec<Validator>,
646
647 hardware_info: Option<dut::DutHardwareInfo>,
648 subcomponent: Option<dut::Subcomponent>,
649
650 metadata: BTreeMap<String, tv::Value>,
651}
652
653impl MeasurementSeriesDetail {
654 pub fn new(name: &str) -> MeasurementSeriesDetail {
655 MeasurementSeriesDetailBuilder::new(name).build()
656 }
657
658 pub fn builder(name: &str) -> MeasurementSeriesDetailBuilder {
659 MeasurementSeriesDetailBuilder::new(name)
660 }
661}
662
663#[derive(Default)]
665pub struct MeasurementSeriesDetailBuilder {
666 id: tv::Ident,
667 name: String,
668
669 unit: Option<String>,
670 validators: Vec<Validator>,
671
672 hardware_info: Option<dut::DutHardwareInfo>,
673 subcomponent: Option<dut::Subcomponent>,
674
675 metadata: BTreeMap<String, tv::Value>,
676}
677
678impl MeasurementSeriesDetailBuilder {
679 fn new(name: &str) -> Self {
680 MeasurementSeriesDetailBuilder {
681 id: Ident::Auto,
682 name: name.to_string(),
683 ..Default::default()
684 }
685 }
686
687 pub fn id(mut self, id: tv::Ident) -> Self {
688 self.id = id;
689 self
690 }
691
692 pub fn unit(mut self, unit: &str) -> Self {
693 self.unit = Some(unit.to_string());
694 self
695 }
696
697 pub fn add_validator(mut self, validator: Validator) -> Self {
698 self.validators.push(validator);
699 self
700 }
701
702 pub fn hardware_info(mut self, hardware_info: &dut::DutHardwareInfo) -> Self {
703 self.hardware_info = Some(hardware_info.clone());
704 self
705 }
706
707 pub fn subcomponent(mut self, subcomponent: dut::Subcomponent) -> Self {
708 self.subcomponent = Some(subcomponent);
709 self
710 }
711
712 pub fn add_metadata<V: Into<tv::Value>>(mut self, key: &str, value: V) -> Self {
713 self.metadata.insert(key.to_string(), value.into());
714 self
715 }
716
717 pub fn build(self) -> MeasurementSeriesDetail {
718 MeasurementSeriesDetail {
719 id: self.id,
720 name: self.name,
721 unit: self.unit,
722 validators: self.validators,
723 hardware_info: self.hardware_info,
724 subcomponent: self.subcomponent,
725 metadata: self.metadata,
726 }
727 }
728}
729
730#[cfg(test)]
731mod tests {
732 use super::*;
733 use crate::output as tv;
734 use crate::spec;
735 use maplit::{btreemap, convert_args};
736 use tv::dut::*;
737 use tv::ValidatorType;
738
739 use anyhow::{bail, Result};
740
741 #[test]
742 fn test_measurement_as_test_step_descendant_to_artifact() -> Result<()> {
743 let name = "name".to_owned();
744 let value = tv::Value::from(50);
745 let measurement = Measurement::new(&name, value.clone());
746
747 let artifact = measurement.to_artifact();
748 assert_eq!(
749 artifact,
750 spec::Measurement {
751 name: name.to_string(),
752 unit: None,
753 value,
754 validators: None,
755 hardware_info: None,
756 subcomponent: None,
757 metadata: None,
758 }
759 );
760
761 Ok(())
762 }
763
764 #[test]
765 fn test_measurement_builder_as_test_step_descendant_to_artifact() -> Result<()> {
766 let mut dut = DutInfo::new("dut0");
767
768 let name = "name".to_owned();
769 let value = tv::Value::from(50000);
770 let hw_info = dut.add_hardware_info(HardwareInfo::builder("name").build());
771 let validator = Validator::builder(spec::ValidatorType::Equal, 30).build();
772
773 let meta_key = "key";
774 let meta_value = tv::Value::from("value");
775 let metadata = convert_args!(btreemap!(
776 meta_key => meta_value.clone(),
777 ));
778
779 let subcomponent = Subcomponent::builder("name").build();
780
781 let unit = "RPM";
782 let measurement = Measurement::builder(&name, value.clone())
783 .unit(unit)
784 .add_validator(validator.clone())
785 .add_validator(validator.clone())
786 .hardware_info(&hw_info)
787 .subcomponent(subcomponent.clone())
788 .add_metadata(meta_key, meta_value.clone())
789 .build();
790
791 let artifact = measurement.to_artifact();
792 assert_eq!(
793 artifact,
794 spec::Measurement {
795 name,
796 value,
797 unit: Some(unit.to_string()),
798 validators: Some(vec![validator.to_spec(), validator.to_spec()]),
799 hardware_info: Some(hw_info.to_spec()),
800 subcomponent: Some(subcomponent.to_spec()),
801 metadata: Some(metadata),
802 }
803 );
804
805 Ok(())
806 }
807
808 #[test]
809 fn test_validator() -> Result<()> {
810 let validator = Validator::builder(ValidatorType::Equal, 30)
811 .name("validator")
812 .add_metadata("key", "value")
813 .add_metadata("key2", "value2")
814 .build();
815
816 let spec_validator = validator.to_spec();
817
818 assert_eq!(spec_validator.name, Some("validator".to_owned()));
819 assert_eq!(spec_validator.value, 30);
820 assert_eq!(spec_validator.validator_type, ValidatorType::Equal);
821
822 match spec_validator.metadata {
823 Some(m) => {
824 assert_eq!(m["key"], "value");
825 assert_eq!(m["key2"], "value2");
826 }
827 _ => bail!("metadata is none"),
828 }
829
830 Ok(())
831 }
832}