• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

cbruiz / printhor / 13775596467

10 Mar 2025 09:59PM UTC coverage: 90.713% (-0.02%) from 90.734%
13775596467

push

github

web-flow
Review/motion optimizations (#36)

* Module refactoring and light doc improvement
* Bump embassy upstream

184 of 218 new or added lines in 16 files covered. (84.4%)

11 existing lines in 1 file now uncovered.

14837 of 16356 relevant lines covered (90.71%)

733512.36 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.18
/printhor/src/bin/processing/gcode_parser.rs
1
use crate::helpers;
2
use crate::hwa;
3
#[allow(unused)]
4
use crate::processing::EFSXYZ;
5
#[allow(unused)]
6
use crate::processing::EXYZ;
7
#[allow(unused)]
8
use crate::processing::FXYZ;
9
#[allow(unused)]
10
use crate::processing::GCodeCmd;
11
#[allow(unused)]
12
use crate::processing::GCodeValue;
13
#[allow(unused)]
14
use crate::processing::N;
15
#[allow(unused)]
16
use crate::processing::S;
17
use hwa::CommChannel;
18

19
#[derive(Debug)]
20
pub enum GCodeLineParserError {
21
    /// There was an error parsing
22
    ParseError(u32),
23
    /// The Gcode was not implemented
24
    GCodeNotImplemented(u32, alloc::string::String),
25
    /// EOF Reading from parser
26
    EOF,
27
    /// Unexpected fatal error
28
    #[allow(unused)]
29
    FatalError,
30
}
31

32
#[cfg(feature = "debug-gcode")]
33
struct FormatableGCode<'a>(&'a async_gcode::GCode);
34

35
#[cfg(feature = "debug-gcode")]
36
impl core::fmt::Debug for FormatableGCode<'_> {
37
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
38
        match self.0 {
39
            async_gcode::GCode::StatusCommand => {
40
                f.write_str("StatusCommand")?;
41
            }
42
            async_gcode::GCode::BlockDelete => {
43
                f.write_str("BlockDelete")?;
44
            }
45
            async_gcode::GCode::LineNumber(_ln) => {
46
                core::write!(f, "LineNumber<{:?}>", _ln)?;
47
            }
48
            async_gcode::GCode::Word(_w, _v) => {
49
                core::write!(f, "Word[{}", _w.to_uppercase())?;
50
                match _v {
51
                    async_gcode::RealValue::Literal(_l) => {
52
                        match _l {
53
                            async_gcode::Literal::RealNumber(_r) => {
54
                                let div = 10_i32.pow(_r.scale().into());
55
                                let w0 = _r.integer_part() / div;
56
                                let w1 = _r.integer_part() % div;
57
                                core::write!(
58
                                    f,
59
                                    "{}.{:0width$}",
60
                                    w0,
61
                                    w1,
62
                                    width = _r.scale() as usize
63
                                )?;
64
                            }
65
                            async_gcode::Literal::String(_s) => {
66
                                core::write!(f, " {}", _s)?;
67
                            }
68
                        }
69
                        core::write!(f, "]")?;
70
                    }
71
                    _ => {}
72
                }
73
            }
74
            async_gcode::GCode::Text(_t) => match _t {
75
                async_gcode::RealValue::Literal(async_gcode::Literal::String(_s)) => {
76
                    core::write!(f, "String[{}]", _s)?;
77
                }
78
                _ => {}
79
            },
80
            async_gcode::GCode::Execute => {
81
                core::write!(f, "Execute")?;
82
            }
83
            #[allow(unreachable_patterns)]
84
            _ => {}
85
        }
86
        Ok(())
87
    }
88
}
89
#[cfg(all(feature = "with-defmt", feature = "debug-gcode"))]
90
impl defmt::Format for FormatableGCode<'_> {
91
    fn format(&self, f: defmt::Formatter) {
92
        match self.0 {
93
            async_gcode::GCode::StatusCommand => {
94
                defmt::write!(f, "StatusCommand");
95
            }
96
            async_gcode::GCode::BlockDelete => {
97
                defmt::write!(f, "BlockDelete");
98
            }
99
            async_gcode::GCode::LineNumber(_ln) => {
100
                defmt::write!(f, "LineNumber<{:?}>", _ln);
101
            }
102
            async_gcode::GCode::Word(_w, _v) => {
103
                use alloc::string::ToString;
104
                defmt::write!(f, "Word[{:?}", _w.to_uppercase().to_string().as_str());
105
                match _v {
106
                    async_gcode::RealValue::Literal(_l) => {
107
                        match _l {
108
                            async_gcode::Literal::RealNumber(_r) => {
109
                                defmt::write!(
110
                                    f,
111
                                    " val: {}, scale: {}",
112
                                    _r.integer_part(),
113
                                    _r.scale()
114
                                );
115
                            }
116
                            async_gcode::Literal::String(_s) => {
117
                                defmt::write!(f, " {}", _s.as_str());
118
                            }
119
                        }
120
                        defmt::write!(f, "]");
121
                    }
122
                    _ => {}
123
                }
124
            }
125
            async_gcode::GCode::Text(_t) => match _t {
126
                async_gcode::RealValue::Literal(async_gcode::Literal::String(_s)) => {
127
                    defmt::write!(f, "String[{}]", _s.as_str());
128
                }
129
                _ => {}
130
            },
131
            async_gcode::GCode::Execute => {
132
                defmt::write!(f, "Execute");
133
            }
134
            #[allow(unreachable_patterns)]
135
            _ => {}
136
        }
137
    }
138
}
139

140
#[cfg(feature = "with-defmt")]
141
impl defmt::Format for GCodeLineParserError {
142
    fn format(&self, fmt: defmt::Formatter) {
143
        match self {
144
            GCodeLineParserError::ParseError(ln) => {
145
                defmt::write!(fmt, "ParseError(line={})", ln);
146
            }
147
            GCodeLineParserError::GCodeNotImplemented(ln, string) => {
148
                defmt::write!(
149
                    fmt,
150
                    "GCodeNotImplemented(line={}, gcode={})",
151
                    ln,
152
                    string.as_str()
153
                );
154
            }
155
            GCodeLineParserError::FatalError => {
156
                defmt::write!(fmt, "FatalError");
157
            }
158
            GCodeLineParserError::EOF => {
159
                defmt::write!(fmt, "EOF");
160
            }
161
        }
162
    }
163
}
164

165
/// Represents the raw specification of a G-code command.
166
///
167
/// G-code is a language used to control CNC machines, 3D printers, and other similar equipment.
168
/// A `RawGCodeSpec` captures the initial character of the command (e.g., 'G', 'M') and any numerical
169
/// specifics associated with it, potentially including a sub-value used for more granular control.
170
///
171
/// # Fields
172
/// - `code`: The main character of the G-code command.
173
/// - `spec`: An optional numerical part that follows the main character. For example, 'G1' would have
174
///   '1' as its spec.
175
/// - `sub`: An optional sub-value that provides additional specificity. For example, 'G1.1' would have
176
///   '1' as its spec and '1' as its sub.
177
///
178
/// # Example
179
///
180
/// ```
181
/// let gcode = RawTGCodeSpec::from('G', Some((1, 0)));
182
/// assert_eq!(format!("{:?}", gcode), "G1");
183
/// ```
184
pub struct RawGCodeSpec {
185
    code: char,
186
    spec: Option<i32>,
187
    sub: Option<i32>,
188
}
189

