pub struct TuneRunRow {Show 27 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 gateway_compatibility_json: Option<String>,
pub restore_status: Option<RestoreStatus>,
pub restore_detail: Option<String>,
pub recovery_state: Option<TuneRecoveryState>,
pub recovery_evidence_json: 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: LoopConfigSnapshot 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: TemplateOriginWhere the snapshotted template below came from (see TemplateOrigin).
template: DcsTemplateSnapshot 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. 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).
Snapshot of the resolved [LoopTags] this run actually used, deserialized from
tags_json.
request_json: StringThe 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 the bhtune/bhtune-server adapters, not
bhtune-db. "{}" for any run started before
TuneRunRow::record_connection is called. It supports last-run prefill 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: boolWhether this run permitted Quality::Uncertain OPC readings under the global
allow_uncertain_quality policy (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.
gateway_compatibility_json: Option<String>Observed opcda-bridge compatibility report, stored as the driver’s JSON snapshot.
None for non-OPC runs, runs started before the snapshot existed, or until
TuneRunRow::record_gateway_compatibility is called. Kept as raw JSON because the
report type lives in bhtune-driver, which this crate does not depend on.
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.
recovery_state: Option<TuneRecoveryState>Explicit recovery lifecycle state; absent for pre-recovery runs and ordinary terminal runs that never became eligible for restore-loop recovery.
recovery_evidence_json: Option<String>Structured evidence supporting the recovery state; never inferred from error text.
created_at: DateTime<Utc>Implementations§
Source§impl TuneRunRow
impl TuneRunRow
Sourcepub 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>
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, so a historical run stays interpretable even after the template catalog
changes underneath it. Serializing template/tags returns DbError::Serialize
if the JSON encoder rejects a value. Both are plain derived structures; a serialization
failure indicates a persistence contract regression rather than a plant condition.
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>
Sourcepub 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>
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.
pub async fn get_for_demo_session( pool: &SqlitePool, run_id: i64, demo_session_id: i64, ) -> DbResult<Option<TuneRunRow>>
pub async fn list_for_demo_session( pool: &SqlitePool, demo_session_id: i64, pagination: Pagination, ) -> DbResult<Vec<TuneRunRow>>
pub async fn newest_for_demo_session( pool: &SqlitePool, demo_session_id: i64, ) -> DbResult<Option<TuneRunRow>>
pub async fn count_for_demo_session( pool: &SqlitePool, demo_session_id: i64, ) -> DbResult<i64>
Sourcepub async fn count_demo_owned(pool: &SqlitePool) -> DbResult<i64>
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.
Sourcepub async fn prune_terminal_for_demo_session(
pool: &SqlitePool,
demo_session_id: i64,
retain: u32,
) -> DbResult<u64>
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.
Sourcepub async fn prune_terminal_demo_owned(
pool: &SqlitePool,
retain: u32,
) -> DbResult<u64>
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.
pub async fn count_rows_for_demo_session( pool: &SqlitePool, demo_session_id: i64, ) -> DbResult<i64>
Sourcepub async fn record_connection(
pool: &SqlitePool,
run_id: i64,
opc_server: Option<&str>,
bridge_host: Option<&str>,
request_json: &str,
) -> DbResult<TuneRunRow>
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:
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.
Recorded after start() so callers that do not have connection provenance can still
create a run without extra arguments. The production caller stores 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).
Sourcepub async fn record_effective_tuning(
pool: &SqlitePool,
run_id: i64,
effective_tuning: EffectiveTuning,
) -> DbResult<TuneRunRow>
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.
Sourcepub async fn record_gateway_compatibility(
pool: &SqlitePool,
run_id: i64,
gateway_compatibility_json: &str,
) -> DbResult<TuneRunRow>
pub async fn record_gateway_compatibility( pool: &SqlitePool, run_id: i64, gateway_compatibility_json: &str, ) -> DbResult<TuneRunRow>
Records the observed opcda-bridge compatibility snapshot for a live OPC DA run.
gateway_compatibility_json must already be valid JSON. This is a follow-up update
rather than another Self::start parameter so existing repository callers remain
source-compatible.
§Errors
Returns DbError::Query when the row does not exist or the JSON fails the column
check.
Sourcepub async fn update_notes(
pool: &SqlitePool,
run_id: i64,
notes: Option<&str>,
) -> DbResult<TuneRunRow>
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.
Sourcepub async fn record_initial_readings(
pool: &SqlitePool,
run_id: i64,
readings: TuneRunInitialReadings,
) -> DbResult<TuneRunRow>
pub async fn record_initial_readings( pool: &SqlitePool, run_id: i64, readings: TuneRunInitialReadings, ) -> DbResult<TuneRunRow>
Records the driver’s initial-readings snapshot for an already-started run. Called at
most once per run, right after that read succeeds and before transition_to_manual’s
first mutating write. This durably preserves mode_raw/mode_attribute_raw/
setpoint_ini so a crashed run can 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.
Sourcepub async fn record_allow_uncertain_quality(
pool: &SqlitePool,
run_id: i64,
allow_uncertain_quality: bool,
) -> DbResult<TuneRunRow>
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. This is a separate post-start() update because the resolved
policy is available to the orchestration layer, not to every repository caller. The
column defaults to 0/false (see the migration), and the production caller stores
the resolved value after start() succeeds.
Sourcepub async fn record_timing_metrics(
pool: &SqlitePool,
run_id: i64,
metrics: TimingMetrics,
) -> DbResult<TuneRunRow>
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.
Sourcepub async fn record_restore_status(
pool: &SqlitePool,
run_id: i64,
status: RestoreStatus,
detail: Option<&str>,
) -> DbResult<TuneRunRow>
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.
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.
Sourcepub async fn complete(
pool: &SqlitePool,
run_id: i64,
completed_at: DateTime<Utc>,
) -> DbResult<TuneRunRow>
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.
Sourcepub async fn complete_with_timing_metrics(
pool: &SqlitePool,
run_id: i64,
completed_at: DateTime<Utc>,
timing_metrics: Option<TimingMetrics>,
) -> DbResult<TuneRunRow>
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.
Sourcepub async fn fail(
pool: &SqlitePool,
run_id: i64,
completed_at: DateTime<Utc>,
failure_reason: &str,
) -> DbResult<TuneRunRow>
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.
Sourcepub async fn abort(
pool: &SqlitePool,
run_id: i64,
completed_at: DateTime<Utc>,
) -> DbResult<TuneRunRow>
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 by the configured run
timeout rather than failing on its own.
Sourcepub async fn abort_with_timing_metrics(
pool: &SqlitePool,
run_id: i64,
completed_at: DateTime<Utc>,
timing_metrics: Option<TimingMetrics>,
) -> DbResult<TuneRunRow>
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.
Sourcepub 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>
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.
Sourcepub async fn get(pool: &SqlitePool, id: i64) -> DbResult<Option<TuneRunRow>>
pub async fn get(pool: &SqlitePool, id: i64) -> DbResult<Option<TuneRunRow>>
Fetches one row by id, or None if it doesn’t exist.
Sourcepub async fn list(
pool: &SqlitePool,
filter: &TuneRunFilter,
pagination: Pagination,
) -> DbResult<Vec<TuneRunRow>>
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.
Sourcepub async fn count(pool: &SqlitePool, filter: &TuneRunFilter) -> DbResult<i64>
pub async fn count(pool: &SqlitePool, filter: &TuneRunFilter) -> DbResult<i64>
Counts every run matching filter, ignoring pagination — the total Self::list
would page through.
Sourcepub async fn count_deletable_matching(
pool: &SqlitePool,
filter: &TuneRunFilter,
) -> DbResult<i64>
pub async fn count_deletable_matching( pool: &SqlitePool, filter: &TuneRunFilter, ) -> DbResult<i64>
Counts runs matching a deletion filter while preserving runs with an unresolved live recovery. This is the count used by retention previews so they match the actual sweep.
Sourcepub async fn delete_matching(
pool: &SqlitePool,
filter: &TuneRunFilter,
) -> DbResult<u64>
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 in the schema remove each deleted run’s samples,
results, and write-back audit rows automatically.
Runs with recovery state eligible, running, or incomplete are retained until a
recovery is confirmed. count_deletable_matching applies the same protection for
retention previews.
An empty filter (every field None) matches every deletable run in the table.
Callers that mean to scope a deletion must build a filter that says so explicitly;
unresolved live-recovery rows remain protected even when the filter is empty.
Sourcepub async fn delete(pool: &SqlitePool, id: i64) -> DbResult<bool>
pub async fn delete(pool: &SqlitePool, id: i64) -> DbResult<bool>
Deletes exactly one run by id, as used by the browser history page’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, ordinary run children are removed by ON DELETE CASCADE.
An unresolved live recovery state is excluded atomically so a concurrent delete cannot
release the recovery claim.
pub async fn delete_for_demo_session( pool: &SqlitePool, id: i64, demo_session_id: i64, ) -> DbResult<bool>
Trait Implementations§
Source§impl Clone for TuneRunRow
impl Clone for TuneRunRow
Source§fn clone(&self) -> TuneRunRow
fn clone(&self) -> TuneRunRow
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for TuneRunRow
impl Debug for TuneRunRow
Source§impl PartialEq for TuneRunRow
impl PartialEq for TuneRunRow
Source§impl Serialize for TuneRunRow
impl Serialize for TuneRunRow
impl StructuralPartialEq for TuneRunRow
Auto Trait Implementations§
impl Freeze for TuneRunRow
impl RefUnwindSafe for TuneRunRow
impl Send for TuneRunRow
impl Sync for TuneRunRow
impl Unpin for TuneRunRow
impl UnsafeUnpin for TuneRunRow
impl UnwindSafe for TuneRunRow
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
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 moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
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