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

cbruiz / printhor / 13594535099

28 Feb 2025 06:48PM UTC coverage: 88.898% (+0.8%) from 88.116%
13594535099

Pull #34

github

web-flow
Merge 64bcbae9d into 6708673b5
Pull Request #34: Refactor to clean-up dead code

298 of 307 new or added lines in 7 files covered. (97.07%)

1 existing line in 1 file now uncovered.

13509 of 15196 relevant lines covered (88.9%)

804046.82 hits per line

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

83.99
/printhor/src/bin/control/processing/gcode_processor.rs
1
//! This module defines the G-code processing logic for a 3D printer control system.
2
//!
3
//! The purpose of this module is to handle various aspects of G-code interpretation
4
//! and execution, including communication with hardware controllers and asynchronous
5
//! message handling. The module includes structures and functions that facilitate
6
//! the execution of G-codes on the hardware components of the 3D printer.
7
//!
8
//! # Structures
9
//!
10
//! - `GCodeProcessorParams`: Contains configuration parameters and references to
11
//!   various hardware controllers needed for G-code processing.
12
//! - `GCodeProcessor`: The main G-code processor structure that includes methods for
13
//!   writing responses to communication channels and handling G-code execution.
14
//!
15
//! # Features
16
//!
17
//! The module supports conditional compilation based on feature flags. The following
18
//! feature flags are used:
19
//!
20
//! - `with-motion`: Enables motion planning features.
21
//! - `with-serial-usb`: Enables serial USB communication.
22
//! - `with-serial-port-1`, `with-serial-port-2`: Enable serial port communication for two ports.
23
//! - `with-ps-on`: Enables PSU control.
24
//! - `with-probe`: Enables probe hardware support.
25
//! - `with-hot-end`, `with-hot-bed`: Enable hotend and hotbed control respectively.
26
//! - `with-fan-layer`, `with-fan-extra-1`: Enable additional fan control.
27
//! - `with-laser`: Enables laser PWM control.
28
//!
29
//! # Usage
30
//!
31
//! To use the GCodeProcessor, create an instance of `GCodeProcessorParams` with the
32
//! needed references and initialize `GCodeProcessor` using the `new` method. Utilize
33
//! the provided async methods to write to communication channels and handle G-code commands.
34
//!
35
//! # Remark
36
//! Do not **format** on f32 directly and use [math::Real] instead to avoid large code generation.
37
//!
38
//! Reasoning: core::fmt::float::impl_general_format macro expansion will take around:
39
//! - 7.6KiB by [core::fmt::float::float_to_decimal_common_shortest]
40
//! - 6.3KiB by [core::fmt::float::float_to_decimal_common_exact]
41
//!
42
//! Around 14KB in total
43
//!
44
//! Actually, 5.1KiB only using [math::Real::format] and/or [math::Real::fmt] with lexical_core/ryu.
45

46
use crate::control;
47
use crate::hwa;
48
use control::{CodeExecutionFailure, CodeExecutionResult, CodeExecutionSuccess};
49
use control::{GCodeCmd, GCodeValue};
50
use hwa::Contract;
51
use hwa::math;
52
#[allow(unused)]
53
use hwa::{AsyncMutexStrategy, HwiContract, SyncMutexStrategy};
54

55
#[cfg(feature = "with-probe")]
56
use hwa::controllers::ProbeTrait;
57
#[allow(unused)]
58
use hwa::{CommChannel, DeferAction, DeferEvent, EventFlags, EventStatus};
59
use strum::VariantNames;
60

61
cfg_if::cfg_if! {
62
    if #[cfg(any(feature = "with-serial-port-1", feature="with-serial-port-2"))]
63
    {
64
        #[allow(unused)]
65
        use embedded_io_async::Write;
66
    }
67
}
68

69
#[derive(Clone)]
70
pub struct GCodeProcessor {
71
    pub event_bus: hwa::types::EventBus,
72

73
    #[cfg(feature = "with-motion")]
74
    pub motion_planner: hwa::controllers::MotionPlanner,
75

76
    #[cfg(feature = "with-serial-usb")]
77
    pub serial_usb_tx: hwa::types::SerialUsbTxController,
78

79
    #[cfg(feature = "with-serial-port-1")]
80
    pub serial_port1_tx: hwa::types::SerialPort1TxController,
81

82
    #[cfg(feature = "with-serial-port-2")]
83
    pub serial_port2_tx: hwa::types::SerialPort2TxController,
84

85
    #[cfg(feature = "with-ps-on")]
86
    pub ps_on: hwa::types::PSOnController,
87

88
    #[cfg(feature = "with-probe")]
89
    pub probe: hwa::types::ProbeController,
90

91
    #[cfg(feature = "with-hot-end")]
92
    pub hot_end: hwa::types::HotEndController,
93

94
    #[cfg(feature = "with-hot-bed")]
95
    pub hot_bed: hwa::types::HotBedController,
96

97
    #[cfg(feature = "with-fan-layer")]
98
    pub fan_layer: hwa::types::FanLayerController,
99

100
    #[cfg(feature = "with-fan-extra-1")]
101
    pub fan_extra1: hwa::types::FanExtra1Controller,
102

103
    #[cfg(feature = "with-laser")]
104
    pub _laser: hwa::types::LaserController,
105
}
106

