• 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

67.88
/printhor/src/bin/tasks/task_print_job.rs
1
//! This module contains functionality related to the execution of print jobs in a 3D printer.
2
//! It leverages asynchronous tasks and event-driven programming to manage and control the
3
//! sequence of actions required to process G-code commands from an SD card and relay them to the printer controller.
4
//!
5
//! The task `task_print_job` is defined with the main goal of managing the print job execution. It initializes
6
//! controllers, waits for system readiness, handles SD card operations, and processes G-code lines. Key events such
7
//! as setting a new file for printing, resuming a print job, and handling timeouts are meticulously managed.
8
//!
9
//! The workflow involves:
10
//! 1. **Initialization**: The task sets up an event subscriber and waits for the system to finish booting.
11
//! 2. **Event Handling**: It enters a loop where it waits for the system to be free of alarms (`SYS_ALARM`), then tries
12
//!    to consume any commands from the printer controller.
13
//! 3. **G-code Processing**: When a new file is set for printing, it parses the G-code lines asynchronously.
14
//! 4. **Error Management**: Handles various errors such as file not found on the SD card, or any fatal errors during G-code execution.
15
//! 5. **State Updates**: Publishes events to signal the state of the print job (e.g., `JOB_PAUSED`, `JOB_COMPLETED`).
16
//!
17
//! This approach ensures robust handling of print job execution, making sure that the 3D printer can efficiently
18
//! and reliably process and execute the G-code commands.
19

20
use crate::{hwa, processing};
21

22
use hwa::controllers::PrinterController;
23
use hwa::controllers::PrinterControllerEvent;
24
use hwa::sd_card::SDCardError;
25
use hwa::{CommChannel, EventFlags, EventStatus};
26
use processing::{CodeExecutionFailure, CodeExecutionSuccess, GCodeCmd, GCodeValue};
27

28
use embassy_time::Duration;
29
use embassy_time::Instant;
30

