1#![doc = include_str!("../README.md")]
3
4pub mod emit;
5mod file;
6mod parsers;
7mod tests;
8
9#[cfg(feature = "serde")]
10use serde::{Deserialize, Serialize};
11
12use microns::Microns;
13use std::{io::Write, path::Path};
14#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
16#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]
17pub enum Tag {
18 Retraction,
19 DeRetraction,
20 Travel,
21 RaiseZ,
22 LowerZ,
23 Wipe,
24 Extrusion,
25 Feedrate,
26 #[default]
27 Uninitialized,
28}
29
30#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
32#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
33pub struct G1 {
34 pub x: Option<Microns>,
35 pub y: Option<Microns>,
36 pub z: Option<Microns>,
37 pub e: Option<Microns>,
38 pub f: Option<Microns>,
39 pub tag: Tag,
40}
41
42#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
47#[derive(Clone, Debug, PartialEq, Eq, Hash)]
48pub enum Command {
49 G1(G1),
50 G90,
51 G91,
52 M82,
53 M83,
54 Raw(String),
55}
56
57impl Command {
58 pub fn tag(&self) -> Tag {
59 match self {
60 Command::G1(g1) => g1.tag,
61 _ => Tag::Uninitialized,
62 }
63 }
64}
65
66#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
69#[derive(Clone, Debug, PartialEq, Eq, Hash)]
70pub struct GCodeLine {
71 pub id: Id,
72 pub command: Command,
73 pub comments: String,
74}
75
76#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
82#[derive(Clone, Debug, Default, PartialEq, Eq)]
83pub struct GCodeModel {
84 pub lines: Vec<GCodeLine>, pub rel_xyz: bool,
86 pub rel_e: bool,
87 pub id_counter: Counter,
88}
89
90impl std::str::FromStr for GCodeModel {
91 type Err = parsers::GCodeParseError;
92 fn from_str(mut s: &str) -> Result<Self, Self::Err> {
93 let gcode = parsers::gcode_parser(&mut s);
94 match gcode {
95 Ok(gcode) => Ok(gcode),
96 Err(e) => Err(e),
97 }
98 }
99}
100
101impl GCodeModel {
102 pub fn from_file(path: &Path) -> Result<Self, Box<dyn std::error::Error>> {
103 Ok(file::open_gcode_file(path)?.parse()?)
104 }
105 pub fn write_to_file(&self, path: &Path) -> Result<(), std::io::Error> {
106 use emit::Emit;
107 use std::fs::File;
108 let out = self.emit(false);
109 let mut f = File::create(path)?;
110 f.write_all(out.as_bytes())?;
111 println!("save successful");
112 Ok(())
113 }
114 pub fn tag_g1(&mut self) {
115 let mut prev = [
116 Microns::ZERO,
117 Microns::ZERO,
118 Microns::ZERO,
119 ];
120 for line in self.lines.iter_mut() {
121 if let Command::G1(G1 { x, y, z, e, f, tag }) = &mut line.command {
122 let curr = [
123 prev[0] + x.unwrap_or(Microns::ZERO),
124 prev[1] + y.unwrap_or(Microns::ZERO),
125 prev[2] + z.unwrap_or(Microns::ZERO),
126 ];
127
128 let dx = curr[0] - prev[0];
129 let dy = curr[1] - prev[1];
130 let dz = curr[2] - prev[2];
131 let de = e.unwrap_or(Microns::ZERO);
132 let f = f.unwrap_or(Microns::ZERO);
133
134 *tag = {
135 if de > Microns::ZERO {
136 if dx.abs() > Microns::ZERO || dy.abs() > Microns::ZERO {
137 Tag::Extrusion
138 } else { Tag::DeRetraction }
139 } else if de == Microns::ZERO {
140 if dx.abs() > Microns::ZERO || dy.abs() > Microns::ZERO {
141 Tag::Travel
142 } else if dz > Microns::ZERO {
143 Tag::RaiseZ
144 } else if dz < Microns::ZERO {
145 Tag::LowerZ
146 } else if f > Microns::ZERO {
147 Tag::Feedrate
148 } else { Tag::Uninitialized }
149 } else if dx.abs() > Microns::ZERO || dy.abs() > Microns::ZERO {
150 Tag::Wipe
151 } else {
152 Tag::Retraction
153 }
154 };
155 prev = curr;
156 }
157 }
158 }
159}
160
161#[test]
162fn tag_test() {
163 let mut gcode = GCodeModel::default();
164 gcode.lines.push(GCodeLine {
165 id: gcode.id_counter.get(),
166 command: Command::G1(G1 {
167 x: Some(Microns::from(10.0)),
168 y: Some(Microns::from(10.0)),
169 z: Some(Microns::from(10.0)),
170 e: Some(Microns::from(10.0)),
171 f: Some(Microns::from(10.0)),
172 tag: Tag::Uninitialized,
173 }),
174 comments: String::new(),
175 });
176 gcode.tag_g1();
177 assert_eq!(gcode.lines[0].command.tag(), Tag::Extrusion);
178 gcode.lines.push(GCodeLine {
179 id: gcode.id_counter.get(),
180 command: Command::G1(G1::default()),
181 comments: String::new(),
182 });
183 gcode.tag_g1();
184 assert_eq!(gcode.lines[1].command.tag(), Tag::Uninitialized);
185 gcode.lines.push(GCodeLine {
186 id: gcode.id_counter.get(),
187 command: Command::G1(G1 {
188 e: Some(Microns::from(-10.0)),
189 ..Default::default()
190 }),
191 comments: String::new(),
192 });
193 gcode.tag_g1();
194 assert_eq!(gcode.lines[2].command.tag(), Tag::Retraction);
195 gcode.lines.push(GCodeLine {
196 id: gcode.id_counter.get(),
197 command: Command::G1(G1 {
198 e: Some(Microns::from(-10.0)),
199 x: Some(Microns::from(10.0)),
200 y: Some(Microns::from(10.0)),
201 ..Default::default()
202 }),
203 comments: String::new(),
204 });
205 gcode.tag_g1();
206 assert_eq!(gcode.lines[3].command.tag(), Tag::Wipe);
207 gcode.lines.push(GCodeLine {
208 id: gcode.id_counter.get(),
209 command: Command::G1(G1 {
210 e: Some(Microns::from(-10.0)),
211 z: Some(Microns::from(10.0)),
212 ..Default::default()
213 }),
214 comments: String::new(),
215 });
216 gcode.tag_g1();
217 assert_eq!(gcode.lines[4].command.tag(), Tag::Retraction);
218 gcode.lines.push(GCodeLine {
219 id: gcode.id_counter.get(),
220 command: Command::G1(G1 {
221 e: Some(Microns::from(-10.0)),
222 z: Some(Microns::from(-10.0)),
223 ..Default::default()
224 }),
225 comments: String::new(),
226 });
227 gcode.tag_g1();
228 assert_eq!(gcode.lines[5].command.tag(), Tag::Retraction);
229 gcode.lines.push(GCodeLine {
230 id: gcode.id_counter.get(),
231 command: Command::G1(G1 {
232 f: Some(Microns::from(10.0)),
233 ..Default::default()
234 }),
235 comments: String::new(),
236 });
237 gcode.tag_g1();
238 assert_eq!(gcode.lines[6].command.tag(), Tag::Feedrate);
239 gcode.lines.push(GCodeLine {
240 id: gcode.id_counter.get(),
241 command: Command::G1(G1 {
242 e: Some(Microns::from(10.0)),
243 ..Default::default()
244 }),
245 comments: String::new(),
246 });
247 gcode.tag_g1();
248 assert_eq!(gcode.lines[7].command.tag(), Tag::DeRetraction);
249}
250#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
251#[derive(Clone, Debug, Default, PartialEq, Eq)]
252pub struct Counter {
253 count: u32,
254}
255
256impl Counter {
257 fn get(&mut self) -> Id {
258 let out = self.count;
259 self.count += 1;
260 Id(out)
261 }
262}
263#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
264#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default)]
265pub struct Id(u32);
266
267impl Id {
268 pub fn get(&self) -> u32 {
269 self.0
270 }
271}