107
impl GCodeProcessor {
108
    pub const fn new(
4✔
109
        event_bus: hwa::types::EventBus,
4✔
110
        #[cfg(feature = "with-serial-usb")] serial_usb_tx: hwa::types::SerialUsbTxController,
4✔
111
        #[cfg(feature = "with-serial-port-1")] serial_port1_tx: hwa::types::SerialPort1TxController,
4✔
112
        #[cfg(feature = "with-serial-port-2")] serial_port2_tx: hwa::types::SerialPort2TxController,
4✔
113
        #[cfg(feature = "with-motion")] motion_planner: hwa::controllers::MotionPlanner,
4✔
114
        #[cfg(feature = "with-ps-on")] ps_on: hwa::types::PSOnController,
4✔
115

4✔
116
        #[cfg(feature = "with-probe")] probe: hwa::types::ProbeController,
4✔
117

4✔
118
        #[cfg(feature = "with-hot-end")] hot_end: hwa::types::HotEndController,
4✔
119
        #[cfg(feature = "with-hot-bed")] hot_bed: hwa::types::HotBedController,
4✔
120

4✔
121
        #[cfg(feature = "with-fan-layer")] fan_layer: hwa::types::FanLayerController,
4✔
122

4✔
123
        #[cfg(feature = "with-fan-extra-1")] fan_extra1: hwa::types::FanExtra1Controller,
4✔
124

4✔
125
        #[cfg(feature = "with-laser")] _laser: hwa::types::LaserController,
4✔
126
    ) -> Self {
4✔
127
        Self {
4✔
128
            event_bus,
4✔
129
            #[cfg(feature = "with-serial-usb")]
4✔
130
            serial_usb_tx,
4✔
131
            #[cfg(feature = "with-serial-port-1")]
4✔
132
            serial_port1_tx,
4✔
133
            #[cfg(feature = "with-serial-port-2")]
4✔
134
            serial_port2_tx,
4✔
135
            #[cfg(feature = "with-ps-on")]
4✔
136
            ps_on,
4✔
137
            #[cfg(feature = "with-motion")]
4✔
138
            motion_planner,
4✔
139
            #[cfg(feature = "with-probe")]
4✔
140
            probe,
4✔
141
            #[cfg(feature = "with-hot-end")]
4✔
142
            hot_end,
4✔
143
            #[cfg(feature = "with-hot-bed")]
4✔
144
            hot_bed,
4✔
145
            #[cfg(feature = "with-fan-layer")]
4✔
146
            fan_layer,
4✔
147
            #[cfg(feature = "with-fan-extra-1")]
4✔
148
            fan_extra1,
4✔
149
            #[cfg(feature = "with-laser")]
4✔
150
            _laser,
4✔
151
        }
4✔
152
    }
4✔
153

154
    pub async fn write_ok(&self, channel: CommChannel) {
23,984✔
155
        self.write(channel, "ok\n").await;
23,984✔
156
    }
23,984✔
157

158
    pub async fn write(&self, channel: CommChannel, _msg: &str) {
24,947✔
159
        match channel {
24,947✔
160
            #[cfg(feature = "with-serial-usb")]
161
            CommChannel::SerialUsb => {
162
                let _ = self
×
163
                    .serial_usb_tx
×
164
                    .lock()
×
165
                    .await
×
166
                    .write_packet(_msg.as_bytes())
×
167
                    .await;
×
168
            }
169
            #[cfg(feature = "with-serial-port-1")]
170
            CommChannel::SerialPort1 => {
12✔
171
                #[allow(unused)]
12✔
172
                use hwa::AsyncWrapperWriter;
12✔
173
                let mut mg = self.serial_port1_tx.lock().await;
12✔
174
                let _ = mg.wrapped_write(_msg.as_bytes()).await;
12✔
175
                mg.wrapped_flush().await;
12✔
176
            }
177
            #[cfg(feature = "with-serial-port-2")]
178
            CommChannel::SerialPort2 => {
×
179
                #[allow(unused)]
×
180
                use hwa::AsyncWrapperWriter;
×
181
                let mut mg = self.serial_port2_tx.lock().await;
×
182
                let _ = mg.wrapped_write(_msg.as_bytes()).await;
×
183
                mg.wrapped_flush().await;
×
184
            }
185
            CommChannel::Sink => {}
24,004✔
186
            CommChannel::Internal => {
187
                cfg_if::cfg_if! {
188
                    if #[cfg(feature="native")] {
189
                        use std::io::Write;
190
                        // In native backend, we will use stdout as convention for internal output
191
                        std::print!("<< {}", _msg);
931✔
192
                        if std::io::stdout().flush().is_err() {
931✔
193
                            hwa::error!("[Internal] Could not flush (unexpectedly)");
×
194
                        }
931✔
195
                    }
196
                    else {
197
                        // A message shall be always end by \n, so we need to trim it for pretty printing
198
                        hwa::info!("[Internal] {}", _msg.trim_end());
199
                    }
200
                }
201
            }
202
        }
203
    }
24,947✔
204

205
    #[allow(unused)]
206
    pub async fn flush(&self, channel: CommChannel) {
×
207
        match channel {
×
208
            #[cfg(feature = "with-serial-usb")]
209
            CommChannel::SerialUsb => {
×
210
                //let _ = self.serial_usb_tx.lock().await.write_packet(b"").await;
×
211
            }
×
212
            #[cfg(feature = "with-serial-port-1")]
213
            CommChannel::SerialPort1 => {
×
214
                use hwa::AsyncWrapperWriter;
×
215
                let mut mg = self.serial_port1_tx.lock().await;
×
216
                mg.wrapped_flush().await;
×
217
            }
218
            #[cfg(feature = "with-serial-port-2")]
219
            CommChannel::SerialPort2 => {
×
220
                use hwa::AsyncWrapperWriter;
×
221
                let mut mg = self.serial_port2_tx.lock().await;
×
222
                mg.wrapped_flush().await;
×
223
            }
224
            CommChannel::Internal => {}
×
225
            CommChannel::Sink => {}
×
226
        }
227
    }
×
228

229
    /// Executes the given GCode command.
230
    ///
231
    /// # Arguments
232
    ///
233
    /// * `channel` - The communication channel to use for the execution.
234
    /// * `gc` - The GCode command to execute.
235
    /// * `_blocking` - A boolean indicating whether the execution should be blocking or not.
236
    ///
237
    /// # Returns
238
    ///
