25 #include "GrblPlatform.hpp"
35 #include <ccut/Regex.hpp>
36 #include <ccut/async.hpp>
37 #include <ccut/utils.hpp>
39 #include <logger/Logger.hpp>
43 #include "GrblPlatformData.hpp"
44 #include "MotionLibConfig.hpp"
45 #include "util/Promise.hpp"
46 #include "util/grbl/GrblGenerator.hpp"
47 #include "util/grbl/GrblParser.hpp"
48 #include "util/grbl/GrblSim.hpp"
49 #include "util/grbl/GrblTypes.hpp"
51 #ifdef PLATFORM_SAMBUCA
52 #include "util/edge/EdgeGrblDevice.hpp"
55 using namespace logger;
56 using namespace std::chrono;
57 namespace yml = ccut::yml;
60 #define GRBL_MIN_POS_CHANGE 0.001
62 static const ccut::Regex s_errorCode(
"^\\[ERRORCODE:([[:digit:]]+)\\|\\|(.+)"
64 static const ccut::Regex s_alarmCode(
"^\\[ALARMCODE:([[:digit:]]+)\\|\\|(.+)"
70 const std::string GrblPlatform::s_loggerCat{
"smc:platform:grbl"};
72 const milliseconds GrblPlatform::defaultInterval(1000);
74 GrblPlatform::GrblPlatform(
const ccut::yml::NodeRef &ref) :
75 Thread(
"GrblPlatform")
79 yml::NodeRef deviceNode = yml::get(ref,
"device");
80 yml::get(ref,
"id") >> yml::default_to(
id,
"0");
82 m_ctx.reset(
new GrblPlatform::Context(
"grbl://",
id));
84 if (deviceNode.is_map())
85 yml::get(deviceNode,
"type") >> yml::default_to(type, std::string());
87 deviceNode >> yml::default_to(type, std::string());
89 if (type ==
"GrblSim")
93 #ifdef PLATFORM_SAMBUCA
94 else if (type ==
"EdgeSim")
98 yml::get(deviceNode,
"lun") >> yml::default_to(lun,
int());
99 yml::get(deviceNode,
"path") >> yml::default_to(path, std::string());
102 else if (type ==
"Edge")
105 yml::get(deviceNode,
"lun") >> yml::default_to(lun,
int());
110 throw Exception(ErrorCode::InvalidArguments,
111 "unknown GrblPlatform device type");
113 m_ctx->device->dataSignal.connect(
114 m_dataConn, [
this](
const std::string &line) { processMessage(line); });
119 GrblPlatform::GrblPlatform(
const std::string &prefix,
120 const std::string &
id,
121 std::unique_ptr<grbl::GrblDeviceBase> &&device) :
122 Thread(
"GrblPlatform"),
125 m_ctx->device.swap(device);
126 m_ctx->device->dataSignal.connect(
127 m_dataConn, [
this](
const std::string &line) { processMessage(line); });
130 GrblPlatform::~GrblPlatform()
132 GrblPlatform::stop();
133 m_dataConn.disconnect();
134 m_ctx->device.reset();
139 decltype(m_axisMap)::iterator it = m_axisMap.find(uid);
140 if (it == m_axisMap.end())
142 error(GrblPlatform::s_loggerCat) <<
"invalid uid for platform: " << uid;
143 throw Exception(ErrorCode::InvalidArguments,
144 "invalid uid for platform: " + uid);
149 grbl::Axis GrblPlatform::getAxis(
const std::string &uid)
151 std::lock_guard<std::mutex> l(m_lock);
155 void GrblPlatform::updateAxisSetting(
const std::string &axis,
156 grbl::SettingId settingId,
157 const std::string &value)
159 std::lock_guard<std::mutex> l(m_lock);
161 if (settingId == data.resolutionSettingId)
164 <<
"updating axis " << axis <<
" resolution: " << value <<
" "
166 data.resolution.value = std::stof(value);
170 units::Value GrblPlatform::getLastAxisPosition(
const std::string &axis)
const
172 std::lock_guard<std::mutex> l(m_lock);
173 decltype(GrblPlatform::m_axisMap)::const_iterator it = m_axisMap.find(axis);
174 if (it == m_axisMap.end())
176 error(s_loggerCat) <<
"invalid uid for platform: " << axis;
177 throw Exception(ErrorCode::InvalidArguments,
178 "invalid uid for platform: " + axis);
180 return it->second.position;
183 units::Value GrblPlatform::getLastAxisResolution(
const std::string &axis)
const
185 std::lock_guard<std::mutex> l(m_lock);
186 decltype(GrblPlatform::m_axisMap)::const_iterator it = m_axisMap.find(axis);
187 if (it == m_axisMap.end())
189 error(s_loggerCat) <<
"invalid uid for platform: " << axis;
190 throw Exception(ErrorCode::InvalidArguments,
191 "invalid uid for platform: " + axis);
193 return it->second.resolution;
196 uint8_t GrblPlatform::getTrigger(
const std::string &uid)
198 if (!ccut::startsWith(uid, m_trigger.first))
200 error(s_loggerCat) <<
"invalid uid for platform: " << uid;
201 throw Exception(ErrorCode::InvalidArguments,
202 "invalid uid for platform: " + uid);
204 return m_trigger.second;
207 uint8_t GrblPlatform::getGpio(
const std::string &uid)
209 std::lock_guard<std::mutex> l(m_lock);
210 decltype(m_gpioMap)::const_iterator it = std::find_if(
211 m_gpioMap.begin(), m_gpioMap.end(),
212 [&uid](const std::pair<std::string, uint8_t> &p) {
213 return ccut::startsWith(uid, p.first);
215 if (it == m_gpioMap.end())
217 error(s_loggerCat) <<
"invalid uid for platform: " << uid;
218 throw Exception(ErrorCode::InvalidArguments,
219 "invalid uid for platform: " + uid);
224 void GrblPlatform::clearQueues(
const std::string &msg,
bool clearImmediates)
226 info(s_loggerCat) <<
"clearing run-queues";
227 std::unique_lock<std::mutex> lock(m_lock);
230 while (!m_immediate.empty())
234 cmd.prom->set_exception(Exception(ErrorCode::Canceled, msg));
237 while (!m_queue.empty())
239 MotionCmd cmd = std::move(m_queue.front());
241 cmd.prom.set_exception(
242 std::make_exception_ptr(Exception(ErrorCode::Canceled, msg)));
244 while (!m_pending.empty())
246 MotionCmd cmd = std::move(m_pending.front());
248 cmd.prom.set_exception(
249 std::make_exception_ptr(Exception(ErrorCode::Runtime, msg)));
253 void GrblPlatform::wake()
255 std::lock_guard<std::mutex> lock(m_mutex);
258 m_ctx->device->wake();
261 void GrblPlatform::stop()
266 void GrblPlatform::processMessage(
const std::string &message)
270 bool handled =
false;
271 switch (GrblParser::getLineType(message))
273 case grbl::LineType::State:
274 if (GrblParser::parseState(message, m_ctx->state))
280 case grbl::LineType::Message:
281 if (GrblPlatform::Context::isStepperMessage(message))
283 m_ctx->nextPoll = steady_clock::now();
292 debug(s_loggerCat) <<
"dropping message: " << message;
295 catch (std::exception &ex)
297 error(s_loggerCat) <<
"failed to processMessage: " << ex.what();
301 std::future<void> GrblPlatform::queue(
MotionCmd &&motion)
303 std::future<void> ret = motion.prom.get_future();
304 std::unique_lock<std::mutex> lock(m_lock);
307 m_queue.push(std::move(motion));
312 motion.prom.set_exception(std::make_exception_ptr(
313 Exception(ErrorCode::Canceled,
"platform stopped")));
317 void GrblPlatform::thread_func()
319 debug(s_loggerCat) <<
"started";
320 m_ctx->nextPoll = steady_clock::now();
321 const milliseconds interval = defaultInterval;
334 while (m_started.load())
336 steady_clock::time_point now = steady_clock::now();
337 const milliseconds timeout = (now < m_ctx->nextPoll) ?
338 duration_cast<milliseconds>(m_ctx->nextPoll - now) :
339 milliseconds::zero();
341 m_ctx->device->fetch(timeout);
345 while (processImmediate())
350 now = steady_clock::now();
351 if (m_ctx->nextPoll <= now)
353 m_ctx->device->sendRealtime(
"?");
354 m_ctx->nextPoll = now + interval;
358 catch (Exception &ex)
360 crit(s_loggerCat) << ex.what();
361 crit(s_loggerCat) <<
"stopping thread";
362 m_started.store(
false);
364 clearQueues(
"platform stopped");
367 void GrblPlatform::loadErrors()
370 m_ctx->checkReply(m_ctx->device->send(
371 grbl::gen::System::EnumerateErrors, [
this](
const std::string &line) {
372 std::vector<std::string> matches(3);
373 if (s_errorCode.match(line, matches))
375 int code = std::atoi(matches[1].c_str());
377 error(s_loggerCat) <<
"invalid error-code: " << line;
379 m_ctx->errors[code] = matches[2];
384 info(s_loggerCat) <<
"loaded error-codes: " << m_ctx->errors.size();
385 m_ctx->errors[grbl::CustomErrorCode::Timeout] =
"request timeout";
386 m_ctx->errors[grbl::CustomErrorCode::NoPayload] =
"no payload";
387 m_ctx->errors[grbl::CustomErrorCode::InternalError] =
"internal error";
390 void GrblPlatform::loadAlarms()
393 m_ctx->checkReply(m_ctx->device->send(
394 grbl::gen::System::EnumerateAlarms, [
this](
const std::string &line) {
395 std::vector<std::string> matches(3);
396 if (s_alarmCode.match(line, matches))
398 int code = std::atoi(matches[1].c_str());
400 error(s_loggerCat) <<
"invalid alarm-code: " << line;
402 m_ctx->alarms[code] = matches[2];
407 info(s_loggerCat) <<
"loaded alarm-codes: " << m_ctx->alarms.size();
410 void GrblPlatform::loadSettingsDesc()
413 m_ctx->checkReply(m_ctx->device->send(
414 grbl::gen::System::EnumerateGroups, [
this](
const std::string &line) {
415 GrblParser::SettingGroup group;
416 if (GrblParser::parseSettingGroup(line, group))
418 m_ctx->settingGroup[group.name] = group.id;
423 info(s_loggerCat) <<
"loaded settingGroups: " << m_ctx->settingGroup.size();
425 m_ctx->checkReply(m_ctx->device->send(
426 grbl::gen::System::EnumerateSettings,
427 [
this](
const std::string &line) {
428 GrblParser::SettingDesc desc;
429 if (GrblParser::parseSettingDesc(line, desc))
431 m_ctx->setting[desc.name] = desc;
436 std::chrono::milliseconds(5000)));
437 info(s_loggerCat) <<
"loaded settings: " << m_ctx->setting.size();
440 void GrblPlatform::setup()
442 m_ctx->_sendSetSetting(
443 m_ctx->findSetting(
"Status report options").id,
444 std::to_string((grbl::gen::Setting::StatusReportBit::BufferState |
445 grbl::gen::Setting::StatusReportBit::LineNumbers |
446 grbl::gen::Setting::StatusReportBit::FeedSpeed |
447 grbl::gen::Setting::StatusReportBit::PinState |
448 grbl::gen::Setting::StatusReportBit::ParserState |
449 grbl::gen::Setting::StatusReportBit::AlarmSubState)
452 m_ctx->_sendSetSetting(m_ctx->findSetting(
"Hard limits enable").id,
"3");
455 m_ctx->checkReply(m_ctx->device->send(
456 grbl::gen::System::BuildInfo, [
this](
const std::string &line) {
457 return GrblParser::parseBuildInfo(line, m_ctx->binfo);
461 bool GrblPlatform::processImmediate()
463 std::unique_lock<std::mutex> lock(m_lock);
464 if (m_immediate.empty())
472 cmd.run(cmd, *m_ctx);
474 catch (Exception &ex)
476 cmd.prom->set_exception(ex);
478 catch (std::exception &ex)
480 cmd.prom->set_exception(Exception(ErrorCode::Runtime, ex.what()));
485 bool GrblPlatform::processMotion()
488 if (m_ctx->state.motionBufferFree <= 0 ||
489 (m_ctx->state.runState != grbl::RunState::Idle &&
490 m_ctx->state.runState != grbl::RunState::Run))
493 std::unique_lock<std::mutex> lock(m_lock);
496 MotionCmd cmd = std::move(m_queue.front());
501 cmd.run(cmd, *m_ctx);
503 catch (Exception &ex)
505 cmd.prom.set_exception(std::make_exception_ptr(ex));
508 catch (std::exception &ex)
510 cmd.prom.set_exception(
511 std::make_exception_ptr(Exception(ErrorCode::Runtime, ex.what())));
515 m_pending.push(std::move(cmd));
516 --m_ctx->state.motionBufferFree;
520 void GrblPlatform::onStateMessage()
524 if (m_ctx->state.runState == grbl::RunState::Alarm)
531 std::set<DeviceId> notifications;
533 std::lock_guard<std::mutex> lock(m_lock);
534 for (
auto &it : m_axisMap)
536 const auto &newPosition = m_ctx->state.motorPositions.find(
538 if (newPosition == m_ctx->state.motorPositions.end())
539 warning(s_loggerCat) <<
"no status for axis " << it.first;
540 else if (std::isnan(it.second.position.value) ||
541 fabs(it.second.position.value - newPosition->second) >=
544 it.second.position.value = newPosition->second;
545 notifications.insert(it.first);
551 size_t motionQueued = m_ctx->binfo.motionBufferSize -
552 m_ctx->state.motionBufferFree;
557 if (!motionQueued && (m_ctx->state.runState == grbl::RunState::Run))
559 while (m_pending.size() > motionQueued)
561 m_pending.front().prom.set_value();
566 notify(std::move(notifications));
570 void GrblPlatform::handleAlarm()
572 if (m_ctx->state.runState != grbl::RunState::Alarm)
575 const std::string msg = m_ctx->getAlarmMessage(
576 static_cast<grbl::AlarmCode
>(m_ctx->state.runStateArg));
577 warning(s_loggerCat) <<
"alarm detected, resetting: " << msg;
578 clearQueues(msg,
false);
579 info(s_loggerCat) <<
"motion-queue flushed";
Exception thrown by MotionController in case of issues with command.
const std::string & to_string(Axis::State state)
convert State to string
static Modal set(uint8_t system)
construct modal to set coordinate system
main motion-lib namespace