///|
fn csv_escape(value : String) -> String {
let chars = ['"']
for character in value.to_array() {
if character == '"' {
chars.push('"')
}
chars.push(character)
}
chars.push('"')
String::from_array(chars)
}
///|
fn csv_text(value : String) -> String {
if value.contains(",") || value.contains("\"") || value.contains("\n") {
csv_escape(value)
} else {
value
}
}
///|
/// Export features using the documented feature CSV contract.
pub fn export_features_csv(features : Array[Feature]) -> String {
let mut output = "id,kind,nominal,lower,upper,datum\n"
for feature in features {
output = output +
csv_text(feature.id) +
"," +
feature_kind_name(feature.kind) +
"," +
feature.nominal.to_string() +
"," +
feature.lower.to_string() +
"," +
feature.upper.to_string() +
"," +
feature.datum.to_string() +
"\n"
}
output
}
///|
/// Export observations with their sample counts.
pub fn export_observations_csv(observations : Array[Observation]) -> String {
let mut output = "feature_id,value,sample_count\n"
for observation in observations {
output = output +
csv_text(observation.feature_id) +
"," +
observation.value.to_string() +
"," +
observation.sample_count.to_string() +
"\n"
}
output
}
///|
/// Export diagnostics for machine review.
pub fn export_diagnostics_csv(diagnostics : Array[Diagnostic]) -> String {
let mut output = "severity,code,message,index\n"
for diagnostic in diagnostics {
output = output +
severity_text(diagnostic.severity) +
"," +
csv_text(diagnostic.code) +
"," +
csv_text(diagnostic.message) +
"," +
diagnostic.index.to_string() +
"\n"
}
output
}
///|
/// Export plan steps with stable strategy and uncertainty fields.
pub fn export_plan_csv(plan : MeasurementPlan) -> String {
let mut output = "part_id,feature_id,device_id,strategy,direction,uncertainty\n"
for step in plan.steps {
output = output +
csv_text(plan.part_id) +
"," +
csv_text(step.feature_id) +
"," +
csv_text(step.device_id) +
"," +
strategy_text(step.strategy) +
"," +
step.direction +
"," +
step.uncertainty.to_string() +
"\n"
}
output
}
///|
/// Export one batch summary as a single stable record.
pub fn export_batch_summary_csv(summary : BatchSummary) -> String {
"feature_id,count,pass_count,fail_count,unmeasured_count,mean,range,stddev,defect_rate,cp,cpk\n" +
csv_text(summary.feature_id) +
"," +
summary.count.to_string() +
"," +
summary.pass_count.to_string() +
"," +
summary.fail_count.to_string() +
"," +
summary.unmeasured_count.to_string() +
"," +
summary.mean.to_string() +
"," +
summary.range.to_string() +
"," +
summary.standard_deviation.to_string() +
"," +
summary.defect_rate.to_string() +
"," +
summary.capability.cp.to_string() +
"," +
summary.capability.cpk.to_string() +
"\n"
}
///|
/// Export a trace with lifecycle and step fields.
pub fn export_trace_csv(log : TraceLog) -> String {
let mut output = "part_id,event_kind,feature_id,step_index,message\n"
for event in log.events {
output = output +
csv_text(event.part_id) +
"," +
trace_event_kind_text(event.kind) +
"," +
csv_text(event.feature_id) +
"," +
event.step_index.to_string() +
"," +
csv_text(event.message) +
"\n"
}
output
}
///|
/// Return a stable label for a trace event kind.
pub fn trace_event_kind_text(kind : TraceEventKind) -> String {
match kind {
PlanStarted => "plan-started"
PlanStep => "plan-step"
ObservationRecorded => "observation-recorded"
DecisionIssued => "decision-issued"
WarningIssued => "warning-issued"
PlanFinished => "plan-finished"
}
}