Skip to main content

TuneRunRow

Struct TuneRunRow 

Source
pub struct TuneRunRow {
Show 24 fields pub id: i64, pub loop_id: Option<i64>, pub demo_session_id: Option<i64>, pub loop_name: String, pub driver: TuneDriver, pub opc_server: Option<String>, pub bridge_host: Option<String>, pub started_at: DateTime<Utc>, pub completed_at: Option<DateTime<Utc>>, pub outcome: TuneOutcome, pub failure_reason: Option<String>, pub config: LoopConfig, pub template_origin: TemplateOrigin, pub template: DcsTemplate, pub tags: LoopTags, pub request_json: String, pub notes: Option<String>, pub initial_readings: Option<TuneRunInitialReadings>, pub allow_uncertain_quality: bool, pub timing_metrics: Option<TimingMetrics>, pub effective_tuning: Option<EffectiveTuning>, pub restore_status: Option<RestoreStatus>, pub restore_detail: Option<String>, pub created_at: DateTime<Utc>,
}
Expand description

One row of tune_runs: a single MRFT (or future Step Test) execution against a loop.

Fields§

§id: i64§loop_id: Option<i64>§demo_session_id: Option<i64>§loop_name: String§driver: TuneDriver§opc_server: Option<String>

The OPC DA server ProgID this run actually used – None for a non-opcda run, or for any run started before TuneRunRow::record_connection is called (see that method’s doc comment). Flat and filterable rather than folded into request_json, since bhtune history revert must know exactly which plant a past run touched.

§bridge_host: Option<String>

The opcda-bridge gateway host this run actually used – None for a non-opcda run, or before TuneRunRow::record_connection is called. See opc_server.

§started_at: DateTime<Utc>§completed_at: Option<DateTime<Utc>>§outcome: TuneOutcome§failure_reason: Option<String>§config: LoopConfig

Snapshot of the LoopConfig this run was started with — always known up front, since it’s user/schedule input rather than something read from the driver.

§template_origin: TemplateOrigin

Where the snapshotted template below came from (see TemplateOrigin).

§template: DcsTemplate

Snapshot of the exact DcsTemplate this run was configured against, deserialized from template_snapshot_json. Held as the full struct rather than just its name – which is what makes a historical run stay interpretable once the template catalog changes underneath it (safety-run-snapshot). There’s no separate template_name field here even though the table has a template_name column: .name on this field already carries that value, and the column exists purely so it’s filterable/indexable without json_extract (see this module’s own doc comment).

§tags: LoopTags

Snapshot of the resolved LoopTags this run actually used, deserialized from tags_json.

§request_json: String

The complete run request exactly as submitted (CLI flags or the HTTP POST /api/runs body), before any config-driven defaulting – raw JSON rather than a typed struct, since its shape is owned by bhtune-cli/bhtune-server, not bhtune-db. "{}" for any run started before TuneRunRow::record_connection is called. Powers ui-prefill-last-run and “duplicate this run”; never treat this as the source of truth for connection facts – that’s opc_server/bridge_host above.

§notes: Option<String>

Mutable operator notes for this run. None means no note is recorded.

§initial_readings: Option<TuneRunInitialReadings>§allow_uncertain_quality: bool

Whether this run permitted Quality::Uncertain OPC readings under the global allow_uncertain_quality policy (finding 5 of the live-plant safety review; Quality::Bad is never accepted regardless). false for every run started before TuneRunRow::record_allow_uncertain_quality is called – see that method’s doc comment for why it’s a separate post-start() update rather than a start() parameter.

§timing_metrics: Option<TimingMetrics>

Polling-cadence diagnostics collected from successful PV samples. None for runs created before timing diagnostics existed or attempts that ended before polling began.

§effective_tuning: Option<EffectiveTuning>

Concrete timing values used by this run after configuration defaults were resolved. None for runs created before effective-tuning snapshots existed or until TuneRunRow::record_effective_tuning is called.

§restore_status: Option<RestoreStatus>

Outcome of the best-effort restore attempted after this run ended – None if no restore was ever attempted (the run never mutated the loop, or hasn’t ended yet). See RestoreStatus and TuneRunRow::record_restore_status.

§restore_detail: Option<String>

Set only alongside restore_status = Some(RestoreStatus::Incomplete): what a second Ctrl+C, [tuning].restore_timeout_secs, or an individual failed restore step prevented from being confirmed.

