MotionLib  1.0.0
SamBuCa motion library
GrblPlatform_Context.cpp
1 /*
2 ** Copyright (C) 2025 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: 2025-04-17T10:23:16
21 ** Author: Sylvain Fargier <sylvain.fargier@cern.ch>
22 */
23 
24 #include <ccut/utils.hpp>
25 #include <logger/Logger.hpp>
26 
27 #include "GrblPlatform.hpp"
28 #include "util/grbl/GrblDeviceBase.hpp"
29 #include "util/grbl/GrblGenerator.hpp"
30 
31 using namespace smc;
32 using namespace smc::internal;
33 using namespace logger;
34 
35 using grbl::GrblParser;
36 
37 GrblPlatform::Context::Context(const std::string &prefix,
38  const std::string &id) :
39  prefix{prefix},
40  id{id}
41 {}
42 
43 std::string GrblPlatform::Context::getErrorMessage(grbl::ErrorCode code) const
44 {
45  decltype(errors)::const_iterator it = errors.find(code);
46  if (it == errors.end())
47  {
48  return "Unknown error: " + std::to_string(code);
49  }
50  return it->second;
51 }
52 
53 std::string GrblPlatform::Context::getAlarmMessage(grbl::AlarmCode alarm) const
54 {
55  decltype(alarms)::const_iterator it = alarms.find(alarm);
56  if (it == alarms.end())
57  {
58  return "Unknown alarm: " + std::to_string(alarm);
59  }
60  return it->second;
61 }
62 
63 void GrblPlatform::Context::checkReply(grbl::ErrorCode code)
64 {
65  if (code != 0)
66  {
67  throw Exception(ErrorCode::Runtime,
68  "command failed: " + getErrorMessage(code));
69  }
70 }
71 
72 std::set<std::string> GrblPlatform::Context::listConfig()
73 {
74  std::set<std::string> ret;
75  std::transform(setting.begin(), setting.end(),
76  std::inserter(ret, ret.begin()),
77  [](const SettingMap::value_type &t) { return t.first; });
78 
79  std::transform(localSetting.begin(), localSetting.end(),
80  std::inserter(ret, ret.begin()),
81  [](const LocalSettingMap::value_type &t) { return t.first; });
82 
83  return ret;
84 }
85 
86 std::set<std::string> GrblPlatform::Context::listConfig(grbl::Axis grblAxis)
87 {
88  std::set<std::string> ret;
89  std::string groupName = to_string(grblAxis) + "-axis";
90  auto group = settingGroup.find(groupName);
91  if (group != settingGroup.end())
92  {
93  groupName += " ";
94  int groupId = group->second;
95  for (const auto &s : setting)
96  {
97  if (s.second.groupId == groupId)
98  {
99  if (ccut::startsWith(s.first, groupName))
100  ret.insert(s.first.substr(groupName.size()));
101  else
102  ret.insert(s.first);
103  }
104  }
105  }
106  return ret;
107 }
108 
109 const GrblParser::SettingDesc &GrblPlatform::Context::findSetting(
110  const std::string &name,
111  grbl::Axis axis) const
112 {
113  std::string pfx = to_string(axis) + "-axis ";
114  SettingMap::const_iterator it = setting.find(pfx + name);
115  if (it == setting.end())
116  it = setting.find(name);
117 
118  if (it == setting.end())
119  {
120  throw Exception(ErrorCode::InvalidArguments, "no such setting: " + name);
121  }
122  return it->second;
123 }
124 
125 const GrblParser::SettingDesc &GrblPlatform::Context::findSetting(
126  const std::string &name) const
127 {
128  SettingMap::const_iterator it = setting.find(name);
129 
130  if (it == setting.end())
131  {
132  throw Exception(ErrorCode::InvalidArguments, "no such setting: " + name);
133  }
134  return it->second;
135 }
136 
137 void GrblPlatform::Context::setSetting(const std::string &name,
138  const std::string &value)
139 {
140  LocalSettingMap::const_iterator itls = localSetting.find(name);
141  if (itls != localSetting.end())
142  {
143  itls->second.set(*this, value);
144  }
145  else
146  {
147  _sendSetSetting(findSetting(name).id, value);
148  }
149 }
150 
151 std::string GrblPlatform::Context::_sendGetSetting(grbl::SettingId id)
152 {
153  std::string ret;
154  grbl::ErrorCode rc = device->send(
155  grbl::gen::System::getSetting(id),
156  [this, id, &ret](const std::string &line) {
157  if (GrblParser::getLineType(line) == grbl::LineType::Setting)
158  {
159  GrblParser::Setting setting;
160  if ((GrblParser::parseSetting(line, setting)) &&
161  (setting.first == id))
162  {
163  ret = setting.second;
164  return true;
165  }
166  }
167  return false;
168  });
169  checkReply(rc);
170  return ret;
171 }
172 
174  const std::string &value)
175 {
176  grbl::ErrorCode rc = device->send(grbl::gen::System::setSetting(id, value));
177  checkReply(rc);
178 }
179 
180 bool GrblPlatform::Context::isStepperMessage(const std::string &line)
181 {
182  return ccut::startsWith(line, "[MSG:Stepper");
183 }
184 
186 {
187  grbl::ErrorCode ret{-1};
188  for (size_t i = 0; (ret != grbl::ErrorCode{0}) && (i < 3); ++i)
189  {
190  ret = this->device->sendRealtime(
191  grbl::gen::Realtime::Reset, [](const std::string &line) {
192  if (ccut::startsWith(line, "GrblHAL"))
193  throw grbl::ErrorCode{0};
194  return false;
195  });
196  if (ret != grbl::ErrorCode{0})
197  {
198  warning(s_loggerCat) << "failed to reset";
199  std::this_thread::sleep_for(std::chrono::milliseconds{1000});
200  }
201  }
202  if (ret != grbl::ErrorCode{0})
203  crit(s_loggerCat) << "failed to reset";
204 
205  /*
206  * when unlock is sent before "[MSG:'$H'|'$X' to unlock]" it sometimes fails
207  * with "error:9", but we want this sequence to also reset when motor is not
208  * locked, so let's add a small delay and a retry on unlock */
209  ret = -1;
210  for (size_t i = 0; (ret != grbl::ErrorCode{0}) && (i < 3); ++i)
211  {
212  std::this_thread::sleep_for(std::chrono::milliseconds{100});
213  ret = this->device->send(grbl::gen::System::DisableLock);
214  }
215  if (ret != grbl::ErrorCode{0})
216  crit(s_loggerCat) << "failed to unlock";
217 
218  ret = -1;
219  for (size_t i = 0; (ret != grbl::ErrorCode{0}) && (i < 3); ++i)
220  ret = this->device->sendRealtime(
221  grbl::gen::Realtime::Reset, [](const std::string &line) {
222  if (ccut::startsWith(line, "GrblHAL"))
223  throw grbl::ErrorCode{0};
224  return false;
225  });
226  if (ret != grbl::ErrorCode{0})
227  crit(s_loggerCat) << "failed to reset";
228 
229  /* updating state, otherwise next loop may reset again, it also ensures
230  * motor state is sane */
231  ret = -1;
232  for (size_t i = 0; (ret != grbl::ErrorCode{0}) && (i < 3); ++i)
233  {
234  ret = this->device->sendRealtime(
235  grbl::gen::Realtime::StatusReport,
236  [this](const std::string &message) {
237  if (grbl::GrblParser::getLineType(message) ==
238  grbl::LineType::State)
239  {
240  this->device->dataSignal(message);
241  throw grbl::ErrorCode{0};
242  }
243  return false;
244  });
245  if (ret != grbl::ErrorCode{0})
246  {
247  warning(s_loggerCat) << "failed to retrieve state";
248  std::this_thread::sleep_for(std::chrono::milliseconds{1000});
249  }
250  }
251  if (ret != grbl::ErrorCode{0})
252  crit(s_loggerCat) << "failed to retrieve state";
253  info(s_loggerCat) << "instance reset, state: " << this->state.runState;
254 }
static LineType getLineType(const std::string &line)
detect line type
Definition: GrblParser.cpp:51
Exception thrown by MotionController in case of issues with command.
Definition: Exception.hpp:61
const std::string & to_string(Axis::State state)
convert State to string
Definition: Axis.cpp:78
main motion-lib namespace
Definition: Client.cpp:30
void _sendSetSetting(grbl::SettingId settingId, const std::string &value)
set a setting on Grbl
std::set< std::string > listConfig()
list config values
void checkReply(grbl::ErrorCode code)
check command result code
const grbl::GrblParser::SettingDesc & findSetting(const std::string &name, grbl::Axis axis) const
find setting id for axis
std::string _sendGetSetting(grbl::SettingId settingId)
retrieve a setting from Grbl, eventually update internal cache