190
impl RawGCodeSpec {
191
    pub fn from(code: char, spec: Option<(i32, u8)>) -> Self {
452,563✔
192
        match spec {
452,563✔
193
            None => Self {
6✔
194
                code,
6✔
195
                spec: None,
6✔
196
                sub: None,
6✔
197
            },
6✔
198
            Some((_num, _scale)) => {
452,557✔
199
                if _scale == 0 {
452,557✔
200
                    Self {
452,527✔
201
                        code,
452,527✔
202
                        spec: Some(_num),
452,527✔
203
                        sub: None,
452,527✔
204
                    }
452,527✔
205
                } else {
206
                    let sc = 10_i32.pow(_scale as u32);
30✔
207
                    let sp = _num / sc;
30✔
208
                    let ss = _num % sc;
30✔
209
                    Self {
30✔
210
                        code,
30✔
211
                        spec: Some(sp),
30✔
212
                        sub: Some(ss),
30✔
213
                    }
30✔
214
                }
215
            }
216
        }
217
    }
452,563✔
218
}
219
impl core::fmt::Debug for RawGCodeSpec {
220
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
345✔
221
        core::write!(f, "{}", self.code.to_uppercase())?;
345✔
222
        match &self.spec {
345✔
223
            Some(_spec) => {
339✔
224
                core::write!(f, "{}", _spec)?;
339✔
225
                match &self.sub {
339✔
226
                    Some(_sub) => {
×
227
                        core::write!(f, ".{}", _sub)
×
228
                    }
229
                    _ => Ok(()),
339✔
230
                }
231
            }
232
            _ => Ok(()),
6✔
233
        }
234
    }
345✔
235
}
236

237
// dyn trait could reduce code size a lot with the penalty of the indirection
238
pub struct GCodeLineParser<STREAM>
239
where
240
    STREAM: async_gcode::ByteStream<Item = Result<u8, async_gcode::Error>>,
241
{
242
    raw_parser: async_gcode::Parser<STREAM, async_gcode::Error>,
243
    gcode_line: Option<u32>,
244
}
245

246
impl<STREAM> GCodeLineParser<STREAM>
247
where
248
    STREAM: async_gcode::ByteStream<Item = Result<u8, async_gcode::Error>>,
249
{
250
    pub fn new(stream: STREAM) -> Self {
221✔
251
        Self {
221✔
252
            raw_parser: async_gcode::Parser::new(stream),
221✔
253
            gcode_line: None,
221✔
254
        }
221✔
255
    }
221✔
256

257
    pub fn gcode_line(&self) -> Option<u32> {
144✔
258
        self.gcode_line
144✔
259
    }
144✔
260

261
    pub async fn next_gcode(
452,620✔
262
        &mut self,
452,620✔
263
        _channel: CommChannel,
452,620✔
264
    ) -> Result<GCodeCmd, GCodeLineParserError> {
452,620✔
265
        // The GcodeCmd being constructed.
452,620✔
266
        // * Initially set to none
452,620✔
267
        // * Reset back to None when built, updated and returned
452,620✔
268
        let mut current_gcode: Option<GCodeCmd> = None;
452,620✔
269
        // Same as previous but unparsed
452,620✔
270
        let mut raw_gcode_spec: Option<RawGCodeSpec> = None;
452,620✔
271

452,620✔
272
        let mut tagged_line_num = None;
452,620✔
273
        let mut skip_gcode = false;
452,620✔
274

275
        loop {
276
            match self.raw_parser.next().await {
2,281,607✔
277
                None => {
278
                    hwa::trace!("EOF reading from stream");
45✔
279
                    self.gcode_line = tagged_line_num;
45✔
280
                    match current_gcode.take() {
45✔
281
                        None => {
282
                            match raw_gcode_spec.take() {
21✔
283
                                None => {
284
                                    return Err(GCodeLineParserError::EOF);
21✔
285
                                } // Empty line. Just ignore
286
                                Some(rgs) => {
×
287
                                    return Err(GCodeLineParserError::GCodeNotImplemented(
×
288
                                        self.raw_parser.get_current_line(),
×
289
                                        alloc::format!("{:?}", rgs),
×
290
                                    ));
×
291
                                }
292
                            }
293
                        }
294
                        Some(mut cgv) => {
24✔
295
                            cgv.order_num = self.raw_parser.get_current_line();
24✔
296
                            return Ok(cgv);
24✔
297
                        }
298
                    }
299
                }
300
                Some(parser_gcode) => {
2,281,526✔
301
                    match parser_gcode {
2,281,526✔
302
                        Ok(g) => {
2,281,517✔
303
                            #[cfg(feature = "debug-gcode")]
2,281,517✔
304
                            hwa::info!(
2,281,517✔
305
                                "[debug-gcode] channel: {:?} gcode: {:?}",
2,281,517✔
306
                                _channel,
2,281,517✔
307
                                FormatableGCode(&g)
2,281,517✔
308
                            );
2,281,517✔
309
                            match g {
2,281,517✔
310
                                // FIXME Undo this hacky temporary solution in async-gcode.
311
                                // A sub-protocol approach is preferred
312
                                #[cfg(feature = "grbl-compat")]
313
                                async_gcode::GCode::StatusCommand => {
314
                                    return Ok(GCodeCmd::new(
315
                                        self.raw_parser.get_current_line(),
316
                                        tagged_line_num,
317
                                        GCodeValue::Status,
318
                                    ));
319
                                }
320
                                async_gcode::GCode::BlockDelete => {
6✔
321
                                    skip_gcode = true;
6✔
322
                                    //crate::debug!("BlockDelete");
6✔
323
                                }
6✔
324
                                async_gcode::GCode::LineNumber(n) => {
18✔
325
                                    tagged_line_num = n;
18✔
326
                                }
18✔
327
                                async_gcode::GCode::Word(ch, fv) => {
1,739,323✔
328
                                    if skip_gcode {
1,739,323✔
329
                                        continue;
333✔
330
                                    }
1,738,990✔
331
                                    let frx = match &fv {
1,738,990✔
332
                                        async_gcode::RealValue::Literal(
333
                                            async_gcode::Literal::RealNumber(f),
1,738,972✔
334
                                        ) => Some((f.integer_part(), f.scale())),
1,738,972✔
335
                                        _ => None,
18✔
336
                                    };
337
                                    if let Some(current_gcode) = &mut current_gcode {
1,738,990✔
338
                                        // We already are building a GCodeCmd, so we update its fields
339
                                        update_current(current_gcode, ch, frx, fv)
1,286,433✔
340
                                    } else {
341
                                        raw_gcode_spec.replace(RawGCodeSpec::from(ch, frx));
452,557✔
342

343
                                        let has_text_argument = match (ch, frx) {
452,557✔
344
                                            ('m', Some((23, 0)))
345
                                            | ('m', Some((117, 0)))
346
                                            | ('m', Some((118, 0))) => true,
30✔
347
                                            _ => false,
452,527✔
348
                                        };
349
                                        // We need to start building the current GCodeCmd that is being parsed
350
                                        match init_current(ch, frx) {
452,557✔
351
                                            None => {
339✔
352
                                                // Unable to init: The GCodeValue is unexpected
339✔
353
                                                // Hence, we will skip every [async_gcode::GCode::Word] until [async_gcode::GCode::Execute]
339✔
354
                                                skip_gcode = true;
339✔
355
                                            }
339✔
356
                                            Some(gcode_value) => {
452,218✔
357
                                                current_gcode.replace(GCodeCmd::new(
452,218✔
358
                                                    0, // Will update later at [async_gcode::GCode::Execute]
452,218✔
359
                                                    tagged_line_num,
452,218✔
360
                                                    gcode_value,
452,218✔
361
                                                ));
452,218✔
362
                                                if has_text_argument {
452,218✔
363
                                                    self.raw_parser.switch_to_text();
30✔
364
                                                }
452,188✔
365
                                            }
366
                                        }
367
                                    }
368
                                }
369
                                async_gcode::GCode::Text(value) => {
30✔
370
                                    if let Some(current_gcode) = &mut current_gcode {
30✔
371
                                        // We already are building a GCodeCmd, so we update its fields
372
                                        update_current(current_gcode, 'T', None, value)
30✔
373
                                    }
×
374
                                }
375
                                async_gcode::GCode::Execute => {
376
                                    // Reset skip_gcode status
377
                                    skip_gcode = false;
542,140✔
378
                                    self.gcode_line = tagged_line_num;
542,140✔
379
                                    match current_gcode.take() {
542,140✔
380
                                        None => {
381
                                            match raw_gcode_spec.take() {
89,949✔
382
                                                None => {
383
                                                    #[cfg(feature = "trace-commands")]
384
                                                    hwa::warn!("Ignoring empty line");
385
                                                    continue;
89,610✔
386
                                                } // Empty line. Just ignore
387
                                                Some(rgs) => {
339✔
388
                                                    return Err(
339✔
389
                                                        GCodeLineParserError::GCodeNotImplemented(
339✔
390
                                                            self.raw_parser.get_current_line(),
339✔
391
                                                            alloc::format!("{:?}", rgs),
339✔
392
                                                        ),
339✔
393
                                                    );
339✔
394
                                                }
395
                                            }
396
                                        }
397
                                        Some(mut cgv) => {
452,191✔
398
                                            cgv.order_num = self.raw_parser.get_current_line();
452,191✔
399
                                            return Ok(cgv);
452,191✔
400
                                        }
401
                                    }
402
                                }
403
                                _ => {
404
                                    return Err(GCodeLineParserError::GCodeNotImplemented(
×
405
                                        self.raw_parser.get_current_line(),
×
NEW
406
                                        alloc::format!("N/A"),
×
407
                                    ));
×
408
                                }
409
                            }
410
                        }
411
                        Err(error) => {
9✔
412
                            match error {
9✔
413
                                async_gcode::Error::UnexpectedByte(_b) => {
9✔
414
                                    hwa::warn!("Unexpected byte: {} ({})", _b, char::from(_b));
9✔
415
                                }
416
                                async_gcode::Error::NumberOverflow => {
417
                                    hwa::warn!("Number overflow");
×
418
                                }
419
                                async_gcode::Error::BadNumberFormat => {
420
                                    hwa::warn!("Bad number format");
×
421
                                }
422
                                _e => {
×
423
                                    #[cfg(feature = "native")]
×
424
                                    hwa::error!("Parse error {:?}", _e);
×
425
                                    hwa::error!("Parse error");
×
426
                                }
427
                            }
428
                            return Err(GCodeLineParserError::ParseError(
9✔
429
                                self.raw_parser.get_current_line(),
9✔
430
                            ));
9✔
431
                        }
432
                    }
433
                }
434
            }
435
        }
436
    }
452,584✔
437

438
    pub async fn reset(&mut self) {
30✔
439
        hwa::debug!("AsyncGcodeParser reset");
30✔
440
        self.raw_parser.reset().await;
30✔
441
    }
30✔
442

443
    pub fn reset_current_line(&mut self) {
12✔
444
        hwa::warn!("AsyncGcodeParser reset_current_line");
12✔
445
        self.raw_parser.update_current_line(0);
12✔
446
    }
12✔
447

448
    pub fn get_state(&self) -> async_gcode::AsyncParserState {
36✔
449
        self.raw_parser.get_state()
36✔
450
    }
36✔
451

452
    pub fn get_line(&self) -> u32 {
452,359✔
453
        self.raw_parser.get_current_line()
452,359✔
454
    }
452,359✔
455

456
    pub async fn close(&mut self) {
16✔
457
        self.raw_parser.reset().await;
16✔
458
    }
16✔
459
}
460

