Skip to main content

brep_cam/autoprogram/
report.rs

1//! The report for the UI (lane C13 row 4): what was recognised, what was
2//! programmed with which operations and tools, what was skipped and why,
3//! and how each generated operation ended.
4
5use 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    /// Operations programmed for the feature, in program order.
21    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    /// `planned`, `generated` or `failed`.
34    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    /// Per-face accounting (see `features::accounting`); `None` is unknown.
52    #[serde(default)]
53    pub face_accounting: Option<Vec<crate::features::FaceAccount>>,
54    pub generated: u32,
55    pub failed: u32,
56}
57
58/// Assemble the report; `generated` may be empty (a plan not yet generated).
59pub 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}