239
    /// * `CodeExecutionResult` - The result of the GCode command execution.
240
    ///
241
    /// # Errors
242
    ///
243
    /// This method may return `CodeExecutionFailure::PowerRequired` if the ATX power is not on
244
    /// and the command requires power. It may also return `CodeExecutionFailure::BUSY` if the
245
    /// system is currently busy, for instance during homing or probing.
246
    ///
247
    /// # Features
248
    ///
249
    /// This method supports conditional compilation flags such as `grbl-compat`, `with-motion`,
250
    /// and `with-probe`. Based on these features, the behavior and supported GCode commands
251
    /// will vary.
252
    ///
253
    /// # Async
254
    ///
255
    /// This is an asynchronous method and should be awaited.
256
    pub async fn execute(
452,095✔
257
        &mut self,
452,095✔
258
        channel: CommChannel,
452,095✔
259
        gc: &GCodeCmd,
452,095✔
260
        _blocking: bool,
452,095✔
261
    ) -> CodeExecutionResult {
452,095✔
262
        let result = match &gc.value {
452,095✔
263
            #[cfg(feature = "grbl-compat")]
264
            GCodeValue::GRBLCmd => {
265
                let str = format!(
266
                    "[VER:1.1 {} v{}:]\n[MSG: Machine: {} {}]\n",
267
                    Contract::FIRMWARE_NAME,
268
                    Contract::FIRMWARE_VERSION,
269
                    Contract::MACHINE_BOARD,
270
                    Contract::MACHINE_TYPE,
271
                );
272
                self.write(channel, str.as_str()).await;
273
                Ok(CodeExecutionSuccess::OK)
274
            }
275
            GCodeValue::G => {
276
                for x in GCodeValue::VARIANTS.iter().filter(|x| x.starts_with("G")) {
556✔
277
                    let _ = self
132✔
278
                        .write(channel, alloc::format!("echo: {}\n", x).as_str())
132✔
279
                        .await;
132✔
280
                }
281
                Ok(CodeExecutionSuccess::OK)
4✔
282
            }
283
            #[cfg(feature = "with-motion")]
284
            GCodeValue::G0(_) | GCodeValue::G1(_) => {
285
                #[cfg(feature = "with-ps-on")]
286
                if !self
440,017✔
287
                    .event_bus
440,017✔
288
                    .get_status()
440,017✔
289
                    .await
440,017✔
290
                    .contains(EventFlags::ATX_ON)
440,017✔
291
                {
292
                    return Err(CodeExecutionFailure::PowerRequired);
4✔
293
                }
440,013✔
294
                Ok(self
440,013✔
295
                    .motion_planner
440,013✔
296
                    .plan(channel, &gc, _blocking, &self.event_bus)
440,013✔
297
                    .await?)
440,013✔
298
            }
299
            #[cfg(feature = "with-motion")]
300
            GCodeValue::G4(_) => Ok(self
11✔
301
                .motion_planner
11✔
302
                .plan(channel, &gc, _blocking, &self.event_bus)
11✔
303
                .await?),
11✔
304

305
            #[cfg(feature = "with-motion")]
306
            GCodeValue::G10 => Ok(CodeExecutionSuccess::OK),
×
307
            #[cfg(feature = "with-motion")]
308
            GCodeValue::G17 => Ok(CodeExecutionSuccess::OK),
×
309
            #[cfg(feature = "with-motion")]
310
            GCodeValue::G21 => Ok(CodeExecutionSuccess::OK),
10✔
311
            #[cfg(feature = "with-motion")]
312
            GCodeValue::G28(_) => match self.event_bus.has_flags(EventFlags::HOMING).await {
17✔
313
                true => Err(CodeExecutionFailure::BUSY),
×
314
                false => {
315
                    #[cfg(feature = "with-ps-on")]
316
                    if !self
17✔
317
                        .event_bus
17✔
318
                        .get_status()
17✔
319
                        .await
17✔
320
                        .contains(EventFlags::ATX_ON)
17✔
321
                    {
322
                        return Err(CodeExecutionFailure::PowerRequired);
×
323
                    }
17✔
324
                    hwa::debug!("Planing homing");
17✔
325
                    let result = self
17✔
326
                        .motion_planner
17✔
327
                        .plan(channel, &gc, _blocking, &self.event_bus)
17✔
328
                        .await;
17✔
329
                    hwa::debug!("Homing planned");
17✔
330
                    result
17✔
331
                }
332
            },
333
            #[cfg(feature = "with-probe")]
334
            GCodeValue::G31 => {
NEW
335
                if self.event_bus.has_flags(EventFlags::HOMING).await {
×
NEW
336
                    Err(CodeExecutionFailure::BUSY)
×
337
                } else {
338
                    #[cfg(feature = "with-ps-on")]
339
                    if !self
×
340
                        .event_bus
×
341
                        .get_status()
×
342
                        .await
×
343
                        .contains(EventFlags::ATX_ON)
×
344
                    {
345
                        return Err(CodeExecutionFailure::PowerRequired);
×
346
                    }
×
347
                    self.probe.lock().await.probe_pin_up(300_000).await;
×
348
                    Ok(CodeExecutionSuccess::OK)
×
349
                }
350
            }
351
            #[cfg(feature = "with-probe")]
352
            GCodeValue::G32 => {
353
                if self.event_bus.has_flags(EventFlags::HOMING).await {
×
354
                    Err(CodeExecutionFailure::BUSY)
×
355
                } else {
356
                    #[cfg(feature = "with-ps-on")]
357
                    if !self
×
358
                        .event_bus
×
359
                        .get_status()
×
360
                        .await
×
361
                        .contains(EventFlags::ATX_ON)
×
362
                    {
363
                        return Err(CodeExecutionFailure::PowerRequired);
×
364
                    }
×
365
                    let mut md = self.probe.lock().await;
×
366
                    md.probe_pin_down(300_000).await;
×
367
                    Ok(CodeExecutionSuccess::OK)
×
368
                }
369
            }
370
            GCodeValue::G80 => Ok(CodeExecutionSuccess::OK),
3✔
371
            #[cfg(feature = "with-motion")]
372
            GCodeValue::G90 => {
373
                self.motion_planner
13✔
374
                    .motion_status()
13✔
375
                    .set_absolute_positioning(true);
13✔
376
                Ok(CodeExecutionSuccess::OK)
13✔
377
            }
378
            #[cfg(feature = "with-motion")]
379
            GCodeValue::G91 => {
380
                self.motion_planner
×
381
                    .motion_status()
×
382
                    .set_absolute_positioning(false);
×
383
                Ok(CodeExecutionSuccess::OK)
×
384
            }
385
            #[cfg(feature = "with-motion")]
386
            GCodeValue::G92(_pos) => {
730✔
387
                self.motion_planner
730✔
388
                    .plan(channel, &gc, _blocking, &self.event_bus)
730✔
389
                    .await?;
730✔
390
                Ok(CodeExecutionSuccess::OK)
730✔
391
            }
392
            #[cfg(feature = "with-motion")]
393
            GCodeValue::G94 => Ok(CodeExecutionSuccess::OK),
×
394
            GCodeValue::M => {
395
                for x in GCodeValue::VARIANTS.iter().filter(|x| x.starts_with("M")) {
×
396
                    let _ = self
×
397
                        .write(channel, alloc::format!("echo: {}\n", x).as_str())
×
398
                        .await;
×
399
                }
400
                Ok(CodeExecutionSuccess::OK)
×
401
            }
402
            GCodeValue::M3 => Ok(CodeExecutionSuccess::OK),
×
403
            GCodeValue::M4 => Ok(CodeExecutionSuccess::OK),
4✔
404
            GCodeValue::M5 => Ok(CodeExecutionSuccess::OK),
4✔
405
            GCodeValue::M37(t) => {
6✔
406
                let dry_run_set = !t.s.unwrap_or(math::ZERO).is_zero();
6✔
407
                let _ = self
6✔
408
                    .event_bus
6✔
409
                    .publish_event(if dry_run_set {
6✔
410
                        EventStatus::containing(EventFlags::DRY_RUN)
3✔
411
                    } else {
412
                        EventStatus::not_containing(EventFlags::DRY_RUN)
3✔
413
                    })
414
                    .await;
6✔
415
                if dry_run_set {
6✔
416
                    let _ = self.write(channel, "echo: DRY_RUN mode enabled\n").await;
3✔
417
                } else {
418
                    let _ = self.write(channel, "echo: DRY_RUN mode disabled\n").await;
3✔
419
                }
420
                Ok(CodeExecutionSuccess::OK)
6✔
421
            }
422
            GCodeValue::M73 => Ok(CodeExecutionSuccess::OK),
1,176✔
423
            GCodeValue::M79 => {
424
                let _ = self.write(channel, "echo: Software reset\n").await;
×
425
                self.flush(channel).await;
×
426
                hwa::Contract::sys_reset();
×
427
                Ok(CodeExecutionSuccess::OK)
×
428
            }
429
            #[cfg(feature = "with-ps-on")]
430
            GCodeValue::M80 => {
431
                hwa::debug!("Received PowerOn");
7✔
432
                cfg_if::cfg_if! {
433
                    if #[cfg(feature = "with-ps-on")] {
434
                        self.ps_on.apply_mut(|pin| pin.set_high());
7✔
435
                        embassy_time::Timer::after_millis(500).await;
7✔
436
                    }
437
                }
438
                cfg_if::cfg_if! {
439
                    if #[cfg(feature = "with-trinamic")] {
440
                        let _ = self.motion_planner.motion_driver().lock().await.trinamic_controller.init().await.is_ok();
7✔
441
                    }
7✔
442
                }
7✔
443
                #[cfg(feature = "with-ps-on")]
7✔
444
                self.event_bus
7✔
445
                    .publish_event(EventStatus::containing(EventFlags::ATX_ON))
7✔
446
                    .await;
7✔
447
                Ok(CodeExecutionSuccess::OK)
7✔
448
            }
