MotionLib  1.0.0
SamBuCa motion library
GrblSim.cpp
1 /*
2 ** Copyright (C) 2021 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-02-11T17:16:38+01:00
21 ** Author: Michal Mysior <mmysior> <michal.mysior@cern.ch>
22 **
23 */
24 
25 #include "GrblSim.hpp"
26 
27 #include <algorithm>
28 #include <cerrno>
29 #include <cstring>
30 #include <thread>
31 
32 #include <ccut/Regex.hpp>
33 #include <fcntl.h>
34 #include <logger/Logger.hpp>
35 #include <poll.h>
36 #include <regex.h>
37 #include <signal.h>
38 #include <spawn.h>
39 #include <stdlib.h>
40 #include <sys/wait.h>
41 #include <unistd.h>
42 
43 #include "Exception.hpp"
44 #include "local-config.h"
45 
46 using namespace logger;
47 using namespace std::chrono;
48 using namespace grbl;
49 
50 // Grbl-sim defines for executable location
51 #define GRBL_GPIOD_EXE_DEV (BUILDDIR "/instroot/bin/grbl-gpiod")
52 #define GRBL_GPIOD_EXENAME "grbl-gpiod"
53 
54 static const std::string s_loggerCat{"smc:grbldevice:sim"};
55 const milliseconds GrblSim::defaultExpectTimeout(500);
56 static const ccut::Regex s_grblInit("^GrblHAL.*$");
57 
58 static const char *getExe()
59 {
60  if (::access(GRBL_GPIOD_EXE, X_OK) == 0)
61  return GRBL_GPIOD_EXE;
62  else if (::access(GRBL_GPIOD_EXE_DEV, X_OK) == 0)
63  return GRBL_GPIOD_EXE_DEV;
64  else
65  return "grbl-gpiod";
66 }
67 
68 GrblSim::GrblSim()
69 {
70  // Launch Grbl-sim, set up pipes
71  int pa2s[2];
72  int ps2a[2];
73 
74  ::pipe(pa2s);
75  ::pipe(ps2a);
76 
77  posix_spawn_file_actions_t fileActions;
78  posix_spawn_file_actions_init(&fileActions);
79  posix_spawn_file_actions_addclose(&fileActions, pa2s[1]);
80  posix_spawn_file_actions_adddup2(&fileActions, pa2s[0], STDIN_FILENO);
81  posix_spawn_file_actions_addclose(&fileActions, pa2s[0]);
82  posix_spawn_file_actions_addclose(&fileActions, ps2a[0]);
83  posix_spawn_file_actions_adddup2(&fileActions, ps2a[1], STDOUT_FILENO);
84  posix_spawn_file_actions_addclose(&fileActions, ps2a[1]);
85 
86  char *args[] = {const_cast<char *>(getExe()), (char *) NULL};
87  debug(s_loggerCat) << "starting " << args[0];
88  int err = posix_spawnp(&m_simPid, args[0], &fileActions, NULL, args, NULL);
89  if (err != 0)
90  {
91  errno = err;
92  crit(s_loggerCat) << "failed to spawn " << args[0];
93  }
94 
95  posix_spawn_file_actions_destroy(&fileActions);
96 
97  ::close(pa2s[0]);
98  m_out = pa2s[1];
99  ::close(ps2a[1]);
100  m_in = ps2a[0];
101 
102  ::pipe(m_wakeFd);
103  ::fcntl(m_wakeFd[0], F_SETFL, ::fcntl(m_wakeFd[0], F_GETFL, 0) | O_NONBLOCK);
104  ::fcntl(m_wakeFd[1], F_SETFL, ::fcntl(m_wakeFd[1], F_GETFL, 0) | O_NONBLOCK);
105  ::fcntl(m_out, F_SETFL, ::fcntl(m_out, F_GETFL, 0) | O_NONBLOCK);
106  ::fcntl(m_in, F_SETFL, ::fcntl(m_in, F_GETFL, 0) | O_NONBLOCK);
107 
108  if (send(
109  std::string(),
110  [](const std::string &msg) {
111  notice(s_loggerCat) << "<--(init) " << msg;
112  if (s_grblInit.search(msg))
113  throw grbl::ErrorCode{0};
114  return true;
115  },
116  std::chrono::milliseconds{3000}) != grbl::ErrorCode{0})
117  {
118  error(s_loggerCat) << "Grbl welcome banner not found";
119  throw smc::Exception{smc::ErrorCode::InternalError,
120  std::string{"Failed to get Grbl banner"}};
121  }
122 }
123 
124 GrblSim::~GrblSim()
125 {
126  // Kill Grbl-sim
127  char stopChar = 0x06;
128  ::write(m_out, &stopChar, 1);
129  ::close(m_out);
130  ::close(m_in);
131  ::close(m_wakeFd[0]);
132  ::close(m_wakeFd[1]);
133 
134  int status;
135  std::this_thread::sleep_for(std::chrono::milliseconds(100));
136  if (::waitpid(m_simPid, &status, WNOHANG) == -1)
137  {
138  ::kill(m_simPid, SIGKILL);
139  ::waitpid(m_simPid, &status, 0);
140  }
141 }
142 
143 void GrblSim::flush()
144 {
145  m_buffer.reset();
146 }
147 
148 void GrblSim::read()
149 {
150  if (!m_buffer.remaining())
151  {
152  error(s_loggerCat) << "flushing buffer";
153  m_buffer.reset();
154  }
155 
156  ssize_t sz = ::read(m_in, m_buffer.end(), m_buffer.remaining());
157  if (sz > 0)
158  {
159  // debug(s_loggerCat) << "bytes read: " << sz;
160  m_buffer.fwd(sz);
161  }
162 }
163 
164 void GrblSim::write(const std::string &raw)
165 {
166  for (size_t pos = 0; pos < raw.size();)
167  {
168  ssize_t ret = ::write(m_out, raw.c_str() + pos, raw.size() - pos);
169  if (ret < 0)
170  {
171  error(s_loggerCat)
172  << "failed to send command : " << strerror(errno);
173  return;
174  }
175  pos += ret;
176  if (pos != raw.size())
177  std::this_thread::sleep_for(std::chrono::milliseconds(10));
178  }
179 }
180 
181 bool GrblSim::wait(const std::chrono::milliseconds &timeout)
182 {
183  struct pollfd pfds[2] = {{m_in, POLLIN | POLLERR, 0},
184  {m_wakeFd[0], POLLIN | POLLERR, 0}};
185  if (::poll(pfds, 2, timeout.count() + 1) >= 0)
186  {
187  if (pfds[1].revents & POLLIN)
188  {
189  std::vector<char> buffer(10);
190  ::read(m_wakeFd[0], buffer.data(), buffer.size());
191  }
192  if (pfds[0].revents & POLLERR || pfds[1].revents & POLLERR)
193  throw smc::make_errno_exception(smc::ErrorCode::IO);
194 
195  return (pfds[0].revents & POLLIN);
196  }
197  else
198  throw smc::make_errno_exception(smc::ErrorCode::IO);
199  return false;
200 }
201 
202 void GrblSim::wake()
203 {
204  ::write(m_wakeFd[1], "X", 1);
205 }
Exception thrown by MotionController in case of issues with command.
Definition: Exception.hpp:61