461
/// This trait is just a hack to give backward compatibility with unpatched async-gcode 0.3.0
462
/// Normally, won't be used as it is patched in cargo.toml
463
/// Purpose of this trait and impl is just to be able to publish printhor in crates.io
464
#[allow(unused)]
465
trait FixedAdaptor {
466
    fn integer_part(&self) -> i32;
467
    fn scale(&self) -> u8;
468
}
469

470
impl FixedAdaptor for f64 {
471
    fn integer_part(&self) -> i32 {
×
472
        panic!("Please, use patched async-gcode instead")
×
473
    }
474

475
    fn scale(&self) -> u8 {
×
476
        panic!("Please, use patched async-gcode instead")
×
477
    }
478
}
479

480
/// Initialize and EMPTY GCodeValue variant from ch, frx spec coming from parser
481
fn init_current(ch: char, frx: Option<(i32, u8)>) -> Option<GCodeValue> {
452,557✔
482
    match (ch, frx) {
452,557✔
483
        #[cfg(feature = "grbl-compat")]
484
        ('$', None) => Some(GCodeValue::GRBLCmd),
485
        ('g', None) => Some(GCodeValue::G),
×
486
        #[cfg(feature = "with-motion")]
487
        ('g', Some((0, 0))) => Some(GCodeValue::G0(FXYZ::new())),
46✔
488
        #[cfg(feature = "with-motion")]
489
        ('g', Some((1, 0))) => Some(GCodeValue::G1(EFSXYZ::new())),
439,999✔
490
        #[cfg(feature = "with-motion")]
491
        ('g', Some((4, 0))) => Some(GCodeValue::G4(S::new())),
19✔
492
        #[cfg(feature = "with-motion")]
493
        ('g', Some((10, 0))) => Some(GCodeValue::G10),
×
494
        #[cfg(feature = "with-motion")]
495
        ('g', Some((17, 0))) => Some(GCodeValue::G17),
×
496
        #[cfg(feature = "with-motion")]
497
        ('g', Some((21, 0))) => Some(GCodeValue::G21),
10✔
498
        #[cfg(feature = "with-motion")]
499
        ('g', Some((28, 0))) => Some(GCodeValue::G28(EXYZ::new())),
19✔
500
        #[cfg(feature = "with-motion")]
501
        ('g', Some((29, 0))) => Some(GCodeValue::G29),
6✔
502
        #[cfg(feature = "with-motion")]
503
        ('g', Some((291, 1))) => Some(GCodeValue::G29_1),
6✔
504
        #[cfg(feature = "with-motion")]
505
        ('g', Some((292, 1))) => Some(GCodeValue::G29_2),
6✔
506
        #[cfg(feature = "with-probe")]
507
        ('g', Some((31, 0))) => Some(GCodeValue::G31),
6✔
508
        #[cfg(feature = "with-probe")]
509
        ('g', Some((32, 0))) => Some(GCodeValue::G32),
×
510
        ('g', Some((80, 0))) => Some(GCodeValue::G80),
3✔
511
        #[cfg(feature = "with-motion")]
512
        ('g', Some((90, 0))) => Some(GCodeValue::G90),
13✔
513
        #[cfg(feature = "with-motion")]
514
        ('g', Some((91, 0))) => Some(GCodeValue::G91),
×
515
        #[cfg(feature = "with-motion")]
516
        ('g', Some((92, 0))) => Some(GCodeValue::G92(EXYZ::new())),
733✔
517
        #[cfg(feature = "with-motion")]
518
        ('g', Some((94, 0))) => Some(GCodeValue::G94),
×
519
        ('m', None) => Some(GCodeValue::M),
×
520
        #[cfg(all(feature = "with-sd-card", feature = "with-print-job"))]
521
        ('m', Some((2, 0))) => Some(GCodeValue::M2),
7✔
522
        ('m', Some((3, 0))) => Some(GCodeValue::M3),
×
523
        ('m', Some((4, 0))) => Some(GCodeValue::M4),
4✔
524
        ('m', Some((5, 0))) => Some(GCodeValue::M5),
4✔
525
        #[cfg(feature = "with-sd-card")]
526
        ('m', Some((20, 0))) => Some(GCodeValue::M20(None)),
6✔
527
        #[cfg(feature = "with-sd-card")]
528
        ('m', Some((21, 0))) => Some(GCodeValue::M21),
×
529
        #[cfg(all(feature = "with-sd-card", feature = "with-print-job"))]
530
        ('m', Some((23, 0))) => Some(GCodeValue::M23(None)),
18✔
531
        #[cfg(all(feature = "with-sd-card", feature = "with-print-job"))]
532
        ('m', Some((24, 0))) => Some(GCodeValue::M24),
×
533
        #[cfg(all(feature = "with-sd-card", feature = "with-print-job"))]
534
        ('m', Some((25, 0))) => Some(GCodeValue::M25),
×
535
        ('m', Some((37, 0))) => Some(GCodeValue::M37(S::new())),
12✔
536
        ('m', Some((73, 0))) => Some(GCodeValue::M73),
1,176✔
537
        ('m', Some((79, 0))) => Some(GCodeValue::M79),
×
538
        #[cfg(feature = "with-ps-on")]
539
        ('m', Some((80, 0))) => Some(GCodeValue::M80),
3✔
540
        #[cfg(feature = "with-ps-on")]
541
        ('m', Some((81, 0))) => Some(GCodeValue::M81),
3✔
542
        ('m', Some((83, 0))) => Some(GCodeValue::M83),
6✔
543
        #[cfg(feature = "with-motion")]
544
        ('m', Some((84, 0))) => Some(GCodeValue::M84),
3✔
545
        ('m', Some((100, 0))) => Some(GCodeValue::M100),
×
546
        #[cfg(feature = "with-hot-end")]
547
        ('m', Some((104, 0))) => Some(GCodeValue::M104(S::new())),
8✔
548
        ('m', Some((105, 0))) => Some(GCodeValue::M105),
×
549
        #[cfg(feature = "with-fan-layer")]
550
        ('m', Some((106, 0))) => Some(GCodeValue::M106),
640✔
551
        #[cfg(feature = "with-fan-layer")]
552
        ('m', Some((107, 0))) => Some(GCodeValue::M107),
10✔
553
        #[cfg(feature = "with-hot-end")]
554
        ('m', Some((109, 0))) => Some(GCodeValue::M109(S::new())),
4✔
555
        ('m', Some((110, 0))) => Some(GCodeValue::M110(N::new())),
12✔
556
        #[cfg(feature = "with-motion")]
557
        ('m', Some((114, 0))) => Some(GCodeValue::M114),
10✔
558
        ('m', Some((115, 0))) => Some(GCodeValue::M115),
3✔
559
        ('m', Some((117, 0))) => Some(GCodeValue::M117(None)),
6✔
560
        ('m', Some((118, 0))) => Some(GCodeValue::M118(None)),
6✔
561
        ('m', Some((119, 0))) => Some(GCodeValue::M119),
×
562
        #[cfg(feature = "with-hot-bed")]
563
        ('m', Some((140, 0))) => Some(GCodeValue::M140(S::new())),
6✔
564
        #[cfg(feature = "with-hot-bed")]
565
        ('m', Some((190, 0))) => Some(GCodeValue::M190),
2✔
566
        #[cfg(feature = "with-motion")]
567
        ('m', Some((201, 0))) => Some(GCodeValue::M201),
3✔
568
        #[cfg(feature = "with-motion")]
569
        ('m', Some((203, 0))) => Some(GCodeValue::M203),
3✔
570
        ('m', Some((204, 0))) => Some(GCodeValue::M204),
9,315✔
571
        ('m', Some((205, 0))) => Some(GCodeValue::M205),
12✔
572
        #[cfg(feature = "with-motion")]
573
        ('m', Some((206, 0))) => Some(GCodeValue::M206),
10✔
574
        ('m', Some((220, 0))) => Some(GCodeValue::M220(S::new())),
12✔
575
        ('m', Some((221, 0))) => Some(GCodeValue::M221(S::new())),
6✔
576
        ('m', Some((502, 0))) => Some(GCodeValue::M502),
×
577
        ('m', Some((503, 0))) => Some(GCodeValue::M503(S::new())),
×
578
        #[cfg(feature = "with-motion")]
579
        ('m', Some((8621, 1))) => Some(GCodeValue::M862_1),
9✔
580
        #[cfg(feature = "with-motion")]
581
        ('m', Some((8623, 1))) => Some(GCodeValue::M862_3),
9✔
582
        #[cfg(feature = "with-motion")]
583
        ('m', Some((900, 0))) => Some(GCodeValue::M900),
12✔
584
        #[cfg(feature = "with-motion")]
585
        ('m', Some((907, 0))) => Some(GCodeValue::M907),
12✔
586
        _ => None,
339✔
587
    }
588
}
452,557✔
589

