///|
fn usage() -> String {
(
#|ThermoTrail - cold-chain temperature analysis and replay
#|
#|Usage:
#| thermo-trail help
#| thermo-trail version
#| thermo-trail list-scenarios
#| thermo-trail simulate [summary|json|markdown|events|readings]
#| thermo-trail demo [summary|json|markdown|events|readings]
#|
#|Scenarios:
#| stable, door-open, cooling-failure, freezer-exposure,
#| sensor-dropout, sensor-stuck, power-cycle, mixed-faults
#|
#|Examples:
#| moon run cmd/main -- simulate door-open summary
#| moon run cmd/main -- simulate cooling-failure json
#| moon run cmd/main -- demo markdown
#|
)
}
///|
fn scenario_list() -> String {
(
#|stable Stable cold-chain transit
#|door-open Temporary door opening and recovery
#|cooling-failure Progressive warming after refrigeration failure
#|freezer-exposure Accidental sub-zero exposure
#|sensor-dropout Missing logger samples
#|sensor-stuck Constant-value logger fault
#|power-cycle Explicit offline interval
#|mixed-faults Multiple faults in one transport
#|
)
}
///|
fn render_report(report : @thermo.AnalysisReport, format : String) -> String {
match format {
"summary" => @thermo.report_summary(report)
"json" => @thermo.report_to_json(report)
"markdown" | "md" => @thermo.report_to_markdown(report)
"events" | "event-csv" => @thermo.events_to_csv(report.events)
"readings" | "reading-csv" => @thermo.readings_to_csv(report.readings)
_ => "unknown output format: \{format}\n\n\{usage()}"
}
}
///|
fn run_simulation(arguments : Array[String]) -> String {
let scenario = if arguments.length() >= 2 { arguments[1] } else { "stable" }
let format = if arguments.length() >= 3 {
arguments[2].to_lower()
} else {
"summary"
}
match @thermo.parse_simulation_scenario(scenario) {
None => "unknown scenario: \{scenario}\n\n\{scenario_list()}"
Some(_) => render_report(@thermo.analyze_simulation(scenario), format)
}
}
///|
fn demo_csv() -> String {
(
#|timestamp,sensor_id,temperature_c,humidity_percent,battery_percent,status
#|2026-08-05T10:00:00Z,crate-a,4.2,61,94,ok
#|2026-08-05T10:05:00Z,crate-a,4.4,61,94,ok
#|2026-08-05T10:10:00Z,crate-a,9.1,62,93,ok
#|2026-08-05T10:15:00Z,crate-a,10.4,63,93,ok
#|2026-08-05T10:20:00Z,crate-a,9.7,64,93,ok
#|2026-08-05T10:25:00Z,crate-a,7.2,63,93,ok
#|2026-08-05T10:00:00Z,crate-b,4.1,60,88,ok
#|2026-08-05T10:05:00Z,crate-b,4.3,60,88,ok
#|2026-08-05T10:10:00Z,crate-b,8.9,61,88,ok
#|2026-08-05T10:15:00Z,crate-b,10.1,62,87,ok
#|2026-08-05T10:20:00Z,crate-b,9.5,63,87,ok
#|2026-08-05T10:25:00Z,crate-b,7.0,62,87,ok
#|
)
}
///|
fn run_demo(arguments : Array[String]) -> String {
let format = if arguments.length() >= 2 {
arguments[1].to_lower()
} else {
"summary"
}
let report = @thermo.analyze_csv(
"demo-shipment",
"embedded-demo.csv",
1785916800L,
demo_csv(),
)
render_report(report, format)
}
///|
fn main {
let raw_arguments = @env.args()
let arguments : Array[String] = []
for index = 1; index < raw_arguments.length(); index = index + 1 {
arguments.push(raw_arguments[index])
}
let command = if arguments.length() == 0 {
"help"
} else {
arguments[0].to_lower()
}
let output = match command {
"help" | "-h" | "--help" => usage()
"version" | "-v" | "--version" => "ThermoTrail 0.1.0\n"
"list-scenarios" | "scenarios" => scenario_list()
"simulate" => run_simulation(arguments)
"demo" => run_demo(arguments)
_ => "unknown command: \{command}\n\n\{usage()}"
}
println(output)
}