449
            #[cfg(feature = "with-ps-on")]
450
            GCodeValue::M81 => {
451
                hwa::info!("Received PowerOff");
7✔
452
                cfg_if::cfg_if! {
453
                    if #[cfg(feature = "with-ps-on")] {
454
                        self.ps_on.apply_mut(
7✔
455
                            |pin| pin.set_low()
7✔
456
                        );
7✔
457
                    }
7✔
458
                }
7✔
459
                #[cfg(feature = "with-ps-on")]
7✔
460
                self.event_bus
7✔
461
                    .publish_event(EventStatus::not_containing(EventFlags::ATX_ON))
7✔
462
                    .await;
7✔
463
                Ok(CodeExecutionSuccess::OK)
7✔
464
            }
465
            GCodeValue::M83 => Ok(CodeExecutionSuccess::OK),
6✔
466
            #[cfg(feature = "with-motion")]
467
            GCodeValue::M84 => Ok(CodeExecutionSuccess::OK),
3✔
468
            GCodeValue::M100 => {
469
                let heap_current = hwa::Contract::heap_current_size();
4✔
470
                let stack_current = hwa::Contract::stack_reservation_current_size();
4✔
471
                if Contract::MAX_HEAP_SIZE_BYTES > 0 {
4✔
472
                    let z1 = alloc::format!(
×
473
                        "echo: {}/{} bytes ({:?}%) of heap usage\n",
×
474
                        heap_current,
×
475
                        Contract::MAX_HEAP_SIZE_BYTES,
×
476
                        math::Real::from_f32(
×
477
                            100.0f32 * heap_current as f32 / Contract::MAX_HEAP_SIZE_BYTES as f32
×
478
                        ),
×
479
                    );
×
480
                    self.write(channel, z1.as_str()).await;
×
481
                } else {
482
                    let z1 = alloc::format!("echo: {} bytes of heap usage\n", heap_current);
4✔
483
                    self.write(channel, z1.as_str()).await;
4✔
484
                }
485

486
                let z2 = alloc::format!(
4✔
487
                    "echo: {}/{} bytes ({:?}%) of expected stack reservation\n",
4✔
488
                    stack_current,
4✔
489
                    Contract::MAX_EXPECTED_STATIC_ALLOC_BYTES,
4✔
490
                    math::Real::from_f32(
4✔
491
                        100.0f32 * stack_current as f32
4✔
492
                            / Contract::MAX_EXPECTED_STATIC_ALLOC_BYTES as f32
4✔
493
                    ),
4✔
494
                );
4✔
495
                self.write(channel, z2.as_str()).await;
4✔
496

497
                let status = self.event_bus.get_status().await;
4✔
498
                let z3 = alloc::format!("echo: Status: {:?}\n", status);
4✔
499
                self.write(channel, z3.as_str()).await;
4✔
500
                Ok(CodeExecutionSuccess::OK)
4✔
501
            }
