1use super::{GeneratedOperation, Plan, Skipped};
6use crate::features::Recognized;
7use crate::model::{OperationId, ToolId};
8use serde::{Deserialize, Serialize};
9
10#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
11#[serde(rename_all = "camelCase")]
12pub struct FeatureLine {
13 pub id: String,
14 pub kind: String,
15 pub solid: String,
16 pub faces: Vec<String>,
17 pub machinable: bool,
18 #[serde(default, skip_serializing_if = "Option::is_none")]
19 pub reason: Option<String>,
20 pub operations: Vec<OperationId>,
22}
23
24#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
25#[serde(rename_all = "camelCase")]
26pub struct OperationLine {
27 pub id: OperationId,
28 pub name: String,
29 pub feature: String,
30 pub role: String,
31 pub strategy: String,
32 pub tool: ToolId,
33 pub status: String,
35 #[serde(default, skip_serializing_if = "Option::is_none")]
36 pub error: Option<String>,
37 pub motions: u64,
38 pub warnings: u64,
39 #[serde(default, skip_serializing_if = "Vec::is_empty")]
40 pub notes: Vec<String>,
41}
42
43#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
44#[serde(rename_all = "camelCase")]
45pub struct Report {
46 pub features: Vec<FeatureLine>,
47 pub operations: Vec<OperationLine>,
48 pub skipped: Vec<Skipped>,
49 pub tool_changes: u32,
50 pub unassigned_faces: Vec<(String, String)>,
51 #[serde(default)]
53 pub face_accounting: Option<Vec<crate::features::FaceAccount>>,
54 pub generated: u32,
55 pub failed: u32,
56}
57
58pub fn report(recognized: &Recognized, plan: &Plan, generated: &[GeneratedOperation]) -> Report {
60 let mut features = Vec::new();
61 for f in &recognized.features {
62 features.push(FeatureLine {
63 id: f.id.clone(),
64 kind: f.kind.name().to_string(),
65 solid: f.solid.clone(),
66 faces: f.faces.clone(),
67 machinable: f.machinable,
68 reason: f.reason.clone(),
69 operations: plan.operations.iter().filter(|p| p.feature == f.id).map(|p| p.operation.id.clone()).collect(),
70 });
71 }
72 let mut operations = Vec::new();
73 let (mut ok, mut failed) = (0u32, 0u32);
74 for p in &plan.operations {
75 let g = generated.iter().find(|g| g.operation == p.operation.id);
76 let (status, error, motions, warnings) = match g {
77 None => ("planned", None, 0, 0),
78 Some(GeneratedOperation { toolpath: Some(t), .. }) => {
79 ok += 1;
80 ("generated", None, t.motions.len() as u64, t.report.warnings.len() as u64)
81 }
82 Some(GeneratedOperation { error, .. }) => {
83 failed += 1;
84 ("failed", error.clone(), 0, 0)
85 }
86 };
87 operations.push(OperationLine {
88 id: p.operation.id.clone(),
89 name: p.operation.name.clone(),
90 feature: p.feature.clone(),
91 role: p.role.clone(),
92 strategy: p.operation.strategy.kind_name().to_string(),
93 tool: p.tool.clone(),
94 status: status.to_string(),
95 error,
96 motions,
97 warnings,
98 notes: p.notes.clone(),
99 });
100 }
101 Report {
102 features,
103 operations,
104 skipped: plan.skipped.clone(),
105 tool_changes: plan.tool_changes,
106 unassigned_faces: recognized.unassigned.clone(),
107 face_accounting: recognized.accounting.clone(),
108 generated: ok,
109 failed,
110 }
111}