use serde::{Deserialize, Serialize};
use std::path::Path;
pub const ANALYSIS_SCHEMA_VERSION: u32 = 1;
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct AnalysisArtifact {
pub schema_version: u32,
pub source: AnalysisSource,
pub distributions: DistributionSet,
#[serde(default)]
pub demand_reliability: Option<StoredDemandReliability>,
#[serde(default)]
pub service_compliance: Option<StoredServiceCompliance>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct AnalysisSource {
pub output_file: String,
pub report_file: String,
pub period_count: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct DistributionSet {
pub pressure: ContinuousDistribution,
pub head: ContinuousDistribution,
pub flow: ContinuousDistribution,
pub velocity: ContinuousDistribution,
pub status: StatusDistribution,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ContinuousDistribution {
pub bins: Vec<HistogramBin>,
pub summary: SummaryStats,
pub thresholds: Option<ThresholdBreakdown>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct HistogramBin {
pub start: f64,
pub end: f64,
pub count: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct SummaryStats {
pub min: f64,
pub max: f64,
pub mean: f64,
pub p05: f64,
pub p25: f64,
pub p50: f64,
pub p75: f64,
pub p95: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ThresholdBreakdown {
pub below: u64,
pub within: u64,
pub above: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct StatusDistribution {
pub open: u64,
pub closed: u64,
pub active: u64,
pub other: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StoredDemandReliability {
pub demand_model: String,
pub period_count: usize,
pub reliability_ratio: f64,
pub required_volume: f64,
pub unmet_volume: f64,
pub surplus_volume: f64,
pub deficit_periods: usize,
pub max_node_deficit_rate: f64,
pub worst_node_id: Option<String>,
pub worst_node_reliability: Option<f64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StoredServiceCompliance {
pub period_count: usize,
pub node_count: usize,
pub compliance_ratio: f64,
pub below_min_samples: usize,
pub above_max_samples: usize,
pub pressure_deficit_integral: f64,
pub pressure_excess_integral: f64,
pub worst_below_min: f64,
pub worst_above_max: f64,
pub max_node_violation_ratio: f64,
pub min_pressure_threshold: f64,
pub max_pressure_threshold: Option<f64>,
}
pub fn read_analysis_bytes(path: &Path) -> Result<Vec<u8>, std::io::Error> {
std::fs::read(path)
}
pub fn write_analysis_bytes(path: &Path, bytes: &[u8]) -> Result<(), std::io::Error> {
if let Some(parent) = path.parent() {
std::fs::create_dir_all(parent)?;
}
std::fs::write(path, bytes)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn defaults_to_schema_v1() {
let artifact = AnalysisArtifact {
schema_version: ANALYSIS_SCHEMA_VERSION,
..AnalysisArtifact::default()
};
assert_eq!(artifact.schema_version, 1);
}
}