502
            // Set hotend temperature
503
            // An immediate command
504
            #[cfg(feature = "with-hot-end")]
505
            GCodeValue::M104(s) => {
8✔
506
                #[cfg(feature = "with-ps-on")]
8✔
507
                if !self
8✔
508
                    .event_bus
8✔
509
                    .get_status()
8✔
510
                    .await
8✔
511
                    .contains(EventFlags::ATX_ON)
8✔
512
                {
513
                    return Err(CodeExecutionFailure::PowerRequired);
2✔
514
                }
6✔
515
                let val = s.s.and_then(|v| v.to_i32()).unwrap_or(0);
6✔
516
                let mut h = self.hot_end.lock().await;
6✔
517
                h.set_target_temp(
6✔
518
                    CommChannel::Internal,
6✔
519
                    DeferAction::HotEndTemperature,
6✔
520
                    val as f32,
6✔
521
                    gc.order_num,
6✔
522
                )
6✔
523
                .await;
6✔
524
                Ok(CodeExecutionSuccess::OK)
6✔
525
            }
526
            GCodeValue::M105 => {
527
                let hotend_temp_report = {
4✔
528
                    #[cfg(feature = "with-hot-end")]
4✔
529
                    {
4✔
530
                        let mut h = self.hot_end.lock().await;
4✔
531
                        alloc::format!(
4✔
532
                            " T:{:?} /{:?} T@:{:?} TZ:{:?}",
4✔
533
                            math::Real::from_f32(h.get_current_temp()),
4✔
534
                            math::Real::from_f32(h.get_target_temp()),
4✔
535
                            math::Real::from_f32(h.get_current_power()),
4✔
536
                            math::Real::from_f32(h.get_current_resistance()),
4✔
537
                        )
4✔
538
                    }
2✔
539
                    #[cfg(not(feature = "with-hot-end"))]
2✔
540
                    alloc::string::String::from(" T:0 /0 T@:0")
2✔
541
                };
2✔
542
                let hotbed_temp_report = {
4✔
543
                    #[cfg(feature = "with-hot-bed")]
4✔
544
                    {
4✔
545
                        let mut h = self.hot_bed.lock().await;
4✔
546
                        alloc::format!(
4✔
547
                            " B:{:?} /{:?} B@:{:?} BZ:{:?}",
4✔
548
                            math::Real::from_f32(h.get_current_temp()),
4✔
549
                            math::Real::from_f32(h.get_target_temp()),
4✔
550
                            math::Real::from_f32(h.get_current_power()),
4✔
551
                            math::Real::from_f32(h.get_current_resistance()),
4✔
552
                        )
4✔
553
                    }
4✔
554
                    #[cfg(not(feature = "with-hot-bed"))]
4✔
555
                    alloc::string::String::from(" B:0 /0 B@:0")
4✔
556
                };
4✔
557

4✔
558
                let report = alloc::format!("ok{}{}\n", hotend_temp_report, hotbed_temp_report);
4✔
559
                let _ = self.write(channel, report.as_str()).await;
4✔
560
                Ok(CodeExecutionSuccess::CONSUMED)
4✔
561
            }
562
            #[cfg(feature = "with-fan-layer")]
563
            GCodeValue::M106 => {
564
                #[cfg(feature = "with-ps-on")]
565
                if !self
642✔
566
                    .event_bus
642✔
567
                    .get_status()
642✔
568
                    .await
642✔
569
                    .contains(EventFlags::ATX_ON)
642✔
570
                {
571
                    return Err(CodeExecutionFailure::PowerRequired);
2✔
572
                }
640✔
573
                //crate::info!("M106 BEGIN");
640✔
574
                self.fan_layer.lock().await.set_power(255);
640✔
575
                //crate::info!("M106 END");
640✔
576
                Ok(CodeExecutionSuccess::OK)
640✔
577
            }
578
            #[cfg(feature = "with-fan-layer")]
579
            GCodeValue::M107 => {
580
                self.fan_layer.lock().await.set_power(0);
10✔
581
                Ok(CodeExecutionSuccess::OK)
10✔
582
            }
583
            // Wait for hot-end temperature
584
            // Mostly deferred code
585
            #[cfg(feature = "with-hot-end")]