§created_at: DateTime<Utc>

Implementations§

Source§

impl TuneRunRow

Source

pub async fn start( pool: &SqlitePool, loop_id: Option<i64>, loop_name: &str, driver: TuneDriver, config: LoopConfig, template_origin: TemplateOrigin, template: &DcsTemplate, tags: &LoopTags, now: DateTime<Utc>, ) -> DbResult<TuneRunRow>

Starts a new run: inserts a tune_runs row with outcome = 'running' and no initial readings yet (see Self::record_initial_readings). now is used for both started_at and created_at, which are naturally the same instant for a run that’s only just begun. loop_id may be None for an ad-hoc run against tags that were never saved as a reusable LoopRow.

template_origin/template/tags snapshot exactly what this run was configured against (safety-run-snapshot), so a historical run stays interpretable even after the template catalog changes underneath it. Serializing template/tags is treated as infallible here, the same way enum_to_text treats enum serialization as infallible: both types are plain, derived, string/enum-only structures with no maps, and every f32 field they can carry is validated finite well before a run reaches this call (see safety-validation) – a panic here would mean that contract regressed upstream, not a normal runtime failure this function’s DbResult should model.

Source

pub async fn start_with_demo_session( pool: &SqlitePool, demo_session_id: Option<i64>, loop_id: Option<i64>, loop_name: &str, driver: TuneDriver, config: LoopConfig, template_origin: TemplateOrigin, template: &DcsTemplate, tags: &LoopTags, now: DateTime<Utc>, ) -> DbResult<TuneRunRow>

Source

pub async fn start_owned( pool: &SqlitePool, demo_session_id: i64, loop_name: &str, config: LoopConfig, template_origin: TemplateOrigin, template: &DcsTemplate, tags: &LoopTags, now: DateTime<Utc>, ) -> DbResult<TuneRunRow>

Starts a simulator run owned by a demo session. The database trigger rejects any attempt to attach an owner to a live OPC DA run.

Source

pub async fn get_for_demo_session( pool: &SqlitePool, run_id: i64, demo_session_id: i64, ) -> DbResult<Option<TuneRunRow>>

Source

pub async fn list_for_demo_session( pool: &SqlitePool, demo_session_id: i64, pagination: Pagination, ) -> DbResult<Vec<TuneRunRow>>

Source

pub async fn newest_for_demo_session( pool: &SqlitePool, demo_session_id: i64, ) -> DbResult<Option<TuneRunRow>>

Source

pub async fn count_for_demo_session( pool: &SqlitePool, demo_session_id: i64, ) -> DbResult<i64>

Source

pub async fn count_demo_owned(pool: &SqlitePool) -> DbResult<i64>

Counts every current Demo-owned run across all sessions and outcomes. Full-mode rows have demo_session_id = NULL and are excluded.

Source

pub async fn prune_terminal_for_demo_session( pool: &SqlitePool, demo_session_id: i64, retain: u32, ) -> DbResult<u64>

Deletes terminal history beyond the newest retain rows for one Demo owner. Running rows never consume a retention slot and are never deleted. Child samples, results, writes, and MV-actuation evidence cascade with each deleted run.

Source

pub async fn prune_terminal_demo_owned( pool: &SqlitePool, retain: u32, ) -> DbResult<u64>

Applies Self::prune_terminal_for_demo_session’s retention rule independently to every Demo owner in one statement. Intended for periodic global cleanup.

Source

pub async fn count_rows_for_demo_session( pool: &SqlitePool, demo_session_id: i64, ) -> DbResult<i64>

Source

pub async fn record_connection( pool: &SqlitePool, run_id: i64, opc_server: Option<&str>, bridge_host: Option<&str>, request_json: &str, ) -> DbResult<TuneRunRow>

Records this run’s connection provenance and the exact request it was started with (db-run-request-snapshot): the OPC DA server ProgID and opcda-bridge gateway host actually used (None/None for a non-opcda run), and a JSON snapshot of the complete submitted request (CLI flags or the HTTP POST /api/runs body), captured before any config-driven defaulting so it reflects what the caller actually asked for.

