Skip to main content

bhtune_runtime/tune/
outcome.rs

1use bhtune_driver::Quality;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4pub enum TuneOutcome {
5    Completed,
6    Aborted,
7    TimedOut,
8    PoorQuality,
9    ActuationFailed,
10    WriteBackFailed,
11    RestoreIncomplete,
12}
13
14impl TuneOutcome {
15    pub fn label(self) -> &'static str {
16        match self {
17            Self::Completed => "completed",
18            Self::Aborted => "aborted",
19            Self::TimedOut => "timed_out",
20            Self::PoorQuality => "poor_quality",
21            Self::ActuationFailed => "actuation_failed",
22            Self::WriteBackFailed => "write_back_failed",
23            Self::RestoreIncomplete => "restore_incomplete",
24        }
25    }
26}
27
28#[derive(Debug)]
29pub enum RunOutcome {
30    Completed {
31        write_back: WriteBackOutcome,
32        write_back_detail: Option<String>,
33    },
34    Aborted(AbortReason),
35    RestoreIncomplete {
36        reason: String,
37    },
38}
39
40#[derive(Debug)]
41pub struct TuneRunReport {
42    pub run_id: i64,
43    pub outcome: RunOutcome,
44}
45
46#[derive(Debug, Clone, PartialEq)]
47pub enum AbortReason {
48    UserInterrupt,
49    Timeout {
50        timeout_secs: u64,
51    },
52    OperationTimedOut {
53        tag: String,
54        op_timeout_secs: u64,
55    },
56    PoorQuality {
57        tag: String,
58        quality: Quality,
59    },
60    MvActuationUnconfirmed {
61        tag: String,
62        target: f32,
63        readback: Option<f32>,
64        tolerance: f32,
65        elapsed_ms: u64,
66        deadline_secs: u64,
67    },
68}
69
70#[derive(Debug, Clone, Copy, PartialEq, Eq)]
71pub enum WriteBackOutcome {
72    Skipped,
73    Written {
74        response_level: bhtune_core::ResponseLevel,
75    },
76    Failed,
77}
78
79pub fn tune_outcome_for_run(outcome: &RunOutcome) -> TuneOutcome {
80    match outcome {
81        RunOutcome::Completed {
82            write_back: WriteBackOutcome::Failed,
83            ..
84        } => TuneOutcome::WriteBackFailed,
85        RunOutcome::Completed { .. } => TuneOutcome::Completed,
86        RunOutcome::Aborted(AbortReason::UserInterrupt) => TuneOutcome::Aborted,
87        RunOutcome::Aborted(
88            AbortReason::Timeout { .. } | AbortReason::OperationTimedOut { .. },
89        ) => TuneOutcome::TimedOut,
90        RunOutcome::Aborted(AbortReason::PoorQuality { .. }) => TuneOutcome::PoorQuality,
91        RunOutcome::Aborted(AbortReason::MvActuationUnconfirmed { .. }) => {
92            TuneOutcome::ActuationFailed
93        }
94        RunOutcome::RestoreIncomplete { .. } => TuneOutcome::RestoreIncomplete,
95    }
96}
97
98pub(super) fn format_mv_actuation_abort_reason(reason: &AbortReason) -> String {
99    let AbortReason::MvActuationUnconfirmed {
100        tag,
101        target,
102        readback,
103        tolerance,
104        elapsed_ms,
105        deadline_secs,
106    } = reason
107    else {
108        return format!("{reason:?}");
109    };
110    let readback = readback
111        .map(|value| value.to_string())
112        .unwrap_or_else(|| "unavailable".to_string());
113    format!(
114        "MV actuation unconfirmed: tag '{tag}', target {target}, readback {readback}, tolerance {tolerance}, elapsed {elapsed_ms} ms, deadline {deadline_secs} s"
115    )
116}
117
118#[cfg(test)]
119mod tests {
120    use super::TuneOutcome;
121
122    #[test]
123    fn outcome_labels_preserve_the_cli_json_contract() {
124        for (outcome, label) in [
125            (TuneOutcome::Completed, "completed"),
126            (TuneOutcome::Aborted, "aborted"),
127            (TuneOutcome::TimedOut, "timed_out"),
128            (TuneOutcome::PoorQuality, "poor_quality"),
129            (TuneOutcome::ActuationFailed, "actuation_failed"),
130            (TuneOutcome::WriteBackFailed, "write_back_failed"),
131            (TuneOutcome::RestoreIncomplete, "restore_incomplete"),
132        ] {
133            assert_eq!(outcome.label(), label);
134        }
135    }
136}