• 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

48.26
/printhor/src/bin/tasks/task_control.rs
1
//! Task control
2
use crate::{hwa, processing};
3

4
#[cfg(feature = "with-sd-card")]
5
use crate::hwa::controllers::sd_card_controller::SDEntryType;
6
use async_gcode::AsyncParserState;
7
use embassy_time::{Duration, TimeoutError};
8

9
#[allow(unused)]
10
use hwa::{EventFlags, EventStatus};
11

12
cfg_if::cfg_if! {
13
    if #[cfg(feature = "with-print-job")] {
14
        cfg_if::cfg_if! {
15
            if #[cfg(not(feature = "with-sd-card"))] {
16
                compiler_error("Feature with-print-job requires with-sd-card")
17
            }
18
        }
19
    }
20
}
21

22
pub async fn do_with_timeout<F: core::future::Future>(
12✔
23
    _timeout: Duration,
12✔
24
    fut: F,
12✔
25
) -> Result<F::Output, TimeoutError> {
12✔
26
    //Ok(fut.await)
12✔
27
    embassy_time::with_timeout(_timeout, fut).await
12✔
28
}
8✔
29

30
#[embassy_executor::task(pool_size = 1)]
4✔
31
pub async fn task_control(
4✔
32
    mut processor: processing::GCodeProcessor,
4✔
33
    mut gcode_input_stream: processing::GCodeMultiplexedInputStream,
4✔
34
    #[cfg(feature = "with-print-job")] mut printer_controller: hwa::controllers::PrinterController,
4✔
35
    #[cfg(feature = "with-sd-card")] mut card_controller: hwa::types::SDCardController,
4✔
36
) {
4✔
37
    let ev2 = processor.event_bus.clone();
4✔
38
    let mut subscriber = ev2.subscriber().await;
4✔
39

40
    // Initialize an event bus subscriber
41

42
    hwa::info!("[task_control] Waiting for SYS_READY");
4✔
43
    // Pauses this task until system is ready
44
    match subscriber
4✔
45
        .ft_wait_for(hwa::EventStatus::containing(hwa::EventFlags::SYS_READY))
4✔
46
        .await
4✔
47
    {
48
        Ok(_) => hwa::info!("[task_control] Got SYS_READY. Continuing."),
4✔
49
        Err(_) => crate::initialization_error(),
×
50
    }
51

52
    // The task loop
53
    loop {
54
        match do_with_timeout(Duration::from_secs(30), gcode_input_stream.next_gcode()).await {
12✔
55
            // Timeout
56
            Err(_) => {
57
                cfg_if::cfg_if! {
58
                    if #[cfg(feature = "with-serial-usb")] {
59
                        manage_timeout(&mut processor, &mut subscriber, &mut gcode_input_stream,
8✔
60
                            hwa::CommChannel::SerialUsb).await;
8✔
61
                    }
62
                }
63
                cfg_if::cfg_if! {
64
                    if #[cfg(feature = "with-serial-port-1")] {
65
                        manage_timeout(&mut processor, &mut subscriber, &mut gcode_input_stream,
8✔
66
                            hwa::CommChannel::SerialPort1).await;
8✔
67
                    }
68
                }
69
                cfg_if::cfg_if! {
70
                    if #[cfg(feature = "with-serial-port-2")] {
71
                        manage_timeout(&mut processor, &mut subscriber, &mut gcode_input_stream,
8✔
72
                            hwa::CommChannel::SerialPort2).await;
8✔
73
                    }
74
                }
75
                #[cfg(test)]
76
                if crate::tasks::task_integration::INTEGRATION_STATUS.signaled() {
8✔
77
                    hwa::info!("[task_control] Ending gracefully");
×
78
                    return ();
×
79
                }
8✔
80
            }
NEW
81
            Ok((Err(processing::GCodeLineParserError::ParseError(_x)), channel)) => {
×
82
                hwa::error!("[{:?}] GCode N/A ParserError", channel);
×
83
                processor.write(channel, "error; (ParserError)\n").await;
×
84
            }
85
            Ok((
NEW
86
                Err(processing::GCodeLineParserError::GCodeNotImplemented(_ln, _gcode_name)),
×
87
                channel,
×
88
            )) => {
×
89
                hwa::error!("GCode {} (NotImplemented)", _gcode_name.as_str());
×
90
                let s = alloc::format!("error; {} (NotImplemented)\n", _gcode_name);
×
91
                processor.write(channel, &s).await;
×
92
            }
