1use crate::*;
26
27use std::{
28 fmt,
29 io::{BufRead, Error as ioError},
30};
31
32use super::{types::*, utils::*};
33
34pub fn load_gcode_points<IP, P, R>(read: &mut R, ip: &mut IP) -> GcodeResult<()>
38where
39 IP: IsPushable<P>,
40 P: IsBuildable3D,
41 R: BufRead,
42{
43 let mut line_buffer = Vec::new();
44
45 let mut i_line = 0;
46
47 let mut start_pushed = false;
48 let mut ra = RelativeAbsolute::Absolute;
49 let mut x = 0.0;
50 let mut y = 0.0;
51 let mut z = 0.0;
52
53 while let Ok(line) = fetch_line(read, &mut line_buffer) {
54 i_line += 1;
55
56 if line.len() >= 4 && line[0] == b'G' {
57 if line[2] == b' ' && (line[1] == b'1' || line[1] == b'2' || line[1] == b'3') {
58 let mut any_changed = false;
60 let [opt_x, opt_y, opt_z] = command(&line[3..])
61 .ok_or(GcodeError::Command)
62 .line(i_line, line)?;
63
64 if let Some(new_x) = opt_x {
65 any_changed = true;
66 match ra {
67 RelativeAbsolute::Absolute => x = new_x,
68 RelativeAbsolute::Relative => x += new_x,
69 }
70 }
71
72 if let Some(new_y) = opt_y {
73 any_changed = true;
74 match ra {
75 RelativeAbsolute::Absolute => y = new_y,
76 RelativeAbsolute::Relative => y += new_y,
77 }
78 }
79
80 if let Some(new_z) = opt_z {
81 any_changed = true;
82 match ra {
83 RelativeAbsolute::Absolute => z = new_z,
84 RelativeAbsolute::Relative => z += new_z,
85 }
86 }
87
88 if any_changed {
89 if !start_pushed {
90 ip.push(P::new(0.0, 0.0, 0.0));
91 start_pushed = true
92 }
93 ip.push(P::new(x, y, z));
94 }
95 } else if line[1] == b'9' && line[3] == b' ' {
96 if line[2] == b'0' {
98 ra = RelativeAbsolute::Absolute;
100 } else if line[2] == b'1' {
101 ra = RelativeAbsolute::Relative;
103 } else if line[2] == b'2' {
104 let mut any_changed = false;
107 let [opt_x, opt_y, opt_z] = command(&line[4..])
108 .ok_or(GcodeError::Command)
109 .line(i_line, line)?;
110
111 if let Some(new_x) = opt_x {
112 any_changed = true;
113 x = new_x
114 }
115
116 if let Some(new_y) = opt_y {
117 any_changed = true;
118 y = new_y
119 }
120
121 if let Some(new_z) = opt_z {
122 any_changed = true;
123 z = new_z
124 }
125
126 if any_changed {
127 if !start_pushed {
128 ip.push(P::new(0.0, 0.0, 0.0));
129 start_pushed = true
130 }
131 ip.push(P::new(x, y, z));
132 }
133 }
134 }
135 }
136 }
137
138 Ok(())
139}
140
141fn command(line: &[u8]) -> Option<[Option<f64>; 3]> {
144 let mut n_found = 0;
145 let mut x = None;
146 let mut y = None;
147 let mut z = None;
148 let words = line.split(|x| *x == b' ');
149
150 for word in words {
151 if n_found == 3 {
152 break;
153 }
154
155 let n = word.len();
156
157 if n == 0 {
158 continue;
159 }
160
161 if word[0] == b';' {
162 break;
163 }
164
165 if n < 2 {
166 continue;
167 }
168
169 match word[0] {
170 b'X' => {
171 x = Some(from_ascii(&word[1..])?);
172 n_found += 1
173 }
174 b'Y' => {
175 y = Some(from_ascii(&word[1..])?);
176 n_found += 1
177 }
178 b'Z' => {
179 z = Some(from_ascii(&word[1..])?);
180 n_found += 1
181 }
182 _ => (),
183 }
184 }
185
186 Some([x, y, z])
187}
188
189enum RelativeAbsolute {
190 Relative,
191 Absolute,
192}
193
194pub enum GcodeError {
196 AccessFile,
197 Command,
198}
199
200pub type GcodeResult<T> = IOResult<T, GcodeError>;
202
203impl fmt::Debug for GcodeError {
204 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
205 match self {
206 Self::Command => write!(f, "Unable to parse command"),
207 Self::AccessFile => write!(f, "Unable to access file"),
208 }
209 }
210}
211
212impl fmt::Display for GcodeError {
213 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
214 write!(f, "{:?}", self)
215 }
216}
217
218impl From<ioError> for GcodeError {
219 fn from(_error: ioError) -> Self {
220 GcodeError::AccessFile
221 }
222}