nom_reprap_response/
feedback.rs

1use nom::{
2    IResult,
3    character::streaming::*,
4    bytes::streaming::*,
5};
6use nom::branch::*;
7use nom::combinator::*;
8use nom::sequence::*;
9use nom::multi::*;
10
11use super::{
12    Response,
13    f32_str,
14};
15
16#[derive(Clone, Debug, PartialEq)]
17pub enum Busy {
18    Processing,
19    PausedForUser,
20    Other(String),
21}
22
23#[derive(Clone, Debug, PartialEq)]
24pub enum Feedback {
25    SDCard(SDCard),
26    ActualTemperatures(Vec<(String, f32)>),
27    Positions(Positions),
28    StartSDWrite(StartSDWrite),
29    SDWriteComplete,
30    SDPrintComplete,
31    Busy(Busy),
32    PausedForUser,
33}
34
35#[derive(Clone, Debug, PartialEq)]
36pub struct Positions {
37    pub target_positions: Option<Vec<(String, f32)>>,
38    pub actual_positions: Vec<(String, f32)>,
39}
40
41#[derive(Clone, Debug, PartialEq)]
42pub struct StartSDWrite {
43    pub filename: String,
44}
45
46#[derive(Clone, Debug, PartialEq)]
47pub struct SDCard {
48    pub enabled: bool,
49    pub size: Option<u32>,
50}
51
52pub fn feedback_resp<'r>(input: &'r str) ->  IResult<&'r str, Response> {
53    map(
54        feedback,
55        |feedback| Response::Feedback(feedback),
56    )(input)
57}
58
59pub fn feedback<'r>(input: &'r str) ->  IResult<&'r str, Feedback> {
60    preceded(
61        space0,
62        alt((
63            temperature_feedback,
64            position_feedback,
65        )),
66    )(input)
67}
68
69pub fn key_value<'r>(input: &'r str) -> IResult<&'r str, (String, Option<f32>)> {
70    // T:25.0 /0.0 B:25.0 /0.0 T0:25.0 /0.0 @:0 B@:0
71    // X:0.00 Y:191.00 Z:159.00 E:0.00 Count X: 0 Y:19196 Z:254400
72    // X:${position()} Y:${position()} Z:${position()} E:0.00 Count X: 0.00Y:0.00Z:0.00
73    separated_pair(
74        map(
75            recognize(many1(
76                verify(anychar, |c| c.is_ascii_alphanumeric() || c == &'@'),
77            )),
78            |address: &str| address.to_ascii_lowercase()
79        ),
80        pair(
81            char(':'),
82            space0,
83        ),
84        alt((
85            value(None, char('?')),
86            map(f32_str(), |f| Some(f)),
87        ))
88    )(input)
89}
90
91pub fn temperature_feedback<'r>(input: &'r str) ->  IResult<&'r str, Feedback> {
92    // ok T:25.0 /0.0 B:25.0 /0.0 T0:25.0 /0.0 @:0 B@:0
93    // T:${extruder} /0.0 B:${bed} /0.0 B@:0 @:0
94    map(
95        preceded(
96            peek(tag("T:")),
97            separated_list1(
98                pair(
99                    space1,
100                    opt(tuple((
101                        char('/'),
102                        f32_str(),
103                        space1,
104                    ))),
105                ),
106                key_value,
107            ),
108        ),
109        |temperatures| {
110            let temperatures = temperatures
111                .into_iter()
112                .filter_map(|(address, v)| {
113                    // Skip "@" and "w" values. I have no idea what they are for but Marlin
114                    // sends them.
115                    if address.contains('@') || address == "e" || address == "w" {
116                        return None
117                    };
118
119                    let address = if &address[..] == "t" {
120                        "e0".to_string()
121                    } else if address.starts_with('t') {
122                        address.replace("t", "e")
123                    }else {
124                        address
125                    };
126
127                    v.map(|v| (address, v))
128                })
129                .collect();
130            Feedback::ActualTemperatures(temperatures)
131        }
132    )(input)
133}
134
135pub fn position_feedback<'r>(input: &'r str) ->  IResult<&'r str, Feedback> {
136    //           target positions                actual positions
137    // |-------------------------------|     |---------------------|
138    // 'X:0.00 Y:191.00 Z:159.00 E:0.00 Count X: 0 Y:19196 Z:254400',
139    // `X:${position()} Y:${position()} Z:${position()} E:0.00 Count X: 0.00Y:0.00Z:0.00`,
140    map(
141        preceded(
142            peek(tag("X:")),
143            pair(
144                many1(preceded(
145                    space0,
146                    key_value,
147                )),
148                opt(preceded(
149                    tuple((
150                        space1,
151                        tag_no_case("Count"),
152                        space1,
153                    )),
154                    many0(terminated(
155                        key_value,
156                        space0,
157                    )),
158                )),
159            ),
160        ),
161        |(p1, p2)| {
162            if let Some(p2) = p2 {
163                Feedback::Positions(Positions {
164                    target_positions: Some(normalize_positions(p1)),
165                    actual_positions: normalize_positions(p2),
166                })
167            } else {
168                Feedback::Positions(Positions {
169                    target_positions: None,
170                    actual_positions: normalize_positions(p1),
171                })
172            }
173        }
174    )(input)
175}
176
177fn normalize_positions(positions: Vec<(String, Option<f32>)>) -> Vec<(String, f32)> {
178    positions
179        .into_iter()
180        .filter_map(|(address, v)| {
181            let address = if &address[..] == "e" {
182                "e0".to_string()
183            } else if address.starts_with('t') {
184                address.replace("t", "e")
185            }else {
186                address
187            };
188
189            v.map(|v| (address, v))
190        })
191        .collect()
192}