586
            GCodeValue::M109(s) => {
4✔
587
                #[cfg(feature = "with-ps-on")]
4✔
588
                if !self
4✔
589
                    .event_bus
4✔
590
                    .get_status()
4✔
591
                    .await
4✔
592
                    .contains(EventFlags::ATX_ON)
4✔
593
                {
594
                    return Err(CodeExecutionFailure::PowerRequired);
2✔
595
                }
2✔
596
                let deferred = {
2✔
597
                    let mut he = self.hot_end.lock().await;
2✔
598
                    let value = s.s.and_then(|v| v.to_i32()).unwrap_or(0);
2✔
599
                    let was_deferred = he
2✔
600
                        .set_target_temp(
2✔
601
                            channel,
2✔
602
                            DeferAction::HotEndTemperature,
2✔
603
                            value as f32,
2✔
604
                            gc.order_num,
2✔
605
                        )
2✔
606
                        .await;
2✔
607
                    value > 0 && was_deferred
2✔
608
                };
609
                if deferred {
2✔
610
                    Ok(CodeExecutionSuccess::DEFERRED(EventStatus::containing(
×
611
                        EventFlags::HOT_END_TEMP_OK,
×
612
                    )))
×
613
                } else {
614
                    Ok(CodeExecutionSuccess::OK)
2✔
615
                }
616
            }
617
            GCodeValue::M110(_n) => Ok(CodeExecutionSuccess::OK),
×
618
            #[cfg(feature = "with-motion")]
619
            GCodeValue::M114 => {
620
                let _pos: hwa::controllers::Position = self
14✔
621
                    .motion_planner
14✔
622
                    .motion_status()
14✔
623
                    .get_last_planned_position();
14✔
624
                let _rpos: hwa::controllers::Position =
14✔
625
                    self.motion_planner.motion_status().get_current_position();
14✔
626
                hwa::debug!(
14✔
627
                    "M114: p_pos: [ {:?}], [{:?}] ] r_pos: [ {:?}], [{:?}] ]",
×
628
                    _pos.world_pos,
629
                    _pos.space_pos,
630
                    _rpos.world_pos,
631
                    _rpos.space_pos
632
                );
633
                let _step_pos: hwa::math::TVector<i32> = (_rpos.world_pos
14✔
634
                    * self
14✔
635
                        .motion_planner
14✔
636
                        .motion_config()
14✔
637
                        .get_steps_per_space_unit_as_vector())
14✔
638
                .map_values(|_c, _v| Some(_v.to_i32().unwrap_or(0)));
68✔
639
                cfg_if::cfg_if! {
14✔
640
                    if #[cfg(feature = "with-x-axis")] {
14✔
641
                        let r_x = _pos.world_pos.x.unwrap_or(math::ZERO);
14✔
642
                        let c_x = _step_pos.x.unwrap_or(0);
14✔
643
                    }
14✔
644
                    else {
14✔
645
                        let r_x = math::ZERO;
14✔
646
                        let c_x = math::ZERO;
14✔
647
                    }
14✔
648
                }
14✔
649
                cfg_if::cfg_if! {
14✔
650
                    if #[cfg(feature = "with-y-axis")] {
14✔
651
                        let r_y = _pos.world_pos.y.unwrap_or(math::ZERO);
14✔
652
                        let c_y = _step_pos.y.unwrap_or(0);
14✔
653
                    }
14✔
654
                    else {
14✔
655
                        let r_y = math::ZERO;
14✔
656
                        let c_y = math::ZERO;
14✔
657
                    }
14✔
658
                }
14✔
659
                cfg_if::cfg_if! {
14✔
660
                    if #[cfg(feature = "with-z-axis")] {
14✔
661
                        let r_z = _pos.world_pos.z.unwrap_or(math::ZERO);
14✔
662
                        let c_z = _step_pos.z.unwrap_or(0);
14✔
663
                    }
14✔
664
                    else {
14✔
665
                        let r_z = math::ZERO;
14✔
666
                        let c_z = math::ZERO;
14✔
667
                    }
14✔
668
                }
14✔
669
                cfg_if::cfg_if! {
14✔
670
                    if #[cfg(feature = "with-e-axis")] {
14✔
671
                        let r_e = _pos.world_pos.e.unwrap_or(math::ZERO);
14✔
672
                        let c_e = _step_pos.e.unwrap_or(0);
14✔
673
                    }
14✔
674
                    else {
14✔
675
                        let r_e = math::ZERO;
14✔
676
                        let c_e = math::ZERO;
14✔
677
                    }
14✔
678
                }
14✔
679
                let z = alloc::format!(
14✔
680
                    "X:{:?} Y:{:?} Z:{:?} E:{:?} Count: X:{:?} Y:{:?} Z:{:?} E:{:?}\n",
14✔
681
                    r_x,
14✔
682
                    r_y,
14✔
683
                    r_z,
14✔
684
                    r_e,
14✔
685
                    c_x,
14✔
686
                    c_y,
14✔
687
                    c_z,
14✔
688
                    c_e,
14✔
689
                );
14✔
690
                let _ = self.write(channel, z.as_str()).await;
14✔
691
                let z2 = alloc::format!("echo: Space {:#?}\n", _rpos.space_pos);
14✔
692
                let _ = self.write(channel, z2.as_str()).await;
14✔
693
                let _ = self.write(channel, "ok\n").await;
14✔
694
                Ok(CodeExecutionSuccess::CONSUMED)
14✔
695
            }