31
/// The `task_print_job` function manages the execution of a 3D printer's print job.
32
///
33
/// # Arguments
34
/// * `processor` - The GCode processor responsible for handling the GCode commands.
35
/// * `printer_controller` - The controller for the printer to interface with the hardware.
36
/// * `card_controller` - The controller for the SD card where GCode files are stored.
37
///
38
/// # Description
39
/// This async function achieves the following tasks:
40
/// 1. **Initialization**: Sets up an event subscriber and waits for the system to finish booting.
41
/// 2. **Event Handling**: Enters a loop where it waits for the system to be free of alarms (`SYS_ALARM`) and tries to consume any commands from the printer controller.
42
/// 3. **G-code Processing**: When a new file is set for printing, it parses the G-code lines asynchronously.
43
/// 4. **Error Management**: Handles various errors such as file not found on the SD card, or any fatal errors during G-code execution.
44
/// 5. **State Updates**: Publishes events to signal the state of the print job (e.g., `JOB_PAUSED`, `JOB_COMPLETED`).
45
///
46
/// The robust handling of print job execution ensures that the 3D printer can efficiently and reliably process and execute the G-code commands.
47
/// FIXME: JOB_PAUSE status flag remains!!
48
#[allow(unused_mut)]
49
#[embassy_executor::task(pool_size = 1)]
4✔
50
pub async fn task_print_job(
4✔
51
    mut processor: processing::GCodeProcessor,
4✔
52
    mut printer_controller: PrinterController,
4✔
53
    mut card_controller: hwa::types::SDCardController,
4✔
54
) {
4✔
55
    let event_bus = processor.event_bus.clone();
4✔
56
    let mut subscriber = event_bus.subscriber().await;
4✔
57

58
    hwa::info!("[task_print_job] Waiting for SYS_BOOTING");
4✔
59
    // Pauses this task until system is ready
60
    match subscriber
4✔
61
        .ft_wait_for(EventStatus::not_containing(EventFlags::SYS_BOOTING))
4✔
62
        .await
4✔
63
    {
64
        Ok(_) => hwa::info!("[task_print_job] Got SYS_BOOTING. Continuing."),
4✔
65
        Err(_) => crate::initialization_error(),
×
66
    }
67

68
    let mut print_job_parser = None;
4✔
69
    let mut job_time = Duration::from_ticks(0);
4✔
70

71
    loop {
72
        let _ = subscriber
39✔
73
            .ft_wait_for(EventStatus::not_containing(EventFlags::SYS_ALARM))
39✔
74
            .await;
39✔
75

76
        match embassy_time::with_timeout(Duration::from_secs(10), printer_controller.consume())
39✔
77
            .await
39✔
78
        {
79
            Err(_) => {
80
                #[cfg(feature = "trace-commands")]
81
                hwa::info!("[trace-commands] [task_print_job] Timeout");
82
                #[cfg(test)]
83
                if crate::tasks::task_integration::INTEGRATION_STATUS.signaled() {
8✔
84
                    hwa::info!("[task_print_job] Ending gracefully");
×
85
                    return ();
×
86
                }
8✔
87
            }
88
            Ok(PrinterControllerEvent::SetFile(channel, file_path)) => {
10✔
89
                #[cfg(feature = "trace-commands")]
10✔
90
                hwa::info!(
10✔
91
                    "[trace-commands] [task_print_job] SetFile: {}",
10✔
92
                    file_path.as_str()
10✔
93
                );
10✔
94
                job_time = Duration::from_ticks(0);
10✔
95
                print_job_parser = match card_controller.new_stream(file_path.as_str()).await {
10✔
96
                    Ok(stream) => {
10✔
97
                        let parser = processing::GCodeLineParser::new(stream);
10✔
98
                        processor.write(channel, "File opened: ").await;
10✔
99
                        processor.write(channel, file_path.as_str()).await;
10✔
100
                        processor.write(channel, "\nFile selected\n").await;
10✔
101
                        Some(parser)
10✔
102
                    }
103
                    Err(_e) => {
×
104
                        let error_msg = match _e {
×
105
                            SDCardError::NoSuchVolume => "SetFile: Card not ready",
×
106
                            SDCardError::NotFound => "SetFile: File not found",
×
107
                            _ => "SetFile: Internal error",
×
108
                        };
109
                        hwa::error!("{}", error_msg);
×
110
                        processor
×
111
                            .write(channel, alloc::format!("error; {}\n", error_msg).as_str())
×
112
                            .await;
×
113
                        continue;
×
114
                    }
115
                };
116
                processor
10✔
117
                    .event_bus
10✔
118
                    .publish_event(
10✔
119
                        EventStatus::containing(EventFlags::JOB_PAUSED)
10✔
120
                            .and_not_containing(EventFlags::JOB_COMPLETED),
10✔
121
                    )
10✔
122
                    .await;
10✔
123
            }
124
            Ok(PrinterControllerEvent::Resume(channel)) => {
10✔
125
                let mut interrupted = false;
10✔
126
                let mut fatal_error = false;
10✔
127
                let job_t0 = Instant::now();
10✔
128
                loop {
129
                    match gcode_pull(&mut print_job_parser).await {
452,359✔
130
                        Ok(gcode) => {
452,020✔
131
                            match &gcode.value {
452,020✔
132
                                GCodeValue::M2 => {
133
                                    match printer_controller
7✔
134
                                        .set(PrinterControllerEvent::Abort(channel))
7✔
135
                                        .await
7✔
136
                                    {
137
                                        Ok(_f) => {
7✔
138
                                            processor
7✔
139
                                                .write(channel, "echo: Print job terminated\n")
7✔
140
                                                .await;
7✔
141
                                            break;
7✔
142
                                        }
143
                                        Err(_e) => {
×
144
                                            let s = alloc::format!(
×
145
                                                "echo: M2: Unable to terminate: {:?}\n",
×
146
                                                _e
×
147
                                            );
×
148
                                            processor.write(channel, s.as_str()).await;
×
149
                                            fatal_error = true;
×
150
                                            hwa::error!("Response: ERR {}", gcode);
×
151
                                            break;
×
152
                                        }
153
                                    }
154
                                }
155
                                _ => {
156
                                    #[cfg(feature = "trace-commands")]
157
                                    hwa::info!("[trace-commands] Executing {}", gcode);
158

159
                                    cfg_if::cfg_if! {
160
                                        if #[cfg(feature = "trace-commands")] {
161
                                            const CHANNEL: CommChannel = CommChannel::Internal;
162
                                        }
163
                                        else {
164
                                            const CHANNEL: CommChannel = CommChannel::Sink;
165
                                        }
166
                                    }
167

168
                                    match processor.execute(CHANNEL, &gcode, true).await {
452,013✔
169
                                        Ok(CodeExecutionSuccess::OK) => {
170
                                            hwa::debug!(
37,165✔
171
                                                "[task-print-job] Response: OK {} (I)",
×
172
                                                gcode
173
                                            );
174
                                        }
175
                                        Ok(CodeExecutionSuccess::CONSUMED) => {
176
                                            hwa::debug!(
10✔
177
                                                "[task-print-job] Response: OK {} (C)",
×
178
                                                gcode
179
                                            );
180
                                        }
181
                                        Ok(CodeExecutionSuccess::QUEUED) => {
182
                                            hwa::debug!(
390,864✔
183
                                                "[task-print-job] Response: OK {} (Q)",
×
184
                                                gcode
185
                                            );
186
                                        }
187
                                        Ok(CodeExecutionSuccess::DEFERRED(_status)) => {
23,974✔
188
                                            hwa::debug!("[task-print-job] Deferred {} (D)", gcode);
23,974✔
189
                                            if subscriber.ft_wait_for(_status).await.is_err() {
23,974✔
190
                                                // Must pause. Recoverable when SYS_ALARM go down
191
                                                break;
×
192
                                            } else {
193
                                                hwa::debug!(
23,974✔
194
                                                    "[task-print-job] Response: OK {} (D)",
×
195
                                                    gcode
196
                                                );
197
                                            }
198
                                        }
199
                                        Err(CodeExecutionFailure::BUSY) => {
200
                                            fatal_error = true;
×
201
                                            hwa::error!(
×
202
                                                "[task-print-job] Response: BUSY {}",
×
203
                                                gcode
204
                                            );
205
                                            break;
×
206
                                        }
207
                                        Err(
208
                                            CodeExecutionFailure::ERR
209
                                            | CodeExecutionFailure::NumericalError,
210
                                        ) => {
211
                                            fatal_error = true;
×
212
                                            hwa::error!("[task-print-job] Response: ERR {}", gcode);
×
213
                                            break;
×
214
                                        }
215
                                        Err(CodeExecutionFailure::NotYetImplemented) => {
216
                                            hwa::warn!(
×
217
                                                "[task-print-job] Ignoring GCode not implemented {}",
×
218
                                                gcode
219
                                            );
220
                                        }
221
                                        Err(CodeExecutionFailure::HomingRequired) => {
222
                                            fatal_error = true;
×
223
                                            hwa::error!(
×
224
                                                "[task-print-job] Unexpected HomingRequired before {}",
×
225
                                                gcode
226
                                            );
227
                                            break;
×
228
                                        }
229
                                        Err(CodeExecutionFailure::PowerRequired) => {
230
                                            fatal_error = true;
×
231
                                            hwa::error!(
×
232
                                                "[task-print-job] Unexpected PowerRequired before {}",
×
233
                                                gcode
234
                                            );
235
                                            break;
×
236
                                        }
237
                                    }
238
                                }
239
                            }
240
                        }
241
                        Err(error) => {
339✔
242
                            let current_line = print_job_parser
339✔
243
                                .as_ref()
339✔
244
                                .map_or(0, |parser| parser.get_line());
339✔
245
                            match error {
339✔
246
                                processing::GCodeLineParserError::EOF => {
247
                                    // EOF
248
                                    break;
3✔
249
                                }
250
                                processing::GCodeLineParserError::FatalError => {
251
                                    // Fatal
252
                                    fatal_error = true;
×
253
                                    hwa::error!(
×
254
                                        "[task-print-job] Fatal error at line {}",
×
255
                                        current_line
256
                                    );
257
                                    break;
×
258
                                }
259
                                processing::GCodeLineParserError::GCodeNotImplemented(
260
                                    _ln,
333✔
261
                                    _gcode,
333✔
262
                                ) => {
333✔
263
                                    processor
333✔
264
                                        .write(
333✔
265
                                            channel,
333✔
266
                                            alloc::format!(
333✔
267
                                                "[task-print-job] Ignoring gcode not supported: {} at {}\n",
333✔
268
                                                _gcode,
333✔
269
                                                current_line
333✔
270
                                            )
333✔
271
                                            .as_str(),
333✔
272
                                        )
333✔
273
                                        .await;
333✔
274
                                    hwa::warn!(
333✔
275
                                        "[task-print-job] Ignoring GCode not supported {} at line {}",
333✔
276
                                        _gcode.as_str(),
333✔
277
                                        current_line
278
                                    );
279
                                }
280
                                processing::GCodeLineParserError::ParseError(_ln) => {
3✔
281
                                    hwa::warn!(
3✔
282
                                        "[task-print-job] Parse error at line {}",
3✔
283
                                        current_line
284
                                    );
285
                                }
286
                            }
287
                        }
288
                    }
289
                    // Peek new event to see if job is cancelled
290
                    if printer_controller.signaled().await {
452,349✔
291
                        interrupted = true;
×
292
                        break;
×
293
                    }
452,349✔
294
                }
295
                if subscriber
10✔
296
                    .ft_wait_until(EventFlags::MOV_QUEUE_EMPTY)
10✔
297
                    .await
10✔
298
                    .is_err()
10✔
299
                {
300
                    hwa::warn!("[task-print-job] SYS_ALARM raised");
×
301
                }
10✔
302
                job_time += job_t0.elapsed();
10✔
303
                if fatal_error {
10✔
304
                    printer_controller
×
305
                        .set(PrinterControllerEvent::Abort(channel))
×
306
                        .await
×
307
                        .unwrap();
×
308
                } else if !interrupted {
10✔
309
                    processor
10✔
310
                        .event_bus
10✔
311
                        .publish_event(
10✔
312
                            EventStatus::containing(EventFlags::JOB_COMPLETED)
10✔
313
                                .and_not_containing(EventFlags::JOB_PAUSED),
10✔
314
                        )
10✔
315
                        .await;
10✔
316
                    // Job completed or cancelled
317
                    match print_job_parser.take() {
10✔
318
                        None => {}
×
319
                        Some(mut p) => p.close().await,
10✔
320
                    }
321
                    hwa::info!(
10✔
322
                        "[task-print-job] Job completed in {:03} seconds",
10✔
323
                        job_time.as_millis() as f64 / 1000.0
10✔
324
                    );
325
                }
×
326
            }
327
            Ok(PrinterControllerEvent::Abort(channel)) => {
7✔
328
                processor.write(channel, "echo: Job aborted\n").await;
7✔
329
                match print_job_parser.take() {
7✔
330
                    None => {}
7✔
331
                    Some(mut p) => p.close().await,
×
332
                }
333
                hwa::debug!("[task-print-job] File done");
7✔
334
            }
335
            Ok(_) => {
336
                hwa::warn!("[task-print-job] Unexpected event");
×
337
                continue;
×
338
            }
339
        }
340
    }
341
}
×
342

343
async fn gcode_pull(
452,359✔
344
    print_job_parser: &mut Option<hwa::types::SDCardLineParser>,
452,359✔
345
) -> Result<GCodeCmd, processing::GCodeLineParserError> {
452,359✔
346
    match print_job_parser.as_mut() {
452,359✔
347
        Some(parser) => parser
452,359✔
348
            .next_gcode(CommChannel::Internal)
452,359✔
349
            .await
452,359✔
350
            .map(|mut gc| {
452,359✔
351
                gc.order_num = parser.get_line();
452,020✔
352
                gc
452,020✔
353
            }),
452,359✔
NEW
354
        None => Err(processing::GCodeLineParserError::FatalError),
×
355
    }
356
}
452,359✔
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

© 2025 Coveralls, Inc