A separate post-start() update rather than three more start() parameters, matching Self::record_allow_uncertain_quality’s precedent – start() already has 8 positional parameters across dozens of call sites in this workspace’s test suites alone, and three more would make every one of them noisier for no benefit, since none of those tests care about connection provenance. Unlike that method, this data is normally known the instant a run begins; the one production caller (bhtune-cli’s prepare()) calls this immediately after start() succeeds, before any driver I/O. opc_server/bridge_host default to NULL and request_json defaults to "{}" (see the migration), so every existing start() call site keeps compiling and behaving exactly as before.

Source

pub async fn record_effective_tuning( pool: &SqlitePool, run_id: i64, effective_tuning: EffectiveTuning, ) -> DbResult<TuneRunRow>

Records the concrete tune timing policy after all configuration defaults have been resolved. This is a follow-up update rather than another Self::start parameter so existing repository callers remain source-compatible. Production orchestration should call it immediately after start() and before any driver I/O.

Source

pub async fn update_notes( pool: &SqlitePool, run_id: i64, notes: Option<&str>, ) -> DbResult<TuneRunRow>

Replaces this run’s operator notes. Passing None clears the note, which is the persistence-layer implementation of the GUI’s delete-note action. This deliberately has no lifecycle restriction: notes remain editable while a run is active and after it reaches a terminal outcome.

Source

pub async fn record_initial_readings( pool: &SqlitePool, run_id: i64, readings: TuneRunInitialReadings, ) -> DbResult<TuneRunRow>

Records the driver’s initial-readings snapshot (ReadInitialOPCvalues in the legacy app) for an already-started run. Called at most once per run, right after that read succeeds – and, deliberately, before transition_to_manual’s first mutating write rather than after it (safety-restore-guard, finding 3 of the live-plant safety review), so mode_raw/mode_attribute_raw/setpoint_ini are always durably persisted before the loop is touched at all, letting a crashed run be reconstructed and restored later via bhtune restore-loop. A run that fails before or during the read instead goes straight to Self::fail with initial_readings left None.

Source

pub async fn record_allow_uncertain_quality( pool: &SqlitePool, run_id: i64, allow_uncertain_quality: bool, ) -> DbResult<TuneRunRow>

Records whether this run permitted Quality::Uncertain OPC readings under the global configuration policy (finding 5 of the live-plant safety review). A separate post-start() update rather than a new start() parameter deliberately: start() already has 8 positional parameters across 28 call sites in this crate’s own test suite alone, and this is a rarely-used escape hatch, not information every caller naturally has on hand at the moment a run begins the way template_origin/template/ tags are. The column defaults to 0/false (see the migration), so every existing start() call site keeps compiling and behaving exactly as before; only the one production caller in bhtune-cli’s run() needs to call this, right after start() succeeds.

Source

pub async fn record_timing_metrics( pool: &SqlitePool, run_id: i64, metrics: TimingMetrics, ) -> DbResult<TuneRunRow>

Records the polling cadence observed while this run was active. Kept as one typed JSON snapshot because these diagnostics are nested, evolve together, and have no SQL-level filtering requirement. Normal completed/aborted tune orchestration uses Self::complete_with_timing_metrics or Self::abort_with_timing_metrics so the terminal outcome and diagnostics become visible atomically; this standalone update is retained for non-terminal/failure paths and direct repository consumers.

Source

pub async fn record_restore_status( pool: &SqlitePool, run_id: i64, status: RestoreStatus, detail: Option<&str>, ) -> DbResult<TuneRunRow>

Records the outcome of a best-effort loop-restore attempt made after this run ended (safety-restore-guard, finding 3 of the live-plant safety review). Called once, after complete/fail/abort (whichever applies) and after attempt_restore has actually run – never before, and never for a run that ended without ever mutating the loop (nothing to restore, so nothing to record). detail should be Some(..) whenever status is RestoreStatus::Incomplete, naming what could not be confirmed; pass None for RestoreStatus::Confirmed. A separate post-hoc update rather than a complete/fail/abort parameter, matching Self::record_allow_uncertain_quality’s precedent: the restore attempt always happens strictly after one of those three, never alongside it.

Source

pub async fn complete( pool: &SqlitePool, run_id: i64, completed_at: DateTime<Utc>, ) -> DbResult<TuneRunRow>

Marks a run completed — a full MRFT test that ran to its natural end. The calculated results themselves are recorded separately via TuneResultRow::insert.

Source

pub async fn complete_with_timing_metrics( pool: &SqlitePool, run_id: i64, completed_at: DateTime<Utc>, timing_metrics: Option<TimingMetrics>, ) -> DbResult<TuneRunRow>

