1use bhtune_core::ResponseLevel;
2use chrono::{DateTime, Utc};
3use sqlx::{Row, SqlitePool, sqlite::SqliteRow};
4
5use crate::{
6 convert::{enum_to_text, option_enum_text, text_to_enum},
7 error::{DbError, DbResult},
8};
9
10#[cfg(doc)]
11use super::{tune_results::TuneResultRow, tune_runs::TuneRunRow};
12
13#[derive(Debug, Clone, PartialEq)]
24pub struct TuneWriteRow {
25 pub id: i64,
26 pub run_id: i64,
27 pub response_level: ResponseLevel,
28 pub written_at: DateTime<Utc>,
29 pub kind: WriteKind,
32 pub allow_uncertain_quality: bool,
36 pub previous: Option<WriteReadback>,
40 pub proportional_written: Option<f32>,
41 pub integral_written: Option<f32>,
42 pub derivative_written: Option<f32>,
43 pub proportional_readback: Option<f32>,
47 pub integral_readback: Option<f32>,
48 pub derivative_readback: Option<f32>,
49 pub success: bool,
50 pub error_message: Option<String>,
51 pub rollback_state: Option<RollbackState>,
57 pub rollback_error: Option<String>,
58}
59
60#[derive(Debug, Clone, Copy, PartialEq)]
65pub struct WriteReadback {
66 pub proportional: f32,
67 pub integral: f32,
68 pub derivative: f32,
69}
70
71#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
74#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
75#[serde(rename_all = "snake_case")]
76pub enum RollbackState {
77 Succeeded,
80 Failed,
84}
85
86#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
91#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
92#[serde(rename_all = "snake_case")]
93pub enum WriteKind {
94 Write,
96 Revert,
100}
101
102#[derive(Debug, Clone, PartialEq)]
107pub struct NewTuneWrite {
108 pub response_level: ResponseLevel,
109 pub written_at: DateTime<Utc>,
110 pub kind: WriteKind,
111 pub allow_uncertain_quality: bool,
112 pub previous: Option<WriteReadback>,
113 pub proportional_written: Option<f32>,
114 pub integral_written: Option<f32>,
115 pub derivative_written: Option<f32>,
116 pub proportional_readback: Option<f32>,
117 pub integral_readback: Option<f32>,
118 pub derivative_readback: Option<f32>,
119 pub success: bool,
120 pub error_message: Option<String>,
121 pub rollback_state: Option<RollbackState>,
122 pub rollback_error: Option<String>,
123}
124
125impl NewTuneWrite {
126 pub fn new(response_level: ResponseLevel, written_at: DateTime<Utc>) -> Self {
131 NewTuneWrite {
132 response_level,
133 written_at,
134 kind: WriteKind::Write,
135 allow_uncertain_quality: true,
136 previous: None,
137 proportional_written: None,
138 integral_written: None,
139 derivative_written: None,
140 proportional_readback: None,
141 integral_readback: None,
142 derivative_readback: None,
143 success: false,
144 error_message: None,
145 rollback_state: None,
146 rollback_error: None,
147 }
148 }
149}
150
151impl TuneWriteRow {
152 pub async fn insert(
154 pool: &SqlitePool,
155 run_id: i64,
156 new: NewTuneWrite,
157 ) -> DbResult<TuneWriteRow> {
158 let row = sqlx::query(
159 r"
160 INSERT INTO tune_writes (
161 run_id, response_level, written_at, kind,
162 allow_uncertain_quality,
163 proportional_previous, integral_previous, derivative_previous,
164 proportional_written, integral_written, derivative_written,
165 proportional_readback, integral_readback, derivative_readback,
166 success, error_message, rollback_state, rollback_error
167 ) VALUES (
168 ?, ?, ?, ?,
169 ?,
170 ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
171 )
172 RETURNING *
173 ",
174 )
175 .bind(run_id)
176 .bind(enum_to_text(&new.response_level)?)
177 .bind(new.written_at)
178 .bind(enum_to_text(&new.kind)?)
179 .bind(new.allow_uncertain_quality)
180 .bind(new.previous.map(|p| p.proportional))
181 .bind(new.previous.map(|p| p.integral))
182 .bind(new.previous.map(|p| p.derivative))
183 .bind(new.proportional_written)
184 .bind(new.integral_written)
185 .bind(new.derivative_written)
186 .bind(new.proportional_readback)
187 .bind(new.integral_readback)
188 .bind(new.derivative_readback)
189 .bind(new.success)
190 .bind(new.error_message)
191 .bind(option_enum_text(new.rollback_state.as_ref())?)
192 .bind(new.rollback_error)
193 .fetch_one(pool)
194 .await
195 .map_err(DbError::Query)?;
196
197 row_to_tune_write(row)
198 }
199
200 pub async fn list_for_run(pool: &SqlitePool, run_id: i64) -> DbResult<Vec<TuneWriteRow>> {
203 let rows = sqlx::query("SELECT * FROM tune_writes WHERE run_id = ? ORDER BY written_at")
204 .bind(run_id)
205 .fetch_all(pool)
206 .await
207 .map_err(DbError::Query)?;
208 rows.into_iter().map(row_to_tune_write).collect()
209 }
210}
211
212fn row_to_tune_write(row: SqliteRow) -> DbResult<TuneWriteRow> {
213 let response_level: String = row.try_get("response_level").map_err(DbError::Query)?;
214 let kind: String = row.try_get("kind").map_err(DbError::Query)?;
215 let proportional_previous: Option<f32> = row
216 .try_get("proportional_previous")
217 .map_err(DbError::Query)?;
218 let integral_previous: Option<f32> =
219 row.try_get("integral_previous").map_err(DbError::Query)?;
220 let derivative_previous: Option<f32> =
221 row.try_get("derivative_previous").map_err(DbError::Query)?;
222 let previous = match (
223 proportional_previous,
224 integral_previous,
225 derivative_previous,
226 ) {
227 (Some(proportional), Some(integral), Some(derivative)) => Some(WriteReadback {
228 proportional,
229 integral,
230 derivative,
231 }),
232 _ => None,
233 };
234 let rollback_state: Option<String> = row.try_get("rollback_state").map_err(DbError::Query)?;
235 Ok(TuneWriteRow {
236 id: row.try_get("id").map_err(DbError::Query)?,
237 run_id: row.try_get("run_id").map_err(DbError::Query)?,
238 response_level: text_to_enum("response_level", &response_level)?,
239 written_at: row.try_get("written_at").map_err(DbError::Query)?,
240 kind: text_to_enum("kind", &kind)?,
241 allow_uncertain_quality: row
242 .try_get("allow_uncertain_quality")
243 .map_err(DbError::Query)?,
244 previous,
245 proportional_written: row
246 .try_get("proportional_written")
247 .map_err(DbError::Query)?,
248 integral_written: row.try_get("integral_written").map_err(DbError::Query)?,
249 derivative_written: row.try_get("derivative_written").map_err(DbError::Query)?,
250 proportional_readback: row
251 .try_get("proportional_readback")
252 .map_err(DbError::Query)?,
253 integral_readback: row.try_get("integral_readback").map_err(DbError::Query)?,
254 derivative_readback: row.try_get("derivative_readback").map_err(DbError::Query)?,
255 success: row.try_get("success").map_err(DbError::Query)?,
256 error_message: row.try_get("error_message").map_err(DbError::Query)?,
257 rollback_state: rollback_state
258 .map(|s| text_to_enum("rollback_state", &s))
259 .transpose()?,
260 rollback_error: row.try_get("rollback_error").map_err(DbError::Query)?,
261 })
262}
263