NEW
93
            Ok((Err(processing::GCodeLineParserError::FatalError), channel)) => {
×
94
                hwa::error!("[{:?}] GCode N/A (Internal error)", channel);
×
95
                let s = "error; Internal error\n";
×
96
                processor.write(channel, s).await;
×
97
            }
NEW
98
            Ok((Err(processing::GCodeLineParserError::EOF), _channel)) => {
×
99
                hwa::warn!("{:?} Got EOF", _channel);
×
100
                //embassy_time::Timer::after_secs(1).await; // Avoid respawn too fast
101
            }
102
            Ok((Ok(gc), channel)) => {
×
103
                hwa::debug!("{:?} got {:?}", channel, gc);
×
104
                let response = execute(
×
105
                    &mut processor,
×
106
                    channel,
×
107
                    &gc,
×
108
                    #[cfg(feature = "with-sd-card")]
×
109
                    &mut card_controller,
×
110
                    #[cfg(feature = "with-print-job")]
×
111
                    &mut printer_controller,
×
112
                )
×
113
                .await;
×
114
                report(response, &gc, channel, &mut processor).await;
×
115
            }
116
        }
117
    }
118
}
×
119

120
#[allow(unused)]
121
async fn manage_timeout<M>(
24✔
122
    processor: &mut processing::GCodeProcessor,
24✔
123
    subscriber: &mut hwa::EventBusSubscriber<'static, M>,
24✔
124
    gcode_input_stream: &mut processing::GCodeMultiplexedInputStream,
24✔
125
    comm_channel: hwa::CommChannel,
24✔
126
) where
24✔
127
    M: hwa::AsyncRawMutex,
24✔
128
{
24✔
129
    let current_parser_state = gcode_input_stream.get_state(comm_channel);
24✔
130

24✔
131
    #[cfg(feature = "trace-commands")]
24✔
132
    hwa::info!(
24✔
133
        "[trace-commands] [task_control] Timeout at {:?}: [State: {:?} Line: {} TaggedLine: {:?}]",
24✔
134
        comm_channel,
24✔
135
        alloc::format!("{:?}", current_parser_state).as_str(),
24✔
136
        gcode_input_stream.get_line(comm_channel),
24✔
137
        gcode_input_stream.get_gcode_line(comm_channel),
24✔
138
    );
24✔
139

24✔
140
    match current_parser_state {
24✔
141
        // Parser is currently ready to accept a gcode. Nothing to do
142
        AsyncParserState::Start(_) => {
143
            gcode_input_stream.reset(comm_channel).await;
24✔
144
        }
145
        // Parser is at any other intermediate state
146
        _ => {
147
            // Notify there is a timeout on [CommChannel::Internal] channel
148
            #[cfg(feature = "trace-commands")]
149
            processor
150
                .write(hwa::CommChannel::Internal, "echo: Control timeout.\n")
151
                .await;
152
            // Dumps the relevant status for a more easy troubleshooting
153
            let z3 = alloc::format!("echo: EventBus status: {:?}", subscriber.get_status().await);
×
154
            processor
×
155
                .write(hwa::CommChannel::Internal, z3.as_str())
×
156
                .await;
×
157

158
            let z = alloc::format!(
×
159
                "echo: Will reset channel: {:?} state was: {:?}\n",
×
160
                comm_channel,
×
161
                current_parser_state
×
162
            );
×
163
            processor
×
164
                .write(hwa::CommChannel::Internal, z.as_str())
×
165
                .await;
×
166
            gcode_input_stream.reset(comm_channel);
×
167
            // Submit an err to give up the intermediate state.
×
168
            processor.write(comm_channel, "err\n").await;
×
169
        }
170
    }
171
    #[cfg(feature = "trace-commands")]
172
    processor
173
        .write(comm_channel, "echo: Control timeout.\n")
174
        .await;
175
}
24✔
176

177
/*
178
Process and consume gcode commands
179
A set of commands will be directly executed
180
Remaining will be delegated to the processor.
181
 */