Atomically marks a run completed and publishes its timing diagnostics. This prevents readers that react to the terminal outcome (notably the SSE stream) from observing a completed run before its timing snapshot is visible.

Source

pub async fn fail( pool: &SqlitePool, run_id: i64, completed_at: DateTime<Utc>, failure_reason: &str, ) -> DbResult<TuneRunRow>

Marks a run failed, recording why. Valid whether or not Self::record_initial_readings was ever called for this run — a run can fail before, during, or after the initial read.

Source

pub async fn abort( pool: &SqlitePool, run_id: i64, completed_at: DateTime<Utc>, ) -> DbResult<TuneRunRow>

Marks a run aborted — stopped deliberately (by a human, or cli-safety’s wall-clock timeout guardrail) rather than failing on its own.

Source

pub async fn abort_with_timing_metrics( pool: &SqlitePool, run_id: i64, completed_at: DateTime<Utc>, timing_metrics: Option<TimingMetrics>, ) -> DbResult<TuneRunRow>

Atomically marks a run aborted and publishes any timing diagnostics collected before the abort, so terminal-state readers cannot miss the final timing snapshot.

Source

pub async fn abort_with_timing_metrics_and_reason( pool: &SqlitePool, run_id: i64, completed_at: DateTime<Utc>, timing_metrics: Option<TimingMetrics>, reason: &str, ) -> DbResult<TuneRunRow>

Atomically marks a run aborted, publishes its timing diagnostics, and persists the operator-facing reason for the abort in the existing failure_reason column. This is used when an abort has a durable safety explanation (for example an MV command whose live readback did not reach its target), while preserving the database’s existing TuneOutcome::Aborted value.

Source

pub async fn get(pool: &SqlitePool, id: i64) -> DbResult<Option<TuneRunRow>>

Fetches one row by id, or None if it doesn’t exist.

Source

pub async fn list( pool: &SqlitePool, filter: &TuneRunFilter, pagination: Pagination, ) -> DbResult<Vec<TuneRunRow>>

Lists runs matching filter, newest-started first, one pagination page at a time. See Self::count for the total number of rows filter matches across all pages.

Source

pub async fn count(pool: &SqlitePool, filter: &TuneRunFilter) -> DbResult<i64>

Counts every run matching filter, ignoring pagination — the total Self::list would page through.

Source

pub async fn delete_matching( pool: &SqlitePool, filter: &TuneRunFilter, ) -> DbResult<u64>

Deletes every run matching filter in one statement (SQLite treats a single statement as its own transaction, so no explicit BEGIN/COMMIT is needed). Returns the number of runs deleted. tune_samples/tune_results/tune_writes’s ON DELETE CASCADE foreign keys (see db-schema’s migration) remove each deleted run’s samples, results, and write-back audit rows automatically.

Shares [push_filter] with Self::list/Self::count, so “what a --dry-run preview reports” and “what an actual sweep deletes” can never disagree — used this way by history-retention’s automatic sweep and bhtune history prune.

An empty filter (every field None) matches and deletes every run in the table — callers that mean to scope a deletion must build a filter that says so explicitly; this function has no separate “are you sure” guard of its own, matching count/list treating an empty filter as “everything” rather than “nothing”.

Source

pub async fn delete(pool: &SqlitePool, id: i64) -> DbResult<bool>

Deletes exactly one run by id (history-explorer-ui’s delete action). Returns whether a row was actually deleted – false if no run has that id, letting the caller map that to a 404 rather than a silent no-op. Unlike DcsTemplateRow::delete, no foreign key ever blocks this: tune_runs has no parent-side RESTRICT reference pointing at it, only the ON DELETE CASCADE children (tune_samples/tune_results/tune_writes, see db-schema’s migration), which SQLite removes automatically as part of the same statement.

Source

pub async fn delete_for_demo_session( pool: &SqlitePool, id: i64, demo_session_id: i64, ) -> DbResult<bool>

Trait Implementations§

Source§

impl Clone for TuneRunRow

Source§

fn clone(&self) -> TuneRunRow

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for TuneRunRow

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl PartialEq for TuneRunRow

Source§

fn eq(&self, other: &TuneRunRow) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl StructuralPartialEq for TuneRunRow

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DynClone for T
where T: Clone,

Source§

fn __clone_box(&self, _: Private) -> *mut ()

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more