696
            GCodeValue::M115 => {
697
                let _ = self.write(channel, "echo: FIRMWARE_NAME: ").await;
4✔
698
                let _ = self.write(channel, hwa::Contract::FIRMWARE_NAME).await;
4✔
699
                let _ = self.write(channel, " FIRMWARE_VERSION: ").await;
4✔
700
                let _ = self.write(channel, hwa::Contract::FIRMWARE_VERSION).await;
4✔
701
                let _ = self.write(channel, " FIRMWARE_URL: ").await;
4✔
702
                let _ = self.write(channel, hwa::Contract::FIRMWARE_URL).await;
4✔
703
                let _ = self.write(channel, " MACHINE_TYPE: ").await;
4✔
704
                let _ = self.write(channel, hwa::Contract::MACHINE_TYPE).await;
4✔
705
                let _ = self.write(channel, " MACHINE_BOARD: ").await;
4✔
706
                let _ = self.write(channel, hwa::Contract::MACHINE_BOARD).await;
4✔
707
                let _ = self.write(channel, " MACHINE_PROCESSOR: ").await;
4✔
708
                let _ = self.write(channel, hwa::Contract::MACHINE_PROCESSOR).await;
4✔
709
                let _ = self.write(channel, " MACHINE_UUID: ").await;
4✔
710
                let _ = self.write(channel, hwa::Contract::MACHINE_UUID).await;
4✔
711
                let _ = self.write(channel, " EXTRUDER_COUNT: ").await;
4✔
712
                let _ = self.write(channel, hwa::Contract::EXTRUDER_COUNT).await;
4✔
713
                let _ = self.write(channel, "\n").await;
4✔
714
                Ok(CodeExecutionSuccess::OK)
4✔
715
            }
716
            GCodeValue::M117(_msg) => {
4✔
717
                if let Some(msg) = _msg {
4✔
718
                    let _ = self.write(Contract::DISPLAY_CHANNEL, "echo: ").await;
4✔
719
                    let _ = self.write(Contract::DISPLAY_CHANNEL, msg.as_str()).await;
4✔
720
                    let _ = self.write(Contract::DISPLAY_CHANNEL, "\n").await;
4✔
721
                }
×
722
                Ok(CodeExecutionSuccess::OK)
4✔
723
            }
724
            GCodeValue::M118(_msg) => {
4✔
725
                if let Some(msg) = _msg {
4✔
726
                    let _ = self.write(Contract::HOST_CHANNEL, "echo: ").await;
4✔
727
                    let _ = self.write(Contract::HOST_CHANNEL, msg.as_str()).await;
4✔
728
                    let _ = self.write(Contract::HOST_CHANNEL, "\n").await;
4✔
729
                }
×
730
                Ok(CodeExecutionSuccess::OK)
4✔
731
            }
732
            #[cfg(feature = "with-motion")]
733
            GCodeValue::M119 => {
734
                let mg = self.motion_planner.motion_driver().lock().await;
4✔
735
                let x_endstop = if mg.step_actuator.end_stop_triggered(hwa::CoordSel::X) {
4✔
736
                    1
4✔
737
                } else {
738
                    0
×
739
                };
740
                let y_endstop = if mg.step_actuator.end_stop_triggered(hwa::CoordSel::Y) {
4✔
741
                    1
4✔
742
                } else {
743
                    0
×
744
                };
745
                let z_endstop = if mg.step_actuator.end_stop_triggered(hwa::CoordSel::Z) {
4✔
746
                    1
4✔
747
                } else {
748
                    0
×
749
                };
750
                drop(mg);
4✔
751
                let z = alloc::format!("M119 X {} Y {} Z {}\n", x_endstop, y_endstop, z_endstop);
4✔
752
                let _ = self.write(channel, z.as_str()).await;
4✔
753
                Ok(CodeExecutionSuccess::OK)
4✔
754
            }
755
            // Set hot-bed temperature
756
            // An immediate command
757
            #[cfg(feature = "with-hot-bed")]
758
            GCodeValue::M140(s) => {
6✔
759
                #[cfg(feature = "with-ps-on")]
6✔
760
                if !self
6✔
761
                    .event_bus
6✔
762
                    .get_status()
6✔
763
                    .await
6✔
764
                    .contains(EventFlags::ATX_ON)
6✔
765
                {
766
                    return Err(CodeExecutionFailure::PowerRequired);
2✔
767
                }
4✔
768
                let val = s.s.and_then(|v| v.to_i32()).unwrap_or(0);
4✔
769
                let mut h = self.hot_bed.lock().await;
4✔
770
                h.set_target_temp(
4✔
771
                    CommChannel::Internal,
4✔
772
                    DeferAction::HotBedTemperature,
4✔
773
                    val as f32,
4✔
774
                    gc.order_num,
4✔
775
                )
4✔
776
                .await;
4✔
777
                Ok(CodeExecutionSuccess::OK)
4✔
778
            }
779
            // Wait for hot-bed temperature
780
            // A normally deferred command
781
            #[cfg(feature = "with-hot-bed")]
782
            GCodeValue::M190 => {
783
                #[cfg(feature = "with-ps-on")]
784
                if !self
2✔
785
                    .event_bus
2✔
786
                    .get_status()
2✔
787
                    .await
2✔
788
                    .contains(EventFlags::ATX_ON)
2✔
789
                {
790
                    return Err(CodeExecutionFailure::PowerRequired);
×
791
                }
2✔
792
                let deferred = {
2✔
793
                    let mut he = self.hot_bed.lock().await;
2✔
794
                    he.ping_subscribe(channel, DeferAction::HotBedTemperature, gc.order_num)
2✔
795
                        .await
2✔
796
                };
797
                if deferred {
2✔
798
                    Ok(CodeExecutionSuccess::DEFERRED(EventStatus::containing(
2✔
799
                        EventFlags::HOT_BED_TEMP_OK,
2✔
800
                    )))
2✔
801
                } else {
802
                    Ok(CodeExecutionSuccess::OK)
×
803
                }
804
            }
805
            #[cfg(feature = "with-motion")]
806
            GCodeValue::M201 => Ok(CodeExecutionSuccess::OK),
3✔
807
            #[cfg(feature = "with-motion")]
808
            GCodeValue::M203 => Ok(CodeExecutionSuccess::OK),
3✔
809
            GCodeValue::M204 => Ok(CodeExecutionSuccess::OK),
9,309✔
810
            GCodeValue::M205 => Ok(CodeExecutionSuccess::OK),
6✔
811
            #[cfg(feature = "with-motion")]
812
            GCodeValue::M206 => Ok(CodeExecutionSuccess::OK),
10✔
813
            GCodeValue::M220(_) => Ok(CodeExecutionSuccess::OK),
