taktwerk 0.8.0

Fixed-step model execution engine for Linux with its own OPC UA server
//! The `taktwerk` command: check, run, inspect and scaffold model projects, and monitor a
//! running engine.
//!
//! ```text
//! taktwerk new models/plant.fmu --kind fmi -o plant.toml
//! taktwerk check plant.toml
//! taktwerk run plant.toml
//! taktwerk tui opc.tcp://127.0.0.1:4840
//! taktwerk record opc.tcp://127.0.0.1:4840 --dir records
//! taktwerk campaign sweep.toml --write
//! taktwerk serve examples/campaign
//! taktwerk fmu-wrap models/plant-pkg --write
//! ```
//!
//! `run` serves the process image on the project's OPC UA server, stops on SIGINT or SIGTERM and
//! exits non-zero when a model or connector failed; `record` pulls a recorder of a running engine
//! into run files until SIGINT or SIGTERM. Logging goes to stderr and follows `RUST_LOG`
//! (default `info` for `run` and `record`, `warn` otherwise).

mod campaign;
mod cli;
mod fmu_wrap;
mod import;
mod inspect;
mod kinds;
mod record;
mod recording;
mod run;
mod scaffold;
#[cfg(feature = "http")]
mod serve;
mod setup;
mod summary;
mod tui;
mod ua;

use std::io::IsTerminal;
use std::process::ExitCode;

use clap::Parser;

fn main() -> ExitCode {
    let cli = cli::Cli::parse();
    match cli.command {
        cli::Command::Tui { .. } => {}
        cli::Command::Run { .. } | cli::Command::Record(_) | cli::Command::Serve { .. } => {
            init_logging("info");
        }
        _ => init_logging("warn"),
    }
    match cli::dispatch(cli.command) {
        Ok(code) => code,
        Err(e) => {
            eprintln!("error: {e:#}");
            ExitCode::FAILURE
        }
    }
}

/// Log to stderr, filtered by `RUST_LOG`, else at `level`; colours only on a terminal.
fn init_logging(level: &str) {
    // Without a secure endpoint the server has no certificate, which the OPC UA stack reports as
    // an error on every connection and a warning at start; keep the stack quiet otherwise.
    let default = format!(
        "{level},opcua_server=error,opcua_core=warn,opcua_crypto=warn,\
         opcua_crypto::certificate_store=off,opcua_core::comms::secure_channel=off"
    );
    let filter = tracing_subscriber::EnvFilter::try_from_default_env()
        .unwrap_or_else(|_| tracing_subscriber::EnvFilter::new(default));
    tracing_subscriber::fmt()
        .with_env_filter(filter)
        .with_writer(std::io::stderr)
        .with_ansi(std::io::stderr().is_terminal())
        .init();
}