cnccoder/
instructions.rs

1//! Representations of all supported G-code instructions/commands for cnccoder.
2//!
3//! This is the lowest level of structs in the crate. Even if they are publicly exposed,
4//! they are primarily intended to be used internally by the higher level [cuts](../cuts/index.html).
5
6use std::fmt::Write as _;
7use std::time::Duration;
8
9use crate::utils::round_precision;
10
11/// Rapid move
12#[derive(Debug, Clone, PartialEq)]
13pub struct G0 {
14    /// X Coordinate
15    pub x: Option<f64>,
16    /// Y Coordinate
17    pub y: Option<f64>,
18    /// Z Coordinate
19    pub z: Option<f64>,
20}
21
22impl G0 {
23    /// Generate G-code string
24    pub fn to_gcode(&self) -> String {
25        let mut command = "G0".to_string();
26
27        if let Some(x) = self.x {
28            let _ = write!(command, " X{}", round_precision(x));
29        }
30
31        if let Some(y) = self.y {
32            let _ = write!(command, " Y{}", round_precision(y));
33        }
34
35        if let Some(z) = self.z {
36            let _ = write!(command, " Z{}", round_precision(z));
37        }
38
39        command
40    }
41}
42
43/// Linear Move
44#[derive(Debug, Clone, PartialEq)]
45pub struct G1 {
46    /// X Coordinate
47    pub x: Option<f64>,
48    /// Y Coordinate
49    pub y: Option<f64>,
50    /// Z Coordinate
51    pub z: Option<f64>,
52    /// Feedrate
53    pub f: Option<f64>,
54}
55
56impl G1 {
57    /// Generate G-code string
58    pub fn to_gcode(&self) -> String {
59        let mut command = "G1".to_string();
60
61        if let Some(x) = self.x {
62            let _ = write!(command, " X{}", round_precision(x));
63        }
64
65        if let Some(y) = self.y {
66            let _ = write!(command, " Y{}", round_precision(y));
67        }
68
69        if let Some(z) = self.z {
70            let _ = write!(command, " Z{}", round_precision(z));
71        }
72
73        if let Some(f) = self.f {
74            let _ = write!(command, " F{}", round_precision(f));
75        }
76
77        command
78    }
79}
80
81/// Arc Move (clockwise)
82///
83/// Use either R or I, J, mixing all three is not allowed
84#[derive(Debug, Clone, PartialEq)]
85pub struct G2 {
86    /// X Coordinate
87    pub x: Option<f64>,
88    /// Y Coordinate
89    pub y: Option<f64>,
90    /// Z Coordinate
91    pub z: Option<f64>,
92    /// X Offset
93    pub i: Option<f64>,
94    /// Y Offset
95    pub j: Option<f64>,
96    /// Z Offset
97    pub k: Option<f64>,
98    /// Radius
99    pub r: Option<f64>,
100    ///  Number of complete circles
101    pub p: Option<u32>,
102    /// Feedrate
103    pub f: Option<f64>,
104}
105
106impl G2 {
107    /// Generate G-code string
108    pub fn to_gcode(&self) -> String {
109        let mut command = "G2".to_string();
110
111        if let Some(x) = self.x {
112            let _ = write!(command, " X{}", round_precision(x));
113        }
114
115        if let Some(y) = self.y {
116            let _ = write!(command, " Y{}", round_precision(y));
117        }
118
119        if let Some(z) = self.z {
120            let _ = write!(command, " Z{}", round_precision(z));
121        }
122
123        if let Some(r) = self.r {
124            let _ = write!(command, " R{}", round_precision(r));
125        } else {
126            if let Some(i) = self.i {
127                let _ = write!(command, " I{}", round_precision(i));
128            }
129
130            if let Some(j) = self.j {
131                let _ = write!(command, " J{}", round_precision(j));
132            }
133
134            if let Some(k) = self.k {
135                let _ = write!(command, " K{}", round_precision(k));
136            }
137        }
138
139        if let Some(p) = self.p {
140            let _ = write!(command, " P{}", round_precision(p.into()));
141        }
142
143        if let Some(f) = self.f {
144            let _ = write!(command, " F{}", round_precision(f));
145        }
146
147        command
148    }
149}
150
151/// Arc Move (counterclockwise)
152///
153/// Use either R or I, J, mixing all three is not allowed
154#[derive(Debug, Clone, PartialEq)]
155pub struct G3 {
156    /// X Coordinate
157    pub x: Option<f64>,
158    /// Y Coordinate
159    pub y: Option<f64>,
160    /// Z Coordinate
161    pub z: Option<f64>,
162    /// X Offset
163    pub i: Option<f64>,
164    /// Y Offset
165    pub j: Option<f64>,
166    /// Z Offset
167    pub k: Option<f64>,
168    /// Radius
169    pub r: Option<f64>,
170    ///  Number of complete circles
171    pub p: Option<u32>,
172    /// Feedrate
173    pub f: Option<f64>,
174}
175
176impl G3 {
177    /// Generate G-code string
178    pub fn to_gcode(&self) -> String {
179        let mut command = "G3".to_string();
180
181        if let Some(x) = self.x {
182            let _ = write!(command, " X{}", round_precision(x));
183        }
184
185        if let Some(y) = self.y {
186            let _ = write!(command, " Y{}", round_precision(y));
187        }
188
189        if let Some(z) = self.z {
190            let _ = write!(command, " Z{}", round_precision(z));
191        }
192
193        if let Some(r) = self.r {
194            let _ = write!(command, " R{}", round_precision(r));
195        } else {
196            if let Some(i) = self.i {
197                let _ = write!(command, " I{}", round_precision(i));
198            }
199
200            if let Some(j) = self.j {
201                let _ = write!(command, " J{}", round_precision(j));
202            }
203
204            if let Some(k) = self.k {
205                let _ = write!(command, " K{}", round_precision(k));
206            }
207        }
208
209        if let Some(p) = self.p {
210            let _ = write!(command, " P{}", round_precision(p.into()));
211        }
212
213        if let Some(f) = self.f {
214            let _ = write!(command, " F{}", round_precision(f));
215        }
216
217        command
218    }
219}
220
221/// Dwell (pause duration)
222#[derive(Debug, Clone, PartialEq)]
223pub struct G4 {
224    /// Duration to pause (serializedto seconds)
225    pub p: Duration,
226}
227
228impl G4 {
229    /// Generate G-code string
230    pub fn to_gcode(&self) -> String {
231        format!("G4 P{}", round_precision(self.p.as_secs_f64()))
232    }
233}
234
235/// Select Plane XY
236#[derive(Debug, Clone, PartialEq)]
237pub struct G17 {}
238
239impl G17 {
240    /// Generate G-code string
241    pub fn to_gcode(&self) -> String {
242        "G17".to_string()
243    }
244}
245
246/// Select Plane ZX
247#[derive(Debug, Clone, PartialEq)]
248pub struct G18 {}
249
250impl G18 {
251    /// Generate G-code string
252    pub fn to_gcode(&self) -> String {
253        "G18".to_string()
254    }
255}
256
257/// Select Plane YZ
258#[derive(Debug, Clone, PartialEq)]
259pub struct G19 {}
260
261impl G19 {
262    /// Generate G-code string
263    pub fn to_gcode(&self) -> String {
264        "G19".to_string()
265    }
266}
267
268/// Inch Units
269#[derive(Debug, Clone, PartialEq)]
270pub struct G20 {}
271
272impl G20 {
273    /// Generate G-code string
274    pub fn to_gcode(&self) -> String {
275        "G20".to_string()
276    }
277}
278
279/// Millimeter Units
280#[derive(Debug, Clone, PartialEq)]
281pub struct G21 {}
282
283impl G21 {
284    /// Generate G-code string
285    pub fn to_gcode(&self) -> String {
286        "G21".to_string()
287    }
288}
289
290/// Tool Length Offset (applies offset to all coordinates)
291#[derive(Debug, Clone, PartialEq)]
292pub struct G43 {
293    /// Tool number (offset will be looked up in the tool table)
294    pub h: u32,
295}
296
297impl G43 {
298    /// Generate G-code string
299    pub fn to_gcode(&self) -> String {
300        format!("G43 H{}", self.h)
301    }
302}
303
304/// Set Feed Rate
305#[derive(Debug, Clone, PartialEq)]
306pub struct F {
307    /// Feed rate (units per minute)
308    pub x: f64,
309}
310
311impl F {
312    /// Generate G-code string
313    pub fn to_gcode(&self) -> String {
314        format!("F{}", round_precision(self.x))
315    }
316}
317
318/// Set Spindle Speed
319#[derive(Debug, Clone, PartialEq)]
320pub struct S {
321    /// Feed rate (rpm)
322    pub x: f64,
323}
324
325impl S {
326    /// Generate G-code string
327    pub fn to_gcode(&self) -> String {
328        format!("S{}", round_precision(self.x))
329    }
330}
331
332/// Program Pause (user must resume)
333#[derive(Debug, Clone, PartialEq)]
334pub struct M0 {}
335
336impl M0 {
337    /// Generate G-code string
338    pub fn to_gcode(&self) -> String {
339        "M0".to_string()
340    }
341}
342
343/// Program End (stop spindle and reset all offsets)
344#[derive(Debug, Clone, PartialEq)]
345pub struct M2 {}
346
347impl M2 {
348    /// Generate G-code string
349    pub fn to_gcode(&self) -> String {
350        "M2".to_string()
351    }
352}
353
354/// Start Spindle (clockwise)
355#[derive(Debug, Clone, PartialEq)]
356pub struct M3 {}
357
358impl M3 {
359    /// Generate G-code string
360    pub fn to_gcode(&self) -> String {
361        "M3".to_string()
362    }
363}
364
365/// Start Spindle (counterclockwise)
366#[derive(Debug, Clone, PartialEq)]
367pub struct M4 {}
368
369impl M4 {
370    /// Generate G-code string
371    pub fn to_gcode(&self) -> String {
372        "M4".to_string()
373    }
374}
375
376/// Stop Spindle
377#[derive(Debug, Clone, PartialEq)]
378pub struct M5 {}
379
380impl M5 {
381    /// Generate G-code string
382    pub fn to_gcode(&self) -> String {
383        "M5".to_string()
384    }
385}
386
387/// Manual Tool Change
388#[derive(Debug, Clone, PartialEq)]
389pub struct M6 {
390    /// Tool number
391    pub t: u8,
392}
393
394impl M6 {
395    /// Generate G-code string
396    pub fn to_gcode(&self) -> String {
397        format!("T{} M6", self.t)
398    }
399}
400
401/// Empty Line
402#[derive(Debug, Clone, PartialEq)]
403pub struct Empty {}
404
405impl Empty {
406    /// Generate G-code string
407    pub fn to_gcode(&self) -> String {
408        "".to_string()
409    }
410}
411
412/// Comment
413#[derive(Debug, Clone, Default, PartialEq)]
414pub struct Comment {
415    /// Comment
416    pub text: String,
417}
418
419impl Comment {
420    /// Generate G-code string
421    pub fn to_gcode(&self) -> String {
422        if self.text.is_empty() {
423            return String::new();
424        }
425
426        format!(";({})", self.text)
427    }
428}
429
430/// Message to print
431#[derive(Debug, Clone, PartialEq)]
432pub struct Message {
433    /// Message
434    pub text: String,
435}
436
437impl Message {
438    /// Generate G-code string
439    pub fn to_gcode(&self) -> String {
440        format!("(MSG,{})", self.text)
441    }
442}
443
444/// The Instruction enum is used to represent a single G-code command in a program.
445/// See the
446/// [Grbl reference](https://github.com/gnea/grbl/wiki/Grbl-v1.1-Commands#g---view-gcode-parser-state)
447/// for more details.
448#[derive(Debug, Clone, PartialEq)]
449pub enum Instruction {
450    /// Command G0, Rapid Move
451    G0(G0),
452    /// Command G1, Linear Move
453    G1(G1),
454    /// Command G2, Arc Move (clockwise)
455    G2(G2),
456    /// Command G3, Arc Move (counterclockwise)
457    G3(G3),
458    /// Command G4, Dwell
459    G4(G4),
460    /// Command G17, Select Plane XY
461    G17(G17),
462    /// Command G18, Select Plane XZ
463    G18(G18),
464    /// Command G19, Select Plane YZ
465    G19(G19),
466    /// Command G20, Inch Units
467    G20(G20),
468    /// Command G20, Millimeter Units
469    G21(G21),
470    /// Command G43, Tool Length Offset
471    G43(G43),
472    /// Command F, Set Feed Rate
473    F(F),
474    /// Command S, Set Spindle Speed
475    S(S),
476    /// Command M0, Program Pause
477    M0(M0),
478    /// Command M2, Program End
479    M2(M2),
480    /// Command M3, Start Spindle (clockwise)
481    M3(M3),
482    /// Command M4, Start Spindle (counterclockwise)
483    M4(M4),
484    /// Command M5, Stop Spindle
485    M5(M5),
486    /// Command M6, Manual Tool Change
487    M6(M6),
488    /// Command Empty, Empty Line
489    Empty(Empty),
490    /// Command Comment, Comment
491    Comment(Comment),
492    /// Command Message, Message to point
493    Message(Message),
494}
495
496impl Instruction {
497    /// Converts instruction to G-code
498    pub fn to_gcode(&self) -> String {
499        match self {
500            Instruction::G0(instruction) => instruction.to_gcode(),
501            Instruction::G1(instruction) => instruction.to_gcode(),
502            Instruction::G2(instruction) => instruction.to_gcode(),
503            Instruction::G3(instruction) => instruction.to_gcode(),
504            Instruction::G4(instruction) => instruction.to_gcode(),
505            Instruction::G17(instruction) => instruction.to_gcode(),
506            Instruction::G18(instruction) => instruction.to_gcode(),
507            Instruction::G19(instruction) => instruction.to_gcode(),
508            Instruction::G20(instruction) => instruction.to_gcode(),
509            Instruction::G21(instruction) => instruction.to_gcode(),
510            Instruction::G43(instruction) => instruction.to_gcode(),
511            Instruction::F(instruction) => instruction.to_gcode(),
512            Instruction::S(instruction) => instruction.to_gcode(),
513            Instruction::M0(instruction) => instruction.to_gcode(),
514            Instruction::M2(instruction) => instruction.to_gcode(),
515            Instruction::M3(instruction) => instruction.to_gcode(),
516            Instruction::M4(instruction) => instruction.to_gcode(),
517            Instruction::M5(instruction) => instruction.to_gcode(),
518            Instruction::M6(instruction) => instruction.to_gcode(),
519            Instruction::Empty(instruction) => instruction.to_gcode(),
520            Instruction::Comment(instruction) => instruction.to_gcode(),
521            Instruction::Message(instruction) => instruction.to_gcode(),
522        }
523    }
524}