182
pub async fn execute(
38✔
183
    processor: &mut processing::GCodeProcessor,
38✔
184
    channel: hwa::CommChannel,
38✔
185
    gc: &processing::GCodeCmd,
38✔
186
    #[cfg(feature = "with-sd-card")] _card_controller: &mut hwa::types::SDCardController,
38✔
187
    #[cfg(feature = "with-print-job")]
38✔
188
    _printer_controller: &mut hwa::controllers::PrinterController,
38✔
189
) -> processing::CodeExecutionResult {
38✔
190
    match &gc.value {
38✔
191
        processing::GCodeValue::Nop => {
192
            hwa::error!("Go Nop");
×
193
            // Should not happen
NEW
194
            Err(processing::CodeExecutionFailure::ERR)
×
195
        }
196
        #[cfg(feature = "grbl-compat")]
197
        processing::GCodeValue::Status => {
198
            // TODO provide GRBL compatibility status
199
            processor
200
                .write(
201
                    channel,
202
                    "<Idle|MPos:0.000,0.000,0.000|Pn:XP|FS:0,0|WCO:0.000,0.000,0.000>\n",
203
                )
204
                .await;
205
            Ok(processing::CodeExecutionSuccess::CONSUMED)
206
        }
207
        #[cfg(all(feature = "with-sd-card", feature = "with-print-job"))]
208
        processing::GCodeValue::M2 => {
209
            match _printer_controller
×
210
                .set(hwa::controllers::PrinterControllerEvent::Abort(channel))
×
211
                .await
×
212
            {
213
                Ok(_f) => {
×
214
                    processor
×
215
                        .write(channel, "echo: Print job terminated\n")
×
216
                        .await;
×
NEW
217
                    Ok(processing::CodeExecutionSuccess::OK)
×
218
                }
219
                Err(_e) => {
×
220
                    let s = alloc::format!("echo: M2: Unable to terminate: {:?}\n", _e);
×
221
                    processor.write(channel, s.as_str()).await;
×
NEW
222
                    Err(processing::CodeExecutionFailure::ERR)
×
223
                }
224
            }
225
        }
226
        #[cfg(feature = "with-sd-card")]
227
        processing::GCodeValue::M20(_path) => {
12✔
228
            {
12✔
229
                let path = _path.clone().unwrap_or(alloc::string::String::from("/"));
12✔
230
                let mut sent_first = false;
12✔
231
                match _card_controller.dir_iterator(path.as_str()).await {
12✔
232
                    Ok(mut it) => {
8✔
233
                        loop {
234
                            match it.next().await {
36✔
235
                                Ok(Some(entry)) => match entry.entry_type {
28✔
236
                                    SDEntryType::FILE => {
237
                                        if !sent_first {
16✔
238
                                            processor.write(channel, "Begin file list\n").await;
8✔
239
                                            sent_first = true;
8✔
240
                                        }
8✔
241
                                        processor.write(channel, entry.name.as_str()).await;
16✔
242
                                        let s = alloc::format!(" {}\n", entry.size);
16✔
243
                                        processor.write(channel, s.as_str()).await;
16✔
244
                                    }
245
                                    SDEntryType::DIRECTORY => {}
12✔
246
                                },
247
                                Ok(None) => {
248
                                    // EOF
249
                                    processor.write(channel, "End file list\n").await;
8✔
250
                                    break;
8✔
251
                                }
252
                                Err(_e) => {
×
253
                                    if sent_first {
×
254
                                        processor.write(channel, "End file list\n").await;
×
255
                                    }
×
256
                                    processor.write(channel, "err\n").await;
×
257
                                    hwa::error!("Error listing files");
×
258
                                    break;
×
259
                                }
260
                            }
261
                        }
262
                        it.close().await;
8✔
263
                        processor.write(channel, "ok\n").await;
8✔
264
                        Ok(processing::CodeExecutionSuccess::CONSUMED)
8✔
265
                    }
266
                    Err(_e) => {
4✔
267
                        let s = alloc::format!("echo: M20: Unable to list: {:?}\n", _e);
4✔
268
                        processor.write(channel, s.as_str()).await;
4✔
269
                        Err(processing::CodeExecutionFailure::ERR)
4✔
270
                    }
271
                }
272
            }
273
        }
274
        #[cfg(feature = "with-sd-card")]
NEW
275
        processing::GCodeValue::M21 => Ok(processing::CodeExecutionSuccess::OK),
×
276
        #[cfg(feature = "with-print-job")]
277
        processing::GCodeValue::M23(file) => {
10✔
278
            match _printer_controller
10✔
279
                .set(hwa::controllers::PrinterControllerEvent::SetFile(
10✔
280
                    channel,
10✔
281
                    match file {
10✔
282
                        None => alloc::string::String::new(),
×
283
                        Some(str) => str.clone(),
10✔
284
                    },
285
                ))
286
                .await
10✔
287
            {
288
                Ok(()) => {
289
                    if let Some(_f) = file {
10✔
290
                        processor.write(channel, "echo Now fresh file: ").await;
10✔
291
                        processor.write(channel, _f.as_str()).await;
10✔
292
                        processor.write(channel, "\n").await;
10✔
293
                    }
×
294
                    processor
10✔
295
                        .event_bus
10✔
296
                        .publish_event(EventStatus::not_containing(EventFlags::JOB_COMPLETED))
10✔
297
                        .await;
10✔
298
                    // TODO
299
                    Ok(processing::CodeExecutionSuccess::CONSUMED)
10✔
300
                }
301
                Err(_e) => {
×
302
                    let s = alloc::format!("echo: M23: Unable to set: {:?}\n", _e);
×
303
                    processor.write(channel, s.as_str()).await;
×
NEW
304
                    Err(processing::CodeExecutionFailure::ERR)
×
305
                }
306
            }
307
        }
308
        #[cfg(feature = "with-print-job")]
309
        processing::GCodeValue::M24 => {
310
            match _printer_controller
10✔
311
                .set(hwa::controllers::PrinterControllerEvent::Resume(channel))
10✔
312
                .await
10✔
313
            {
314
                Ok(_f) => {
10✔
315
                    processor.write(channel, "ok\n").await;
10✔
316
                    processor.write(channel, "echo: Print job resumed\n").await;
10✔
317
                    Ok(processing::CodeExecutionSuccess::CONSUMED)
10✔
318
                }
319
                Err(_e) => {
×
320
                    let s = alloc::format!("echo: M24: Unable to start/resume: {:?}\n", _e);
×
321
                    processor.write(channel, s.as_str()).await;
×
NEW
322
                    Err(processing::CodeExecutionFailure::ERR)
×
323
                }
324
            }
325
        }
326
        #[cfg(feature = "with-print-job")]
327
        processing::GCodeValue::M25 => {
328
            match _printer_controller
×
329
                .set(hwa::controllers::PrinterControllerEvent::Pause(channel))
×
330
                .await
×
331
            {
332
                Ok(_f) => {
×
333
                    processor.write(channel, "ok\n").await;
×
334
                    processor.write(channel, "echo: Print job paused\n").await;
×
NEW
335
                    Ok(processing::CodeExecutionSuccess::CONSUMED)
×
336
                }
337
                Err(_e) => {
×
338
                    let s = alloc::format!("echo: M25: Unable to pause print job: {:?}\n", _e);
×
339
                    processor.write(channel, s.as_str()).await;
×
NEW
340
                    Err(processing::CodeExecutionFailure::ERR)
×
341
                }
342
            }
343
        }
344
        // Otherwise... delegate
345
        _ => processor.execute(channel, &gc, false).await,
6✔
346
    }
347
}
38✔
348