×
814
            GCodeValue::M221(_) => Ok(CodeExecutionSuccess::OK),
6✔
815
            #[cfg(feature = "with-trinamic")]
816
            GCodeValue::M502 => {
817
                /*
818
                if !self.event_bus.get_status().await.contains(EventFlags::ATX_ON) {
819
                    return Err(CodeExecutionFailure::PowerRequired)
820
                }
821
                 */
822
                let success = {
4✔
823
                    self.motion_planner
4✔
824
                        .motion_driver()
4✔
825
                        .lock()
4✔
826
                        .await
4✔
827
                        .trinamic_controller
828
                        .init()
4✔
829
                        .await
4✔
830
                        .is_ok()
4✔
831
                };
4✔
832
                if success {
4✔
833
                    let _ = self.write(channel, "ok; M502\n").await;
3✔
834
                } else {
835
                    let _ = self.write(channel, "error; M502 (internal error)\n").await;
1✔
836
                }
837
                Ok(CodeExecutionSuccess::OK)
4✔
838
            }
839
            GCodeValue::M503(_param) => {
8✔
840
                let detailed = !_param.s.unwrap_or(hwa::math::ONE).is_zero();
8✔
841
                self.write(channel, "echo:  G21").await;
8✔
842
                if detailed {
8✔
843
                    self.write(channel, "; Units in mm (mm)").await;
4✔
844
                    self.write(channel, "\necho:; Filament settings: Disabled")
4✔
845
                        .await;
4✔
846
                }
4✔
847
                self.write(channel, "\necho:  M200 T0 D1.75").await;
8✔
848
                self.write(channel, "\necho:  M200 S0").await;
8✔
849
                if detailed {
8✔
850
                    self.write(channel, "\necho:; Steps per unit:").await;
4✔
851
                }
4✔
852
                self.write(channel, "\necho:  M92 X80.00 Y80.00 Z400.00 E133.00")
8✔
853
                    .await;
8✔
854
                if detailed {
8✔
855
                    self.write(channel, "\necho:; Maximum feedrates (units/s):")
4✔
856
                        .await;
4✔
857
                }
4✔
858
                self.write(channel, "\necho:  M203 X300.00 Y300.00 Z5.00 E25.00")
8✔
859
                    .await;
8✔
860
                if detailed {
8✔
861
                    self.write(channel, "\necho:; Maximum Acceleration (units/s2)")
4✔
862
                        .await;
4✔
863
                }
4✔
864
                self.write(channel, "\necho:  M201 X3000.00 Y3000.00 Z100.00 E10000.00")
8✔
865
                    .await;
8✔
866
                if detailed {
8✔
867
                    self.write(channel, "\necho:; Acceleration (units/s2): P<print_accel> R<retract_accel> T<travel_accel>").await;
4✔
868
                }
4✔
869
                self.write(channel, "\necho:  M204 P3000.00 R3000.00 T3000.00")
8✔
870
                    .await;
8✔
871
                if detailed {
8✔
872
                    self.write(channel, "\necho:; Advanced: B<min_segment_time_us> S<min_feedrate> T<min_travel_feedrate> J<junc_dev>").await;
4✔
873
                }
4✔
874
                self.write(channel, "\necho:  M205 B20000.00 S0.00 T0.00 J0.01")
8✔
875
                    .await;
8✔
876
                if detailed {
8✔
877
                    self.write(channel, "\necho:; Home offset:").await;
4✔
878
                }
4✔
879
                self.write(channel, "\necho:  M206 X0.00 Y0.00 Z0.00").await;
8✔
880
                if detailed {
8✔
881
                    self.write(channel, "\necho:; Hotend offsets:").await;
4✔
882
                }
4✔
883
                self.write(channel, "\necho:  M218 T1 X0.00 Y0.00 Z0.000")
8✔
884
                    .await;
8✔
885
                if detailed {
8✔
886
                    self.write(channel, "\necho:; PID settings:").await;
4✔
887
                }
4✔
888
                self.write(channel, "\necho:  M301 P5.0 I0.01 D1.5").await;
8✔
889
                self.write(channel, "\necho:  M301 P5.0 I0.01 D1.5").await;
8✔
890
                if detailed {
8✔
891
                    self.write(channel, "\necho:; Stepper driver current: ")
4✔
892
                        .await;
4✔
893
                }
4✔
894
                self.write(channel, "\necho:  M906 X800 Y800 Z800").await;
8✔
895
                self.write(channel, "\necho:  M906 T0 E800").await;
8✔
896
                self.write(channel, "\necho:  M906 T1 E800").await;
8✔
897
                if detailed {
8✔
898
                    self.write(channel, "\necho:; Driver stepping mode:").await;
4✔
899
                }
4✔
900
                self.write(channel, "\necho:  M569 S1 X Y Z").await;
8✔
901
                self.write(channel, "\necho:  M569 M569 S1 T0 E").await;
8✔
902
                Ok(CodeExecutionSuccess::OK)
8✔
903
            }
904
            #[cfg(feature = "with-motion")]
905
            GCodeValue::M862_1 => Ok(CodeExecutionSuccess::OK),
3✔
906
            #[cfg(feature = "with-motion")]
907
            GCodeValue::M862_3 => Ok(CodeExecutionSuccess::OK),
3✔
908
            #[cfg(feature = "with-motion")]
909
            GCodeValue::M900 => Ok(CodeExecutionSuccess::OK),
6✔
910
            #[cfg(feature = "with-motion")]
911
            GCodeValue::M907 => Ok(CodeExecutionSuccess::OK),
6✔
912
            _ => Err(CodeExecutionFailure::NotYetImplemented),
×
913
        };
914
        result
452,083✔
915
    }
452,095✔
916
}
917

918
impl Drop for GCodeProcessor {
919
    fn drop(&mut self) {}
8✔
920
}
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