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}