bhtune_db/models/
tune_samples.rs1use bhtune_core::{MrftState, Tick};
2use sqlx::{Row, SqlitePool, sqlite::SqliteRow};
3
4use crate::{
5 convert::{enum_to_text, text_to_enum},
6 error::{DbError, DbResult},
7};
8
9#[cfg(doc)]
10use super::dcs_templates::TemplateOrigin;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
27#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
28#[serde(rename_all = "snake_case")]
29pub enum SampleQuality {
30 Good,
31 Uncertain,
32 Bad,
33}
34
35#[derive(Debug, Clone, Copy, PartialEq)]
37pub struct TuneSampleRow {
38 pub id: i64,
39 pub run_id: i64,
40 pub tick_index: i64,
43 pub sample: Tick,
44 pub state: MrftState,
45 pub pv_quality: SampleQuality,
48}
49
50impl TuneSampleRow {
51 pub async fn insert(
57 pool: &SqlitePool,
58 run_id: i64,
59 tick_index: i64,
60 sample: Tick,
61 state: MrftState,
62 pv_quality: SampleQuality,
63 ) -> DbResult<TuneSampleRow> {
64 let row = sqlx::query(
65 r"
66 INSERT INTO tune_samples (
67 run_id, tick, time, pv, pv_quality, hysteresis, mv_value_current,
68 mv_sign_next_step, counter_all_switches, cycles_completed, cycles_remaining
69 ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
70 RETURNING *
71 ",
72 )
73 .bind(run_id)
74 .bind(tick_index)
75 .bind(sample.time)
76 .bind(sample.pv)
77 .bind(enum_to_text(&pv_quality)?)
78 .bind(state.hysteresis)
79 .bind(state.mv_value_current)
80 .bind(state.mv_sign_next_step)
81 .bind(state.counter_all_switches)
82 .bind(state.cycles_completed)
83 .bind(state.cycles_remaining)
84 .fetch_one(pool)
85 .await
86 .map_err(DbError::Query)?;
87
88 row_to_tune_sample(row)
89 }
90
91 pub async fn list_for_run(pool: &SqlitePool, run_id: i64) -> DbResult<Vec<TuneSampleRow>> {
94 let rows = sqlx::query("SELECT * FROM tune_samples WHERE run_id = ? ORDER BY tick")
95 .bind(run_id)
96 .fetch_all(pool)
97 .await
98 .map_err(DbError::Query)?;
99 rows.into_iter().map(row_to_tune_sample).collect()
100 }
101
102 pub async fn list_for_run_since(
108 pool: &SqlitePool,
109 run_id: i64,
110 after_tick: i64,
111 ) -> DbResult<Vec<TuneSampleRow>> {
112 let rows =
113 sqlx::query("SELECT * FROM tune_samples WHERE run_id = ? AND tick > ? ORDER BY tick")
114 .bind(run_id)
115 .bind(after_tick)
116 .fetch_all(pool)
117 .await
118 .map_err(DbError::Query)?;
119 rows.into_iter().map(row_to_tune_sample).collect()
120 }
121}
122
123fn row_to_tune_sample(row: SqliteRow) -> DbResult<TuneSampleRow> {
124 Ok(TuneSampleRow {
125 id: row.try_get("id").map_err(DbError::Query)?,
126 run_id: row.try_get("run_id").map_err(DbError::Query)?,
127 tick_index: row.try_get("tick").map_err(DbError::Query)?,
128 sample: Tick {
129 time: row.try_get("time").map_err(DbError::Query)?,
130 pv: row.try_get("pv").map_err(DbError::Query)?,
131 },
132 state: MrftState {
133 hysteresis: row.try_get("hysteresis").map_err(DbError::Query)?,
134 mv_value_current: row.try_get("mv_value_current").map_err(DbError::Query)?,
135 mv_sign_next_step: row.try_get("mv_sign_next_step").map_err(DbError::Query)?,
136 counter_all_switches: row
137 .try_get("counter_all_switches")
138 .map_err(DbError::Query)?,
139 cycles_completed: row.try_get("cycles_completed").map_err(DbError::Query)?,
140 cycles_remaining: row.try_get("cycles_remaining").map_err(DbError::Query)?,
141 },
142 pv_quality: {
143 let pv_quality: String = row.try_get("pv_quality").map_err(DbError::Query)?;
144 text_to_enum("pv_quality", &pv_quality)?
145 },
146 })
147}
148