aoer_plotty_rs/gcode/
mod.rs1use geo_types::{CoordNum, MultiLineString};
3use tera::{Context, Tera};
4use std::error::Error;
5use num_traits::real::Real;
6
7pub enum AoerPostMachines {
9 BAPv1,
10 CustomMachine(Tera),
11}
12
13#[derive(Debug)]
14pub enum PostTemplateError {
15 NoSuchTemplateError,
16 TemplateStructureError,
17}
18
19#[derive(Debug,Clone)]
20pub enum PostGeometrySource<T>
21 where T: CoordNum, T: Real{
22 MultiLineString(MultiLineString<T>),
23}
24
25
26impl AoerPostMachines {
28 pub fn get_machine(machine: AoerPostMachines) -> Result<Tera, PostTemplateError> {
32 let mut bap_post_template = Tera::default();
33 match machine {
34 AoerPostMachines::BAPv1 => {
35 bap_post_template.add_raw_templates(vec![
36 ("prelude", "M280 S5\nG4 P150\nG28 X Y\nG90\n G92 X0 Y0 ; HOME"),
37 ("epilog", "M280 S5\nG4 P150\nG0 X0 Y230\nM281 ; FINISHED"),
38 ("penup", "M400\nM280 S9\nG4 P150\nM400\nM281 ; PENUP"),
39 ("pendown", "M400\nM280 S12\nG4 P250\nM400 ; PENDOWN"),
40 ("moveto", "G0 X{{xmm|round(precision=2)}} Y{{ymm|round(precision=2)}} ; NEW LINE START"),
41 ("lineto", "G01 F1200 X{{xmm|round(precision=2)}} Y{{ymm|round(precision=2)}}"),
42 ]).unwrap();
43 Ok(bap_post_template)
44 }
45 _ => Err(PostTemplateError::NoSuchTemplateError)
46 }
47 }
48}
49
50
51pub fn post<T>(lines: &PostGeometrySource<T>, post_template: &Tera)
55 -> Result<Vec<String>, Box<dyn Error>>
56 where T: CoordNum, T: Real {
57 let mut program: Vec<String> = Vec::new();
58 let lines = match lines{
62 PostGeometrySource::MultiLineString(lines) => lines
63 };
64 program.extend(
65 post_template.render("prelude", &Context::new())?
66 .split("\n").map(|s| s.to_string()));
67 for line in lines.iter() {
68 program.extend(post_template.render("penup", &Context::new())?
69 .split("\n")
70 .map(|s| s.to_string()));
71 let mut context = Context::new();
72 context.insert("xmm", &line[0].x.to_f64().unwrap());
73 context.insert("ymm", &line[0].y.to_f64().unwrap());
74 program.extend(
75 post_template.render("moveto", &context)?
76 .split("\n")
77 .map(|s| s.to_string()));
78
79 program.extend(post_template.render("pendown", &Context::new())?
80 .split("\n")
81 .map(|s| s.to_string()));
82 for point in line.points().skip(1) {
83 let mut context = Context::new();
84 context.insert("xmm", &point.x().to_f64().unwrap());
85 context.insert("ymm", &point.y().to_f64().unwrap());
86 program.extend(
87 post_template.render("lineto", &context)?
88 .split("\n").map(|s| s.to_string()));
89 }
90 }
91 program.extend(
92 post_template.render("epilog", &Context::new())?
93 .split("\n").map(|s| s.to_string()));
94 Ok(program)
95}
96
97
98#[cfg(test)]
99mod test {
100 use std::iter::zip;
101 use geo_types::{coord, LineString, MultiLineString};
102 use crate::gcode::{AoerPostMachines, post, PostGeometrySource};
103
104 #[test]
105 fn test_post() {
106 let post_template = AoerPostMachines::get_machine(AoerPostMachines::BAPv1)
107 .unwrap();
108 let lines = MultiLineString::new(vec![LineString::new(vec![
109 coord! {x: 0.0, y: 0.0},
110 coord! {x: 10.0, y: 0.0}])]);
111 let program = post(&PostGeometrySource::MultiLineString(lines), &post_template)
112 .unwrap();
113 let pairs: Vec<(String, String)> = zip(program, vec!["M280 S5", "G4 P150", "G28 X Y",
114 "G90", " G92 X0 Y0 ; HOME", "M400",
115 "M280 S9", "G4 P150", "M400",
116 "M281 ; PENUP",
117 "G0 X0 Y0 ; NEW LINE START", "M400",
118 "M280 S12", "G4 P250",
119 "M400 ; PENDOWN", "G01 F1200 X10 Y0",
120 "M280 S5", "G4 P150", "G0 X0 Y230",
121 "M281 ; FINISHED"].iter()
122 .map(|s| { s.to_string() })).collect();
123 for (left, right) in pairs {
124 assert!(left == right);
125 }
126 }
127}