590
fn update_current(
1,286,463✔
591
    gcode_cmd: &mut GCodeCmd,
1,286,463✔
592
    ch: char,
1,286,463✔
593
    frx: Option<(i32, u8)>,
1,286,463✔
594
    fv: async_gcode::RealValue,
1,286,463✔
595
) {
1,286,463✔
596
    match &mut gcode_cmd.value {
1,286,463✔
597
        #[cfg(feature = "grbl-compat")]
598
        GCodeValue::Status => match (ch, frx) {
599
            ('I', Some(_val)) => {
600
                hwa::warn!("TODO!!");
601
            }
602
            _ => {}
603
        },
604
        #[cfg(feature = "with-motion")]
605
        GCodeValue::G0(coord) => match (ch, frx) {
170✔
606
            ('f', Some(val)) => {
33✔
607
                coord.f.replace(helpers::to_fixed(val));
33✔
608
            }
33✔
609
            //
610
            #[cfg(feature = "with-x-axis")]
611
            ('x', Some(val)) => {
22✔
612
                coord.x.replace(helpers::to_fixed(val));
22✔
613
            }
22✔
614
            #[cfg(feature = "with-y-axis")]
615
            ('y', Some(val)) => {
22✔
616
                coord.y.replace(helpers::to_fixed(val));
22✔
617
            }
22✔
618
            #[cfg(feature = "with-z-axis")]
619
            ('z', Some(val)) => {
33✔
620
                coord.z.replace(helpers::to_fixed(val));
33✔
621
            }
33✔
622
            //
623
            #[cfg(feature = "with-a-axis")]
624
            ('a', Some(val)) => {
1✔
625
                coord.a.replace(helpers::to_fixed(val));
1✔
626
            }
1✔
627
            #[cfg(feature = "with-b-axis")]
628
            ('b', Some(val)) => {
1✔
629
                coord.b.replace(helpers::to_fixed(val));
1✔
630
            }
1✔
631
            #[cfg(feature = "with-c-axis")]
632
            ('c', Some(val)) => {
1✔
633
                coord.c.replace(helpers::to_fixed(val));
1✔
634
            }
1✔
635
            //
636
            #[cfg(feature = "with-i-axis")]
637
            ('i', Some(val)) => {
1✔
638
                coord.i.replace(helpers::to_fixed(val));
1✔
639
            }
1✔
640
            #[cfg(feature = "with-j-axis")]
641
            ('j', Some(val)) => {
1✔
642
                coord.j.replace(helpers::to_fixed(val));
1✔
643
            }
1✔
644
            #[cfg(feature = "with-k-axis")]
645
            ('k', Some(val)) => {
1✔
646
                coord.k.replace(helpers::to_fixed(val));
1✔
647
            }
1✔
648
            //
649
            #[cfg(feature = "with-u-axis")]
650
            ('u', Some(val)) => {
1✔
651
                coord.u.replace(helpers::to_fixed(val));
1✔
652
            }
1✔
653
            #[cfg(feature = "with-v-axis")]
654
            ('v', Some(val)) => {
1✔
655
                coord.v.replace(helpers::to_fixed(val));
1✔
656
            }
1✔
657
            #[cfg(feature = "with-w-axis")]
658
            ('w', Some(val)) => {
1✔
659
                coord.w.replace(helpers::to_fixed(val));
1✔
660
            }
1✔
661
            _ => {}
51✔
662
        },
663

664
        #[cfg(feature = "with-motion")]
665
        GCodeValue::G1(coord) => match (ch, frx) {
1,272,868✔
666
            #[cfg(feature = "with-e-axis")]
667
            ('e', Some(val)) => {
267,540✔
668
                coord.e.replace(helpers::to_fixed(val));
267,540✔
669
            }
267,540✔
670
            ('f', Some(val)) => {
49,603✔
671
                coord.f.replace(helpers::to_fixed(val));
49,603✔
672
            }
49,603✔
673
            ('s', Some(val)) => {
3,413✔
674
                coord.s.replace(helpers::to_fixed(val));
3,413✔
675
            }
3,413✔
676
            //
677
            #[cfg(feature = "with-x-axis")]
678
            ('x', Some(val)) => {
403,668✔
679
                coord.x.replace(helpers::to_fixed(val));
403,668✔
680
            }
403,668✔
681
            #[cfg(feature = "with-y-axis")]
682
            ('y', Some(val)) => {
403,665✔
683
                coord.y.replace(helpers::to_fixed(val));
403,665✔
684
            }
403,665✔
685
            #[cfg(feature = "with-z-axis")]
686
            ('z', Some(val)) => {
11,152✔
687
                coord.z.replace(helpers::to_fixed(val));
11,152✔
688
            }
11,152✔
689
            //
690
            #[cfg(feature = "with-a-axis")]
691
            ('a', Some(val)) => {
1✔
692
                coord.a.replace(helpers::to_fixed(val));
1✔
693
            }
1✔
694
            #[cfg(feature = "with-b-axis")]
695
            ('b', Some(val)) => {
1✔
696
                coord.b.replace(helpers::to_fixed(val));
1✔
697
            }
1✔
698
            #[cfg(feature = "with-c-axis")]
699
            ('c', Some(val)) => {
1✔
700
                coord.c.replace(helpers::to_fixed(val));
1✔
701
            }
1✔
702
            //
703
            #[cfg(feature = "with-i-axis")]
704
            ('i', Some(val)) => {
1✔
705
                coord.i.replace(helpers::to_fixed(val));
1✔
706
            }
1✔
707
            #[cfg(feature = "with-j-axis")]
708
            ('j', Some(val)) => {
1✔
709
                coord.j.replace(helpers::to_fixed(val));
1✔
710
            }
1✔
711
            #[cfg(feature = "with-k-axis")]
712
            ('k', Some(val)) => {
1✔
713
                coord.k.replace(helpers::to_fixed(val));
1✔
714
            }
1✔
715
            //
716
            #[cfg(feature = "with-u-axis")]
717
            ('u', Some(val)) => {
1✔
718
                coord.u.replace(helpers::to_fixed(val));
1✔
719
            }
1✔
720
            #[cfg(feature = "with-v-axis")]
721
            ('v', Some(val)) => {
1✔
722
                coord.v.replace(helpers::to_fixed(val));
1✔
723
            }
1✔
724
            #[cfg(feature = "with-w-axis")]
725
            ('w', Some(val)) => {
1✔
726
                coord.w.replace(helpers::to_fixed(val));
1✔
727
            }
1✔
728
            _ => {}
133,818✔
729
        },
730
        #[cfg(feature = "with-motion")]
731
        GCodeValue::G4(param) => match (ch, frx) {
6✔
732
            ('s', Some(val)) => {
6✔
733
                param.s.replace(helpers::to_fixed(val).abs());
6✔
734
            }
6✔
735
            _ => {}
×
736
        },
737
        #[cfg(feature = "with-motion")]
738
        GCodeValue::G28(coord) => match (ch, frx) {
39✔
739
            #[cfg(feature = "with-e-axis")]
740
            ('e', Some(val)) => {
2✔
741
                coord.e.replace(helpers::to_fixed(val));
2✔
742
            }
2✔
743
            //
744
            #[cfg(feature = "with-x-axis")]
745
            ('x', Some(val)) => {
6✔
746
                coord.x.replace(helpers::to_fixed(val));
6✔
747
            }
6✔
748
            #[cfg(feature = "with-y-axis")]
749
            ('y', Some(val)) => {
6✔
750
                coord.y.replace(helpers::to_fixed(val));
6✔
751
            }
6✔
752
            #[cfg(feature = "with-z-axis")]
753
            ('z', Some(val)) => {
6✔
754
                coord.z.replace(helpers::to_fixed(val));
6✔
755
            }
6✔
756
            //
757
            #[cfg(feature = "with-a-axis")]
758
            ('a', Some(val)) => {
×
759
                coord.a.replace(helpers::to_fixed(val));
×
760
            }
×
761
            #[cfg(feature = "with-b-axis")]
762
            ('b', Some(val)) => {
×
763
                coord.b.replace(helpers::to_fixed(val));
×
764
            }
×
765
            #[cfg(feature = "with-c-axis")]
766
            ('c', Some(val)) => {
×
767
                coord.c.replace(helpers::to_fixed(val));
×
768
            }
×
769
            #[cfg(feature = "with-i-axis")]
770
            ('i', Some(val)) => {
×
771
                coord.i.replace(helpers::to_fixed(val));
×
772
            }
×
773
            #[cfg(feature = "with-j-axis")]
774
            ('j', Some(val)) => {
×
775
                coord.j.replace(helpers::to_fixed(val));
×
776
            }
×
777
            #[cfg(feature = "with-k-axis")]
778
            ('k', Some(val)) => {
×
779
                coord.k.replace(helpers::to_fixed(val));
×
780
            }
×
781
            #[cfg(feature = "with-u-axis")]
782
            ('u', Some(val)) => {
×
783
                coord.u.replace(helpers::to_fixed(val));
×
784
            }
×
785
            #[cfg(feature = "with-v-axis")]
786
            ('v', Some(val)) => {
×
787
                coord.v.replace(helpers::to_fixed(val));
×
788
            }
×
789
            #[cfg(feature = "with-w-axis")]
790
            ('w', Some(val)) => {
×
791
                coord.w.replace(helpers::to_fixed(val));
×
792
            }
×
793
            _ => {}
19✔
794
        },
795
        #[cfg(feature = "with-motion")]
796
        GCodeValue::G92(coord) => match (ch, frx) {
812✔
797
            #[cfg(feature = "with-e-axis")]
798
            ('e', Some(val)) => {
486✔
799
                coord.e.replace(helpers::to_fixed(val));
486✔
800
            }
486✔
801
            //
802
            #[cfg(feature = "with-x-axis")]
803
            ('x', Some(val)) => {
7✔
804
                coord.x.replace(helpers::to_fixed(val));
7✔
805
            }
7✔
806
            #[cfg(feature = "with-y-axis")]
807
            ('y', Some(val)) => {
7✔
808
                coord.y.replace(helpers::to_fixed(val));
7✔
809
            }
7✔
810
            #[cfg(feature = "with-z-axis")]
811
            ('z', Some(val)) => {
6✔
812
                coord.z.replace(helpers::to_fixed(val));
6✔
813
            }
6✔
814
            //
815
            #[cfg(feature = "with-a-axis")]
816
            ('a', Some(val)) => {
1✔
817
                coord.a.replace(helpers::to_fixed(val));
1✔
818
            }
1✔
819
            #[cfg(feature = "with-b-axis")]
820
            ('b', Some(val)) => {
1✔
821
                coord.b.replace(helpers::to_fixed(val));
1✔
822
            }
1✔
823
            #[cfg(feature = "with-c-axis")]
824
            ('c', Some(val)) => {
1✔
825
                coord.c.replace(helpers::to_fixed(val));
1✔
826
            }
1✔
827
            //
828
            #[cfg(feature = "with-i-axis")]
829
            ('i', Some(val)) => {
1✔
830
                coord.i.replace(helpers::to_fixed(val));
1✔
831
            }
1✔
832
            #[cfg(feature = "with-j-axis")]
833
            ('j', Some(val)) => {
1✔
834
                coord.j.replace(helpers::to_fixed(val));
1✔
835
            }
1✔
836
            #[cfg(feature = "with-k-axis")]
837
            ('k', Some(val)) => {
1✔
838
                coord.k.replace(helpers::to_fixed(val));
1✔
839
            }
1✔
840
            //
841
            #[cfg(feature = "with-u-axis")]
842
            ('u', Some(val)) => {
1✔
843
                coord.u.replace(helpers::to_fixed(val));
1✔
844
            }
1✔
845
            #[cfg(feature = "with-v-axis")]
846
            ('v', Some(val)) => {
1✔
847
                coord.v.replace(helpers::to_fixed(val));
1✔
848
            }
1✔
849
            #[cfg(feature = "with-w-axis")]
850
            ('w', Some(val)) => {
1✔
851
                coord.w.replace(helpers::to_fixed(val));
1✔
852
            }
1✔
853
            _ => {}
297✔
854
        },
855
        #[cfg(feature = "with-sd-card")]
856
        GCodeValue::M20(path) => {
6✔
857
            if ch == 'f' {
6✔
858
                if let async_gcode::RealValue::Literal(async_gcode::Literal::String(mstr)) = fv {
6✔
859
                    path.replace(mstr);
6✔
860
                }
6✔
861
            }
×
862
        }
863
        #[cfg(all(feature = "with-sd-card", feature = "with-print-job"))]
864
        GCodeValue::M23(file) => {
18✔
865
            if let async_gcode::RealValue::Literal(async_gcode::Literal::String(mstr)) = fv {
18✔
866
                file.replace(mstr);
18✔
867
            }
18✔
868
        }
869
        GCodeValue::M37(coord) => match (ch, frx) {
6✔
870
            ('s', Some(val)) => {
6✔
871
                coord.s.replace(helpers::to_fixed(val));
6✔
872
            }
6✔
873
            _ => {}
×
874
        },
875
        #[cfg(feature = "with-hot-end")]
876
        GCodeValue::M104(coord) => match (ch, frx) {
6✔
877
            ('s', Some(val)) => {
6✔
878
                coord.s.replace(helpers::to_fixed(val));
6✔
879
            }
6✔
880
            _ => {}
×
881
        },
882
        #[cfg(feature = "with-hot-end")]
883
        GCodeValue::M109(coord) => match (ch, frx) {
2✔
884
            ('s', Some(val)) => {
2✔
885
                coord.s.replace(helpers::to_fixed(val));
2✔
886
            }
2✔
887
            _ => {}
×
888
        },
889
        GCodeValue::M117(msg) | GCodeValue::M118(msg) => {
6✔
890
            if let async_gcode::RealValue::Literal(async_gcode::Literal::String(text)) = fv {
12✔
891
                msg.replace(text);
12✔
892
            }
12✔
893
        }
894
        #[cfg(feature = "with-hot-bed")]
895
        GCodeValue::M140(coord) => match (ch, frx) {
4✔
896
            ('s', Some(val)) => {
4✔
897
                coord.s.replace(helpers::to_fixed(val));
4✔
898
            }
4✔
899
            _ => {}
×
900
        },
901
        GCodeValue::M220(coord) => match (ch, frx) {
6✔
902
            ('s', Some(val)) => {
6✔
903
                coord.s.replace(helpers::to_fixed(val));
6✔
904
            }
6✔
905
            _ => {}
×
906
        },
907
        GCodeValue::M110(coord) => match (ch, frx) {
6✔
908
            ('n', Some(val)) => {
6✔
909
                coord.n.replace(helpers::to_fixed(val));
6✔
910
            }
6✔
911
            _ => {}
×
912
        },
913
        GCodeValue::M503(param) => match (ch, frx) {
×
914
            ('s', Some(val)) => {
×
915
                param.s.replace(helpers::to_fixed(val));
×
916
            }
×
917
            _ => {}
×
918
        },
919
        _ => {}
12,502✔
920
    }
921
}
1,286,463✔
922

