Expand description
bhtune-cli — the headless adapter.
Builds the bhtune binary: a scriptable, no-GUI way to run an MRFT tune and inspect its
history, intended for scheduled/unattended use (cron, CI, batch tuning campaigns) as well
as interactive terminal use.
args— theclapderiveCli/Commanddefinitions and the wrapper enums adaptingbhtune-core’s domain enums toclap::ValueEnum(required by Rust’s orphan rule).config—CLI > env > TOML config file > platform defaultprecedence for the database path, opcda-bridge gateway address, default OPC server, and the user-supplied template catalog path (template-user-catalog).db— opens the database and seeds the built-in and (if configured) user-catalog DCS/PLC templates on every startup, and runs thehistory-retentionsweep if a policy is configured.retention— turnshistory-retention’s “N days” policy into a cutoff and a logged deletion sweep, shared bydb::open’s startup call,bhtune-server’s periodic timer, andbhtune history prune.driver— constructs the selectedDriverimplementation.timing— supplies live or fixed-step timestamps to the clock-free MRFT engine.commands— one module per subcommand family:tune/simulate,template,history,export,opc.output— the--output table|jsonformat shared byhistory list/history showandtune/simulate’s final summary, plus error formatting.logging—tracing/tracing-subscriberstructured logging (cli-logging), initialized once inrun, never touching stdout so it can never interleave with--output json’s single-object contract.
main.rs stays a one-line delegator to run; [run_with_cli] is the actual entry
point, kept separate so tests can exercise it against an already-parsed args::Cli
without needing to control std::env::args() — mirroring opcda-bridge-client’s
run/run_with_cli split. Logging is initialized in run, not [run_with_cli], for
the same reason: it keeps tracing setup (and its process-global, only-succeeds-once
subscriber installation) entirely out of run_with_cli’s own large, injection-based test
suite – see logging’s test module doc comment.
Non-interactive/scheduled use (cron, CI, batch campaigns) is tune/simulate’s
--yes/--write-pid <level> flags (bypassing the interactive write-back prompt) plus
the global [tuning] timing configuration, which mandatorily bounds unattended runs and
caps individual driver operations, and this module’s distinguished exit codes
(EXIT_ABORTED, EXIT_TIMED_OUT,
EXIT_POOR_QUALITY, EXIT_ACTUATION_FAILED, EXIT_WRITE_BACK_FAILED,
EXIT_RESTORE_INCOMPLETE), so a
scheduler can tell “aborted”, “timed out”, “the plant data couldn’t be trusted”, “test ran
but the write-back failed”, “the loop may not have been fully restored”, and “never ran at
all” apart without parsing stdout. See AGENTS.md’s cli-automation/cli-safety sections.
Modules§
- args
- Command-line argument definitions (
clapderive) and the small wrapper enums that adaptbhtune-core’s domain enums toclap::ValueEnum. - cancel
- A single, process-wide Ctrl+C listener shared by every await point in a tune, replacing
the pre-
safety-cancellationdesign of constructingtokio::signal::ctrl_c()fresh on every polling-loop iteration (see AGENTS.md’ssafety-cancellation). Registering the signal exactly once, as early in the process as possible, closes the gap where a Ctrl+C delivered while no listener happens to be alive is silently swallowed – tokio installs a process-wideSIGINThandler the first timectrl_c()is polled and never reverts to the OS default, so a lost signal isn’t merely unhandled, it’s gone. - commands
- One module per subcommand family.
- config
- Global bhtune configuration, including the shared
[tuning]timing policy and theCLI flag > env var > TOML config file > built-in defaultprecedence used by settings that expose command-line or environment overrides. - db
- Opens the CLI’s database, seeds the built-in and user-catalog DCS/PLC templates, and runs
the
history-retentionsweep – all on every startup. - driver
- Constructs the selected
bhtune_driver::Driverimplementation from aTuneArgs. - logging
- Structured logging (
cli-logging), matchingopcda-bridge-gateway’s owntracingstack andlog.*configuration conventions (level/directory/format/rotation, resolved with the sameCLI flag > env var > config file > defaultprecedence as every other bhtune setting) – seecrate::config::LogConfig/resolve_log_settings. - output
- Output format selection for the handful of commands that support
--output json(history list/history showandtune/simulate’s final summary line — see AGENTS.md’scli-automationsection for why exactly these three). - retention
history-retention: age-based deletion of old tune runs.
Constants§
- EXIT_
ABORTED - A
tune/simulaterun was aborted (Ctrl+C) before it finished; the loop was restored to its pre-test mode. Distinct fromEXIT_FAILUREso a scheduler can tell “someone intentionally stopped this” apart from “this broke”. - EXIT_
ACTUATION_ FAILED - A live OPC DA tune was aborted because an accepted MV command could not be confirmed at
the controller before its deadline or before a replacement relay command was required.
The ordinary restore path still ran;
EXIT_RESTORE_INCOMPLETEtakes precedence if that restore could not itself be confirmed. - EXIT_
FAILURE - A setup problem (bad flags, an unreadable config file, a database error, an unexpected
driver error) prevented the command from running to completion at all. Equal to
ExitCode::FAILURE. - EXIT_
POOR_ QUALITY - A
tune/simulaterun was aborted because a driver reported a non-GoodOPC quality for a tuning-critical reading (finding 5 of the live-plant safety review): an initial reading, the transition-to-manual setpoint capture, or an in-flight PV poll sample, and (for the in-flight case) the global Config > OPC quality policy rejectedUncertain, or the quality wasBadrather than merelyUncertain. The loop was restored to its pre-test mode, exactly likeEXIT_ABORTED/EXIT_TIMED_OUT. Distinct from both so a scheduler’s alerting can tell “the plant data itself couldn’t be trusted” apart from a user-initiated stop or a run that simply took too long. Seecommands::tune::TuneOutcome::PoorQualityand AGENTS.md’ssafety-qualitysection. - EXIT_
RESTORE_ INCOMPLETE - A
tune/simulaterun ended (via normal completion, Ctrl+C, or a timeout) without being able to confirm the loop was fully restored to its pre-test mode/MV/setpoint – either a second Ctrl+C was received while the restore was in flight, or[tuning].restore_timeout_secselapsed first. Distinct from every other exit code because it means the loop may have been left mutated with no further attempt made to fix it: an operator must check it by hand, using the tag/value named in the warning printed to stderr. Seecommands::tune::TuneOutcome::RestoreIncompleteand AGENTS.md’ssafety-cancellationsection. - EXIT_
SUCCESS - Process exited normally, and if this was
tune/simulate, any PID write-back either succeeded or was cleanly skipped. Equal toExitCode::SUCCESS. - EXIT_
TIMED_ OUT - A
tune/simulaterun was aborted because[tuning].timeout_secselapsed before the engine reported completion; the loop was restored to its pre-test mode, exactly likeEXIT_ABORTED. Distinct from it so a scheduler’s alerting can tell “this run had to be killed for running too long” (possibly a stuck relay, a misconfigured tag mapping, or a stalled driver read – worth investigating) apart from “an operator stopped it on purpose” (routine). Seecommands::tune::TuneOutcome::TimedOutand AGENTS.md’scli-safetysection. - EXIT_
WRITE_ BACK_ FAILED - A
tune/simulaterun completed the MRFT test itself, but writing the selected PID constants back to the DCS failed (the write was rejected, errored, or its confirmation readback didn’t match). Distinct from bothEXIT_SUCCESS(nothing to report) andEXIT_FAILURE(the test itself never produced a result) so an unattended--write-pid/--yesrun can tell “the test ran fine but the loop was NOT updated” apart from either of those. Seecommands::tune::TuneOutcomeand AGENTS.md’scli-automationsection.
Functions§
- run
- Parses real CLI arguments, initializes structured logging, and runs, returning a process exit code.