349
async fn report(
×
NEW
350
    result: processing::CodeExecutionResult,
×
NEW
351
    gc: &processing::GCodeCmd,
×
352
    channel: hwa::CommChannel,
×
NEW
353
    processor: &mut processing::GCodeProcessor,
×
354
) {
×
355
    match result {
×
356
        Ok(processing::CodeExecutionSuccess::OK) => {
357
            cfg_if::cfg_if! {
358
                if #[cfg(feature="trace-commands")] {
359
                    let s = alloc::format!("ok; [trace-commands] {}\n", gc);
360
                    processor.write(channel, s.as_str()).await;
361
                }
362
                else {
363
                    processor.write(channel, "ok\n").await;
×
364
                }
365
            }
366
        }
367
        Ok(processing::CodeExecutionSuccess::QUEUED) => {
368
            cfg_if::cfg_if! {
369
                if #[cfg(feature="trace-commands")] {
370
                    let s = alloc::format!("ok; [trace-commands] promised {}\n", gc);
371
                    processor.write(channel, s.as_str()).await;
372
                }
373
                else {
374
                    processor.write(channel, "ok\n").await;
×
375
                }
376
            }
377
        }
NEW
378
        Ok(processing::CodeExecutionSuccess::DEFERRED(_)) => {
×
379
            #[cfg(feature = "trace-commands")]
×
380
            {
×
381
                hwa::debug!("Control not sending (deferred)");
×
382
                let s = alloc::format!("echo; promised {}\n", gc);
×
383
                processor.write(channel, s.as_str()).await;
×
384
            }
×
385
        }
×
NEW
386
        Ok(processing::CodeExecutionSuccess::CONSUMED) => {
×
387
            #[cfg(feature = "trace-commands")]
×
388
            {
×
389
                let s = alloc::format!("echo; already confirmed {}\n", gc);
×
390
                processor.write(channel, s.as_str()).await;
×
391
            }
×
392
        }
×
393
        Err(_e) => {
×
394
            hwa::info!("Control sending ERR");
×
395
            let s = alloc::format!("error; {} ({:?})\n", gc, _e);
×
396
            processor.write(channel, s.as_str()).await;
×
397
        }
398
    }
399
}
×
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