923
#[cfg(feature = "native")]
924
#[cfg(test)]
925
mod tests {
926
    use super::*;
927
    use async_gcode::{AsyncParserState, ByteStream};
928
    use std::collections::VecDeque;
929

930
    struct BufferStream {
931
        buff: VecDeque<u8>,
932
    }
933

934
    impl BufferStream {
935
        const fn new(buff: VecDeque<u8>) -> Self {
204✔
936
            Self { buff }
204✔
937
        }
204✔
938
    }
939

940
    impl async_gcode::ByteStream for BufferStream {
941
        type Item = Result<u8, async_gcode::Error>;
942

943
        async fn next(&mut self) -> Option<Self::Item> {
2,526✔
944
            match self.buff.pop_front() {
2,526✔
945
                None => None,
78✔
946
                Some(_b) => Some(Ok(_b)),
2,448✔
947
            }
948
        }
2,526✔
949

950
        async fn recovery_check(&mut self) {}
18✔
951
    }
952

953
    async fn check_m32_benchy_result(parser: &mut GCodeLineParser<BufferStream>) {
18✔
954
        match parser.next_gcode(CommChannel::Internal).await {
18✔
955
            Ok(_cmd) => match _cmd.value {
18✔
956
                GCodeValue::M23(Some(_path)) => {
18✔
957
                    assert!(_path.eq("BENCHY.G"), "Got the proper string param")
18✔
958
                }
959
                _ => assert!(false, "Unexpected code value"),
×
960
            },
961
            Err(_e) => {
×
962
                assert!(false, "Got an error");
×
963
            }
964
        }
965
    }
18✔
966

967
    async fn check_display_m117_result(parser: &mut GCodeLineParser<BufferStream>) {
6✔
968
        match parser.next_gcode(CommChannel::Internal).await {
6✔
969
            Ok(_cmd) => match _cmd.value {
6✔
970
                GCodeValue::M117(Some(_msg)) => {
6✔
971
                    assert!(_msg.eq("Hello World!"), "Got the proper string param")
6✔
972
                }
973
                _ => assert!(false, "Unexpected code value"),
×
974
            },
975
            Err(_e) => {
×
976
                assert!(false, "Got an error");
×
977
            }
978
        }
979
    }
6✔
980
    async fn check_display_m118_result(parser: &mut GCodeLineParser<BufferStream>) {
6✔
981
        match parser.next_gcode(CommChannel::Internal).await {
6✔
982
            Ok(_cmd) => match _cmd.value {
6✔
983
                GCodeValue::M118(Some(_msg)) => {
6✔
984
                    assert!(_msg.eq("Hello World!"), "Got the proper string param")
6✔
985
                }
986
                _ => assert!(false, "Unexpected code value"),
×
987
            },
988
            Err(_e) => {
×
989
                assert!(false, "Got an error");
×
990
            }
991
        }
992
    }
6✔
993
    #[futures_test::test]
994
    async fn test_m23_with_newline() {
6✔
995
        let stream = BufferStream::new("M23 BENCHY.G\n".as_bytes().to_vec().into());
6✔
996
        let mut parser = GCodeLineParser::new(stream);
6✔
997
        check_m32_benchy_result(&mut parser).await;
6✔
998
    }
12✔
999

1000
    #[futures_test::test]
1001
    async fn test_m23_with_eof_condition() {
6✔
1002
        let stream = BufferStream::new("M23 BENCHY.G".as_bytes().to_vec().into());
6✔
1003
        let mut parser = GCodeLineParser::new(stream);
6✔
1004
        check_m32_benchy_result(&mut parser).await;
6✔
1005
    }
12✔
1006

1007
    #[futures_test::test]
1008
    async fn test_m23_with_trailin_comments() {
6✔
1009
        let stream = BufferStream::new("M23 BENCHY.G".as_bytes().to_vec().into());
6✔
1010
        let mut parser = GCodeLineParser::new(stream);
6✔
1011
        check_m32_benchy_result(&mut parser).await;
6✔
1012
    }
12✔
1013

1014
    #[futures_test::test]
1015
    async fn test_m117() {
6✔
1016
        let stream = BufferStream::new("M117 Hello World!".as_bytes().to_vec().into());
6✔
1017
        let mut parser = GCodeLineParser::new(stream);
6✔
1018
        check_display_m117_result(&mut parser).await;
6✔
1019
    }
12✔
1020

1021
    #[futures_test::test]
1022
    async fn test_m118() {
6✔
1023
        let stream = BufferStream::new("M118 Hello World!".as_bytes().to_vec().into());
6✔
1024
        let mut parser = GCodeLineParser::new(stream);
6✔
1025
        check_display_m118_result(&mut parser).await;
6✔
1026
    }
12✔
1027

1028
    #[futures_test::test]
1029
    async fn test_gs() {
6✔
1030
        // TODO: Check EOF condition
6✔
1031
        let stream = BufferStream::new(
6✔
1032
            "G0 F0 E0 X0 Y0 Z0 A0 B0 C0 I0 J0 K0 U0 V0 W0\n"
6✔
1033
                .as_bytes()
6✔
1034
                .to_vec()
6✔
1035
                .into(),
6✔
1036
        );
6✔
1037
        let mut parser = GCodeLineParser::new(stream);
6✔
1038
        let _ = parser.gcode_line();
6✔
1039
        parser
6✔
1040
            .next_gcode(CommChannel::Internal)
6✔
1041
            .await
6✔
1042
            .expect("G0 OK");
6✔
1043

6✔
1044
        let stream = BufferStream::new(
6✔
1045
            "G1 F0 E0 X0 Y0 Z0 A0 B0 C0 I0 J0 K0 U0 V0 W0\n"
6✔
1046
                .as_bytes()
6✔
1047
                .to_vec()
6✔
1048
                .into(),
6✔
1049
        );
6✔
1050
        let mut parser = GCodeLineParser::new(stream);
6✔
1051
        let _ = parser.gcode_line();
6✔
1052
        parser
6✔
1053
            .next_gcode(CommChannel::Internal)
6✔
1054
            .await
6✔
1055
            .expect("G1 OK");
6✔
1056

6✔
1057
        let stream = BufferStream::new(
6✔
1058
            "G92 F0 E0 X0 Y0 Z0 A0 B0 C0 I0 J0 K0 U0 V0 W0\n"
6✔
1059
                .as_bytes()
6✔
1060
                .to_vec()
6✔
1061
                .into(),
6✔
1062
        );
6✔
1063
        let mut parser = GCodeLineParser::new(stream);
6✔
1064
        let _ = parser.gcode_line();
6✔
1065
        parser
6✔
1066
            .next_gcode(CommChannel::Internal)
6✔
1067
            .await
6✔
1068
            .expect("G1 OK");
6✔
1069

6✔
1070
        let stream = BufferStream::new("G4\n".as_bytes().to_vec().into());
6✔
1071
        let mut parser = GCodeLineParser::new(stream);
6✔
1072
        let _ = parser.gcode_line();
6✔
1073
        parser
6✔
1074
            .next_gcode(CommChannel::Internal)
6✔
1075
            .await
6✔
1076
            .expect("G4 OK");
6✔
1077

6✔
1078
        let stream = BufferStream::new("G4 S10\n".as_bytes().to_vec().into());
6✔
1079
        let mut parser = GCodeLineParser::new(stream);
6✔
1080
        let _ = parser.gcode_line();
6✔
1081
        parser
6✔
1082
            .next_gcode(CommChannel::Internal)
6✔
1083
            .await
6✔
1084
            .expect("G4 S10 OK");
6✔
1085

6✔
1086
        let stream = BufferStream::new("N10 G28 F0 E0 X0 Y0 Z0\n".as_bytes().to_vec().into());
6✔
1087
        let mut parser = GCodeLineParser::new(stream);
6✔
1088
        let _ = parser.gcode_line();
6✔
1089
        parser
6✔
1090
            .next_gcode(CommChannel::Internal)
6✔
1091
            .await
6✔
1092
            .expect("G28 OK");
6✔
1093

6✔
1094
        parser.reset_current_line();
6✔
1095

6✔
1096
        let stream = BufferStream::new("N10 G29 F0 E0 X0 Y0 Z0\n".as_bytes().to_vec().into());
6✔
1097
        let mut parser = GCodeLineParser::new(stream);
6✔
1098
        let _ = parser.gcode_line();
6✔
1099
        parser
6✔
1100
            .next_gcode(CommChannel::Internal)
6✔
1101
            .await
6✔
1102
            .expect("G29 OK");
6✔
1103

6✔
1104
        let stream = BufferStream::new("N11 G29.1 F0 E0 X0 Y0 Z0\n".as_bytes().to_vec().into());
6✔
1105
        let mut parser = GCodeLineParser::new(stream);
6✔
1106
        let _ = parser.gcode_line();
6✔
1107
        parser
6✔
1108
            .next_gcode(CommChannel::Internal)
6✔
1109
            .await
6✔
1110
            .expect("G29.1 OK");
6✔
1111

6✔
1112
        let stream = BufferStream::new("G29.2 F0 E0 X0 Y0 Z0\n".as_bytes().to_vec().into());
6✔
1113
        let mut parser = GCodeLineParser::new(stream);
6✔
1114
        let _ = parser.gcode_line();
6✔
1115
        parser
6✔
1116
            .next_gcode(CommChannel::Internal)
6✔
1117
            .await
6✔
1118
            .expect("G29.2 OK");
6✔
1119

6✔
1120
        let stream = BufferStream::new("G31\n".as_bytes().to_vec().into());
6✔
1121
        let mut parser = GCodeLineParser::new(stream);
6✔
1122
        let _ = parser.gcode_line();
6✔
1123
        parser
6✔
1124
            .next_gcode(CommChannel::Internal)
6✔
1125
            .await
6✔
1126
            .expect("G31 OK");
6✔
1127
    }
6✔
1128

1129
    #[futures_test::test]
1130
    async fn test_machinery() {
6✔
1131
        let mut stream = BufferStream::new("".as_bytes().to_vec().into());
6✔
1132
        assert_eq!(stream.next().await, None);
6✔
1133
        stream.recovery_check().await;
6✔
1134
        assert_eq!(stream.next().await, None);
6✔
1135

1136
        let spec = RawGCodeSpec::from('Z', None);
6✔
1137

6✔
1138
        let _str = format!("{:?}", spec);
6✔
1139

6✔
1140
        let stream = BufferStream::new("".as_bytes().to_vec().into());
6✔
1141
        let mut parser = GCodeLineParser::new(stream);
6✔
1142
        parser
6✔
1143
            .next_gcode(CommChannel::Internal)
6✔
1144
            .await
6✔
1145
            .expect_err("None");
6✔
1146
        assert_eq!(parser.get_state(), AsyncParserState::Start(true));
6✔
1147
        parser
6✔
1148
            .next_gcode(CommChannel::Internal)
6✔
1149
            .await
6✔
1150
            .expect_err("EOF");
6✔
1151
        assert_eq!(parser.get_state(), AsyncParserState::Start(true));
6✔
1152
        let _ = parser.close().await;
6✔
1153

1154
        let stream = BufferStream::new("/X0\n".as_bytes().to_vec().into());
6✔
1155
        let mut parser = GCodeLineParser::new(stream);
6✔
1156
        parser
6✔
1157
            .next_gcode(CommChannel::Internal)
6✔
1158
            .await
6✔
1159
            .expect_err("None");
6✔
1160

6✔
1161
        let stream = BufferStream::new("&0\n".as_bytes().to_vec().into());
6✔
1162
        let mut parser = GCodeLineParser::new(stream);
6✔
1163
        parser
6✔
1164
            .next_gcode(CommChannel::Internal)
6✔
1165
            .await
6✔
1166
            .expect_err("None");
6✔
1167

6✔
1168
        let stream = BufferStream::new("V0\n".as_bytes().to_vec().into());
6✔
1169
        let mut parser = GCodeLineParser::new(stream);
6✔
1170
        parser
6✔
1171
            .next_gcode(CommChannel::Internal)
6✔
1172
            .await
6✔
1173
            .expect_err("None");
6✔
1174
    }
6✔
1175

1176
    #[futures_test::test]
1177
    async fn test_ms() {
6✔
1178
        let stream = BufferStream::new("M20 F\"X.g\"\n".as_bytes().to_vec().into());
6✔
1179
        let mut parser = GCodeLineParser::new(stream);
6✔
1180
        let _ = parser.gcode_line();
6✔
1181
        parser
6✔
1182
            .next_gcode(CommChannel::Internal)
6✔
1183
            .await
6✔
1184
            .expect("M20 OK");
6✔
1185

6✔
1186
        let stream = BufferStream::new("M37\n".as_bytes().to_vec().into());
6✔
1187
        let mut parser = GCodeLineParser::new(stream);
6✔
1188
        let _ = parser.gcode_line();
6✔
1189
        parser
6✔
1190
            .next_gcode(CommChannel::Internal)
6✔
1191
            .await
6✔
1192
            .expect("M37 OK");
6✔
1193

6✔
1194
        let stream = BufferStream::new("M37 S1\n".as_bytes().to_vec().into());
6✔
1195
        let mut parser = GCodeLineParser::new(stream);
6✔
1196
        let _ = parser.gcode_line();
6✔
1197
        parser
6✔
1198
            .next_gcode(CommChannel::Internal)
6✔
1199
            .await
6✔
1200
            .expect("M37 S1 OK");
6✔
1201

6✔
1202
        #[cfg(feature = "with-hot-bed")]
6✔
1203
        {
6✔
1204
            let stream = BufferStream::new("M140\n".as_bytes().to_vec().into());
6✔
1205
            let mut parser = GCodeLineParser::new(stream);
6✔
1206
            let _ = parser.gcode_line();
6✔
1207
            parser
6✔
1208
                .next_gcode(CommChannel::Internal)
6✔
1209
                .await
6✔
1210
                .expect("M140 OK");
6✔
1211
        }
6✔
1212

6✔
1213
        #[cfg(feature = "with-hot-end")]
6✔
1214
        {
6✔
1215
            let stream = BufferStream::new("M104\n".as_bytes().to_vec().into());
6✔
1216
            let mut parser = GCodeLineParser::new(stream);
6✔
1217
            let _ = parser.gcode_line();
6✔
1218
            parser
6✔
1219
                .next_gcode(CommChannel::Internal)
6✔
1220
                .await
6✔
1221
                .expect("M104 OK");
6✔
1222

6✔
1223
            let stream = BufferStream::new("M109\n".as_bytes().to_vec().into());
6✔
1224
            let mut parser = GCodeLineParser::new(stream);
6✔
1225
            let _ = parser.gcode_line();
6✔
1226
            parser
6✔
1227
                .next_gcode(CommChannel::Internal)
6✔
1228
                .await
6✔
1229
                .expect("M109 OK");
6✔
1230
        }
6✔
1231

6✔
1232
        let stream = BufferStream::new("M220\n".as_bytes().to_vec().into());
6✔
1233
        let mut parser = GCodeLineParser::new(stream);
6✔
1234
        let _ = parser.gcode_line();
6✔
1235
        parser
6✔
1236
            .next_gcode(CommChannel::Internal)
6✔
1237
            .await
6✔
1238
            .expect("M220 OK");
6✔
1239

6✔
1240
        let stream = BufferStream::new("M220 S1\n".as_bytes().to_vec().into());
6✔
1241
        let mut parser = GCodeLineParser::new(stream);
6✔
1242
        let _ = parser.gcode_line();
6✔
1243
        parser
6✔
1244
            .next_gcode(CommChannel::Internal)
6✔
1245
            .await
6✔
1246
            .expect("M220 S1 OK");
6✔
1247

6✔
1248
        let stream = BufferStream::new("M110\n".as_bytes().to_vec().into());
6✔
1249
        let mut parser = GCodeLineParser::new(stream);
6✔
1250
        let _ = parser.gcode_line();
6✔
1251
        parser
6✔
1252
            .next_gcode(CommChannel::Internal)
6✔
1253
            .await
6✔
1254
            .expect("M110 OK");
6✔
1255

6✔
1256
        parser.reset_current_line();
6✔
1257
        parser.reset().await;
6✔
1258

1259
        let stream = BufferStream::new("M110 N1\n".as_bytes().to_vec().into());
6✔
1260
        let mut parser = GCodeLineParser::new(stream);
6✔
1261
        let _ = parser.gcode_line();
6✔
1262
        parser
6✔
1263
            .next_gcode(CommChannel::Internal)
6✔
1264
            .await
6✔
1265
            .expect("M110 N1 OK");
6✔
1266

6✔
1267
        let stream = BufferStream::new("M204\n".as_bytes().to_vec().into());
6✔
1268
        let mut parser = GCodeLineParser::new(stream);
6✔
1269
        let _ = parser.gcode_line();
6✔
1270
        parser
6✔
1271
            .next_gcode(CommChannel::Internal)
6✔
1272
            .await
6✔
1273
            .expect("M204 OK");
6✔
1274

6✔
1275
        let stream = BufferStream::new("M205\n".as_bytes().to_vec().into());
6✔
1276
        let mut parser = GCodeLineParser::new(stream);
6✔
1277
        let _ = parser.gcode_line();
6✔
1278
        parser
6✔
1279
            .next_gcode(CommChannel::Internal)
6✔
1280
            .await
6✔
1281
            .expect("M205 OK");
6✔
1282

6✔
1283
        let stream = BufferStream::new("M862.1\n".as_bytes().to_vec().into());
6✔
1284
        let mut parser = GCodeLineParser::new(stream);
6✔
1285
        let _ = parser.gcode_line();
6✔
1286
        parser
6✔
1287
            .next_gcode(CommChannel::Internal)
6✔
1288
            .await
6✔
1289
            .expect("M862.1 OK");
6✔
1290

6✔
1291
        let stream = BufferStream::new("M862.3\n".as_bytes().to_vec().into());
6✔
1292
        let mut parser = GCodeLineParser::new(stream);
6✔
1293
        let _ = parser.gcode_line();
6✔
1294
        parser
6✔
1295
            .next_gcode(CommChannel::Internal)
6✔
1296
            .await
6✔
1297
            .expect("M862.3 OK");
6✔
1298

6✔
1299
        let stream = BufferStream::new("M900\n".as_bytes().to_vec().into());
6✔
1300
        let mut parser = GCodeLineParser::new(stream);
6✔
1301
        let _ = parser.gcode_line();
6✔
1302
        parser
6✔
1303
            .next_gcode(CommChannel::Internal)
6✔
1304
            .await
6✔
1305
            .expect("M900 OK");
6✔
1306

6✔
1307
        let stream = BufferStream::new("M907\n".as_bytes().to_vec().into());
6✔
1308
        let mut parser = GCodeLineParser::new(stream);
6✔
1309
        let _ = parser.gcode_line();
6✔
1310
        parser
6✔
1311
            .next_gcode(CommChannel::Internal)
6✔
1312
            .await
6✔
1313
            .expect("M907 OK");
6✔
1314
    }
6✔
1315
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc