MotionLib  1.0.0
SamBuCa motion library
GrblParser.cpp
1 /*
2  * Copyright (C) 2022 CERN
3  *
4  * This software is provided 'as-is', without any express or implied
5  * warranty. In no event will the authors be held liable for any damages
6  * arising from the use of this software.
7  *
8  * Permission is granted to anyone to use this software for any purpose,
9  * including commercial applications, and to alter it and redistribute it
10  * freely, subject to the following restrictions:
11  *
12  * 1. The origin of this software must not be misrepresented; you must not
13  * claim that you wrote the original software. If you use this software
14  * in a product, an acknowledgment in the product documentation would be
15  * appreciated but is not required.
16  * 2. Altered source versions must be plainly marked as such, and must not be
17  * misrepresented as being the original software.
18  * 3. This notice may not be removed or altered from any source distribution.
19  *
20  * Created on: 2022-01-19
21  * Author: Michal Mysior <mmysior> <michal.mysior@cern.ch>
22  *
23  */
24 
25 #include "GrblParser.hpp"
26 
27 #include <algorithm>
28 #include <cctype>
29 #include <cmath>
30 #include <sstream>
31 #include <string>
32 
33 #include <ccut/Regex.hpp>
34 #include <ccut/utils.hpp>
35 #include <logger/Logger.hpp>
36 
37 #include "Exception.hpp"
38 
39 using ccut::Regex;
40 
41 static const std::string s_loggerCat{"grbl:parser"};
42 static const Regex s_settingRegex{"^\\$([0-9]{1,3})=([^\r\n]*)"};
43 static const Regex s_messageRegex{"^\\[([^:]*):(.*)\\]"};
44 static const Regex s_errorResponse("^error:([0-9]*)\\s*$");
45 
46 using namespace smc;
47 using namespace logger;
48 
49 namespace grbl {
50 
51 LineType GrblParser::getLineType(const std::string &line)
52 {
53  if (line.empty())
54  {
55  return LineType::Unknown;
56  }
57  switch (line[0])
58  {
59  case '<': return LineType::State;
60  case '$': return LineType::Setting;
61  case '[': return LineType::Message;
62  default:
63  if (line.compare(0, 2, "ok") == 0)
64  {
65  return LineType::OkReply;
66  }
67  else if (line.compare(0, 6, "error:") == 0)
68  {
69  return LineType::ErrorReply;
70  }
71  return LineType::Unknown;
72  }
73 }
74 
75 bool GrblParser::parseMessage(const std::string &line, Message &message)
76 {
77  std::vector<std::string> matches;
78  matches.reserve(3);
79  if (!s_messageRegex.match(line, matches))
80  {
81  info(s_loggerCat) << "Not a message: " << line;
82  return false;
83  }
84 
85  message = std::make_pair(matches[1], matches[2]);
86  return true;
87 }
88 
89 bool GrblParser::parseSetting(const std::string &line, Setting &setting)
90 {
91  std::vector<std::string> matches;
92  matches.reserve(3);
93  if (!s_settingRegex.match(line, matches))
94  {
95  info(s_loggerCat) << "Not a setting: " << line;
96  return false;
97  }
98 
99  setting = std::make_pair(std::atoi(matches[1].c_str()), matches[2]);
100  return true;
101 }
102 
103 bool GrblParser::parseErrorReply(const std::string &line,
104  grbl::ErrorCode &errorCode)
105 {
106  std::string::size_type idx = line.find(':');
107  if (idx == std::string::npos)
108  {
109  info(s_loggerCat) << "Not a valid error: " << line;
110  return false;
111  }
112 
113  errorCode = std::atoi(&line[idx + 1]);
114  return true;
115 }
116 
117 bool GrblParser::parseState(const std::string &line, State &state)
118 {
119  std::string::size_type end = line.rfind('>');
120  if (line.size() <= 2 || line[0] != '<' || end == std::string::npos)
121  {
122  info(s_loggerCat) << "Not a state: " << line;
123  return false;
124  }
125 
126  std::vector<std::string> split;
127  split.reserve(8);
128  ccut::split(line.cbegin() + 1, line.cbegin() + end, '|', split);
129 
130  if (split.size() == 0)
131  {
132  error(s_loggerCat) << "split failed";
133  return false;
134  }
135 
136  try
137  {
138  std::string::size_type sz = split[0].find(':');
139  if (sz != std::string::npos)
140  {
141  const std::string &part = split[0];
142  state.runState = from_string<RunState>(part.substr(0, sz));
143  char *strtoulend;
144  state.runStateArg = std::strtoul(&part[sz + 1], &strtoulend, 10);
145  if (strtoulend != part.data() + part.length())
146  {
147  error(s_loggerCat) << "invalid runStateArg: " << part;
148  return false;
149  }
150  }
151  else
152  state.runState = from_string<RunState>(split[0]);
153 
154  if (state.runState == RunState::Unknown)
155  {
156  error(s_loggerCat) << "invalid RunState: " << split[0];
157  return false;
158  }
159 
160  for (size_t i = 1; i < split.size(); ++i)
161  {
162  const std::string &part = split[i];
163  if (ccut::startsWith(part, "MPos:") ||
164  ccut::startsWith(part, "WPos:"))
165  {
166  Axis axis = Axis::X;
167  std::vector<std::string> positions;
168  positions.reserve(6);
169  ccut::split(part.cbegin() + 5, part.cend(), ',', positions);
170  for (const std::string &pos : positions)
171  {
172  state.motorPositions[axis] = std::stof(pos);
173  axis = static_cast<Axis>(static_cast<int>(axis) + 1);
174  }
175  }
176  else if (ccut::startsWith(part, "Bf:"))
177  {
178  std::vector<std::string> available;
179  available.reserve(2);
180  ccut::split(part.cbegin() + 3, part.cend(), ',', available);
181  if (available.size() != 2)
182  {
183  error(s_loggerCat) << "invalid buffer size: " << part;
184  return false;
185  }
186  state.motionBufferFree = std::stoul(available[0]);
187  state.charBufferFree = std::stoul(available[1]);
188  }
189  else if (ccut::startsWith(part, "Ln:"))
190  {
191  char *strtoulend;
192  state.line = std::strtoul(&part[3], &strtoulend, 10);
193  if (strtoulend != part.data() + part.length())
194  {
195  error(s_loggerCat) << "invalid line: " << part;
196  return false;
197  }
198  }
199  }
200  }
201  catch (const std::exception &ex)
202  {
203  error(s_loggerCat) << "exception parsing state: " << ex.what();
204  return false;
205  }
206  return true;
207 }
208 
209 bool GrblParser::parseBuildInfo(const std::string &line,
210  GrblParser::BuildInfo &buildInfo)
211 {
212  if (getLineType(line) != LineType::Message)
213  return false;
214 
215  try
216  {
217  grbl::GrblParser::Message msg;
218  if (grbl::GrblParser::parseMessage(line, msg))
219  {
220  if (msg.first == "OPT")
221  {
222  std::vector<std::string> vec;
223  vec.reserve(5);
224  ccut::split(msg.second, ',', vec);
225  if (vec.size() < 5)
226  {
227  error(s_loggerCat) << "invalid OPT in BuildInfo: " << line;
228  return false;
229  }
230  buildInfo.options = vec[0];
231  buildInfo.motionBufferSize = std::stoul(vec[1]);
232  buildInfo.charBufferSize = std::stoul(vec[2]);
233  buildInfo.axisCount = std::stoul(vec[3]);
234  buildInfo.toolsCount = std::stoul(vec[4]);
235  return true;
236  }
237  else if (msg.first == "VER")
238  buildInfo.version = msg.second;
239  else if (msg.first == "FIRMWARE")
240  buildInfo.firmware = msg.second;
241  else if (msg.first == "DRIVER")
242  buildInfo.driver = msg.second;
243  else if (msg.first == "DRIVER VERSION")
244  buildInfo.driverVersion = msg.second;
245  else
246  {
247  info(s_loggerCat) << "Not a build info: " << line;
248  return false;
249  }
250  return true;
251  }
252  return false;
253  }
254  catch (const std::exception &ex)
255  {
256  error(s_loggerCat) << "exception parsing buildInfo: " << ex.what();
257  }
258  return false;
259 }
260 
261 bool GrblParser::parseSettingGroup(const std::string &line, SettingGroup &group)
262 {
263  if (getLineType(line) != LineType::Message)
264  return false;
265 
266  try
267  {
268  grbl::GrblParser::Message msg;
269  if ((!grbl::GrblParser::parseMessage(line, msg)) ||
270  (msg.first != "SETTINGGROUP"))
271  return false;
272 
273  std::vector<std::string> vec;
274  vec.reserve(3);
275  ccut::split(msg.second, '|', vec);
276 
277  if (vec.size() < 3)
278  {
279  error(s_loggerCat) << "invalid SETTINGGROUP: " << line;
280  }
281 
282  group.id = std::stoul(vec[0]);
283  group.parentId = std::stoul(vec[1]);
284  group.name = vec[2];
285 
286  return true;
287  }
288  catch (const std::exception &ex)
289  {
290  error(s_loggerCat) << "exception parsing SettingGroup" << ex.what();
291  }
292  return false;
293 }
294 
295 bool GrblParser::parseSettingDesc(const std::string &line, SettingDesc &desc)
296 {
297  if (getLineType(line) != LineType::Message)
298  return false;
299 
300  try
301  {
302  grbl::GrblParser::Message msg;
303  if ((!grbl::GrblParser::parseMessage(line, msg)) ||
304  (msg.first != "SETTING"))
305  return false;
306 
307  std::vector<std::string> vec;
308  vec.reserve(8);
309  ccut::split(msg.second, '|', vec);
310 
311  if (vec.size() < 8)
312  {
313  error(s_loggerCat) << "invalid SETTING: " << line;
314  }
315 
316  desc.id = std::stoul(vec[0]);
317  desc.groupId = std::stoul(vec[1]);
318  desc.name = vec[2];
319  desc.unit = vec[3];
320  desc.datatype = std::stoul(vec[4]);
321  desc.format = vec[5];
322 
323  desc.min = vec[6].empty() ? NAN : std::stof(vec[6]);
324  desc.max = vec[7].empty() ? NAN : std::stof(vec[7]);
325 
326  return true;
327  }
328  catch (const std::exception &ex)
329  {
330  error(s_loggerCat) << "exception parsing SettingDesc: " << ex.what();
331  }
332  return false;
333 }
334 
335 bool GrblParser::parsePinInfo(const std::string &line, PinInfo &info)
336 {
337  if (getLineType(line) != LineType::Message)
338  return false;
339 
340  try
341  {
342  grbl::GrblParser::Message msg;
343  if ((!grbl::GrblParser::parseMessage(line, msg)) || (msg.first != "PIN"))
344  return false;
345 
346  std::vector<std::string> vec;
347  vec.reserve(3);
348  ccut::split(msg.second, ',', vec);
349 
350  if (vec.size() < 2)
351  {
352  error(s_loggerCat) << "invalid PinInfo (size): " << line;
353  return false;
354  }
355 
356  std::string::const_iterator it = std::find_if(vec[0].cbegin(),
357  vec[0].cend(), isdigit);
358  if (it == vec[0].cbegin())
359  {
360  info.pin = std::stoul(vec[0]);
361  info.port = std::string();
362  }
363  else if (it != vec[0].cend())
364  {
365  info.port = std::string(vec[0].cbegin(), it);
366  info.pin = std::stoul(std::string(it, vec[0].cend()));
367  }
368  else
369  {
370  error(s_loggerCat) << "invalid PinInfo (pin number): " << line;
371  return false;
372  }
373  info.function = vec[1];
374  info.description = (vec.size() > 2) ? vec[2] : std::string();
375 
376  return true;
377  }
378  catch (const std::exception &ex)
379  {
380  error(s_loggerCat) << "exception parsing PinInfo: " << ex.what();
381  }
382  return false;
383 }
384 
385 } // namespace grbl
static bool parseMessage(const std::string &line, Message &message)
parse message
Definition: GrblParser.cpp:75
main motion-lib namespace
Definition: Client.cpp:30
structure holding Grbl build-info ($I)
Definition: GrblParser.hpp:134
structure holding Grbl pin description ($PINS)
Definition: GrblParser.hpp:200
structure holding Grbl setting description ($ES)
Definition: GrblParser.hpp:177
structure holding Grbl settingroup info ($EG)
Definition: GrblParser.hpp:159
Struct holding state of Grbl as returned by "?".
Definition: GrblParser.hpp:96