Deterministic cold-chain temperature excursion analysis and replay engine
项目边界:ThermoTrail 解决冷链和时序传感器数据问题。MoonBit 只是实现语言;本项目不是 MoonBit 编译器、LSP、格式化器、包管理器、IDE 插件或其他 MoonBit 工具链组件。
moon check --deny-warn
moon test
moon build --target native --release
moon run cmd/main -- version
moon run cmd/main -- simulate door-open summary
moon run cmd/main -- simulate cooling-failure json
moon run cmd/main -- demo markdown
moon run examples/embeddedmoon run cmd/main -- list-scenarioslet batch = @thermo.parse_readings_csv(csv_text)
let request = @thermo.analysis_request(
"shipment-001",
"logger-export.csv",
1785916800L,
batch.readings,
)
let report = @thermo.analyze({ ..request, diagnostics: batch.diagnostics })
println(@thermo.report_to_markdown(report))let report = @thermo.analyze_csv(
"shipment-001",
"logger-export.csv",
1785916800L,
csv_text,
)timestamp,sensor_id,temperature_c,humidity_percent,battery_percent,status
2026-08-05T10:00:00Z,S-001,4.2,61.0,92,okmoon fmt
moon check --deny-warn
moon test
moon build --target native --release
moon infomoon login
moon check --deny-warn
moon test
moon package --list
moon publishpub(all) struct AnalysisConfig {
policy : TemperaturePolicy
profiles : Array[SensorProfile]
default_profile : SensorProfile
merge_duplicate_average : Bool
interpolate_short_gaps : Bool
interpolation_limit_seconds : Int64
conflict_threshold_c : Double
include_flagged_in_metrics : Bool
} derive(Eq, Debug)pub(all) struct AnalysisReport {
schema : String
shipment_id : String
generated_at : Int64
source_name : String
sensor_ids : Array[String]
readings : Array[Reading]
gaps : Array[SamplingGap]
events : Array[ExcursionEvent]
statistics : Array[SensorStatistics]
health : Array[SensorHealth]
risk : RiskAssessment
diagnostics : Array[Diagnostic]
} derive(Eq, Debug)pub(all) struct AnalysisRequest {
shipment_id : String
source_name : String
generated_at : Int64
readings : Array[Reading]
diagnostics : Array[Diagnostic]
config : AnalysisConfig
} derive(Eq, Debug)pub(all) struct Diagnostic {
level : DiagnosticLevel
code : String
message : String
line : Int?
sensor_id : String?
} derive(Eq, Debug)pub(all) struct ExcursionEvent {
event_id : String
sensor_id : String
kind : ExcursionKind
status : EventStatus
started_at : Int64
ended_at : Int64
duration_seconds : Int64
sample_count : Int
minimum_c : Double?
maximum_c : Double?
mean_c : Double?
degree_seconds : Double
peak_deviation_c : Double
reason : String
} derive(Eq, Debug)pub(all) struct NormalizationResult {
readings : Array[Reading]
gaps : Array[SamplingGap]
diagnostics : Array[Diagnostic]
} derive(Eq, Debug)pub(all) struct Reading {
timestamp : Int64
sensor_id : String
temperature_c : Double
humidity_percent : Double?
battery_percent : Double?
status : String
origin : SampleOrigin
flags : Array[QualityFlag]
source_line : Int?
} derive(Eq, Debug)pub(all) struct SensorProfile {
sensor_id : String
calibration_offset_c : Double
physical_min_c : Double
physical_max_c : Double
expected_interval_seconds : Int64
stuck_tolerance_c : Double
stuck_minimum_samples : Int
noise_step_c : Double
drift_window_samples : Int
drift_threshold_c : Double
} derive(Eq, Debug)pub(all) struct SensorQualityResult {
readings : Array[Reading]
health : Array[SensorHealth]
diagnostics : Array[Diagnostic]
} derive(Eq, Debug)pub(all) struct SensorStatistics {
sensor_id : String
sample_count : Int
usable_count : Int
first_timestamp : Int64?
last_timestamp : Int64?
minimum_c : Double?
maximum_c : Double?
mean_c : Double?
time_weighted_mean_c : Double?
mean_kinetic_temperature_c : Double?
standard_deviation_c : Double?
median_c : Double?
p05_c : Double?
p95_c : Double?
in_range_seconds : Int64
low_seconds : Int64
high_seconds : Int64
unknown_seconds : Int64
low_degree_seconds : Double
high_degree_seconds : Double
} derive(Eq, Debug)pub(all) struct SimulationConfig {
scenario : SimulationScenario
sensor_ids : Array[String]
started_at : Int64
duration_seconds : Int64
interval_seconds : Int64
baseline_c : Double
ambient_c : Double
noise_amplitude_c : Double
humidity_percent : Double
battery_percent : Double
event_start_seconds : Int64
event_duration_seconds : Int64
seed : Int64
} derive(Eq, Debug)pub(all) struct SimulationResult {
config : SimulationConfig
readings : Array[Reading]
description : String
diagnostics : Array[Diagnostic]
} derive(Eq, Debug)fn abs_double(value : Double) -> Doublefn analysis_request(shipment_id : String, source_name : String, generated_at : Int64, readings : Array[Reading]) -> AnalysisRequestfn analyze_csv(shipment_id : String, source_name : String, generated_at : Int64, input : String, config? : AnalysisConfig) -> AnalysisReportfn analyze_sensor_quality(input : Array[Reading], gaps : Array[SamplingGap], config : AnalysisConfig) -> SensorQualityResultfn assess_risk(events : Array[ExcursionEvent], gaps : Array[SamplingGap], diagnostics : Array[Diagnostic], health : Array[SensorHealth]) -> RiskAssessmentfn build_event_timeline(readings : Array[Reading], gaps : Array[SamplingGap], policy : TemperaturePolicy) -> Array[ExcursionEvent]fn civil_from_days(days : Int64) -> (Int, Int, Int)fn clamp_double(value : Double, minimum : Double, maximum : Double) -> Doublefn clamp_int(value : Int, minimum : Int, maximum : Int) -> Intfn compute_all_statistics(readings : Array[Reading], policy : TemperaturePolicy) -> Array[SensorStatistics]fn compute_sensor_statistics(sensor_id : String, input : Array[Reading], policy : TemperaturePolicy) -> SensorStatisticsfn compute_windows(sensor_id : String, input : Array[Reading], policy : TemperaturePolicy, width_seconds : Int64) -> Array[WindowStatistic]fn days_from_civil(year : Int, month : Int, day : Int) -> Int64fn days_in_month(year : Int, month : Int) -> Intfn detect_excursions(readings : Array[Reading], policy : TemperaturePolicy) -> Array[ExcursionEvent]fn detect_sensor_excursions(readings : Array[Reading], policy : TemperaturePolicy) -> Array[ExcursionEvent]fn empty_excursion(event_id : String, sensor_id : String, kind : ExcursionKind, started_at : Int64) -> ExcursionEventfn format_duration(seconds : Int64) -> Stringfn format_iso8601(unix_seconds : Int64) -> Stringfn json_escape(value : String) -> Stringfn parse_decimal(text : String) -> Double?fn unix_seconds_from_parts(year : Int, month : Int, day : Int, hour : Int, minute : Int, second : Int, offset_seconds : Int) -> Int64Deterministic cold-chain temperature excursion analysis and replay engine