MotionLib  1.0.0
SamBuCa motion library
MGrblSim.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-05-02T16:10:25+02:00
21 ** Author: Sylvain Fargier <sfargier> <sylvain.fargier@cern.ch>
22 **
23 */
24 
25 #include "MGrblSim.hpp"
26 
27 #include <chrono>
28 #include <cstring>
29 #include <sstream>
30 
31 #include <ccut/utils.hpp>
32 #include <logger/Logger.hpp>
33 #include <poll.h>
34 #include <stdlib.h>
35 #include <unistd.h>
36 
37 #include "Exception.hpp"
38 #include "GrblParser.hpp"
39 
40 using namespace logger;
41 using namespace std::chrono;
42 using namespace grbl;
43 using namespace smc;
44 
45 namespace mgrbl {
46 
47 static const std::string s_loggerCat{"smc:mgrbldevice:sim"};
48 
49 constexpr size_t MGrblSim::defaultInstancesCount;
50 constexpr size_t MGrblSim::axisCount;
51 
52 MGrblSim::MGrblSim(size_t instances) : trigArmed{false}
53 {
54  m_instances.reserve(instances);
55  m_layout.assign(axisCount, 0);
56  while (instances--)
57  m_instances.emplace_back(new grbl::GrblSim());
58 
59  const std::string msg("CMGrbl 1.0\r\n");
60  ::strcpy(m_buffer.end(), msg.data());
61  m_buffer.fwd(msg.length());
62 }
63 
64 MGrblSim::~MGrblSim()
65 {
66  m_instances.clear();
67 }
68 
69 void MGrblSim::read()
70 {
71  for (std::unique_ptr<grbl::GrblSim> &instance : m_instances)
72  instance->read();
73 }
74 
75 void MGrblSim::write(const std::string &raw)
76 {
77  if (raw.empty())
78  return;
79  const char c = raw[0];
80 
81  if (c >= '0' && c <= '9')
82  {
83  const size_t instance = c - '0';
84  if (instance > m_instances.size())
85  {
86  error(s_loggerCat) << "unknown instance: " << c;
87  ctrlWrite("error:3");
88  return;
89  }
90  m_instances[instance]->write(raw.substr(1));
91  }
92  else if (c == 'C')
93  processCtrl(raw);
94  else
95  {
96  error(s_loggerCat) << "unknown command: " << raw;
97  ctrlWrite("error:3");
98  }
99 }
100 
101 void MGrblSim::wake()
102 {
103  if (m_instances.empty())
104  return;
105  m_instances.at(0)->wake();
106 }
107 
108 void MGrblSim::ctrlWrite(const std::string &line)
109 {
110  m_buffer.add('C');
111  for (char c : line)
112  m_buffer.add(c);
113  m_buffer.add('\r');
114  m_buffer.add('\n');
115  wake();
116 }
117 
118 bool MGrblSim::takeLine(std::string &line)
119 {
120  if (m_buffer.takeLine(line))
121  return true;
122 
123  for (size_t i = 0; i < m_instances.size(); ++i)
124  {
125  if (m_instances[i]->takeLine(line))
126  {
127  char c[2] = {'0', 0};
128  c[0] += i;
129 
130  line = c + line;
131  return true;
132  }
133  }
134  return false;
135 }
136 
137 bool MGrblSim::wait(const std::chrono::milliseconds &timeout)
138 {
139  std::vector<struct pollfd> pfds;
140  pfds.reserve(m_instances.size() + 1);
141  bool ret = false;
142 
143  for (size_t i = 0; i < m_instances.size(); ++i)
144  {
145  pfds.emplace_back(pollfd{m_instances[i]->m_in, POLLIN | POLLERR, 0});
146  }
147  pfds.emplace_back(pollfd{m_instances[0]->m_wakeFd[0], POLLIN | POLLERR, 0});
148 
149  if (::poll(pfds.data(), pfds.size(), timeout.count() + 1) >= 0)
150  {
151  if (pfds.back().revents & POLLIN)
152  {
153  std::vector<char> buffer(10);
154  ::read(m_instances[0]->m_wakeFd[0], buffer.data(), buffer.size());
155  }
156 
157  for (size_t i = 0; i < pfds.size(); ++i)
158  {
159  if (pfds[i].revents & POLLERR)
160  throw smc::make_errno_exception(smc::ErrorCode::IO);
161  ret = ret || (pfds[i].revents & POLLIN);
162  }
163  }
164  else
165  throw smc::make_errno_exception(smc::ErrorCode::IO);
166  return ret;
167 }
168 
169 void MGrblSim::processCtrl(const std::string &line)
170 {
171  const std::string str = ccut::trim(line).substr(1);
172  try
173  {
174  if (ccut::startsWith(str, "$layout="))
175  {
176  Layout layout;
177  if ((!MGrblParser::parseLayout(str, layout)) ||
178  (layout.size() != m_layout.size()))
179  throw Exception(smc::ErrorCode::InvalidArguments,
180  "invalid layout (wrong instances count)");
181  for (size_t idx : layout)
182  {
183  if (idx >= m_instances.size())
184  throw Exception(
185  smc::ErrorCode::InvalidArguments,
186  "invalid layout (instance index out_of_range)");
187  }
188  m_layout.swap(layout);
189  ctrlWrite("ok");
190  }
191  else if (str == "$layout")
192  {
193  std::string layout(m_layout.size(), '0');
194  for (size_t i = 0; i < m_layout.size(); ++i)
195  layout[i] = static_cast<char>(m_layout[i] + '0');
196 
197  ctrlWrite("$layout=" + layout);
198  ctrlWrite("ok");
199  }
200  else if (str == "$grblCount")
201  {
202  ctrlWrite("$grblCount=" + std::to_string(m_instances.size()));
203  ctrlWrite("ok");
204  }
205  else if (str == "$I")
206  {
207  ctrlWrite("[VER:1.0]");
208  ctrlWrite("[INSTANCES:" + std::to_string(m_instances.size()) + "]");
209  ctrlWrite("ok");
210  }
211  else if (ccut::startsWith(str, "$trigArm="))
212  {
213  if (str[9] == '1')
214  {
215  for (std::unique_ptr<grbl::GrblSim> const &instance :
216  m_instances)
217  instance->send("M64P0");
218  trigArmed = 1;
219  }
220  else
221  {
222  for (std::unique_ptr<grbl::GrblSim> const &instance :
223  m_instances)
224  instance->send("M65P0");
225  trigArmed = 0;
226  }
227  ctrlWrite("ok");
228  }
229  else if (str == "$trigArm")
230  {
231  std::string res{"$trigArm="};
232  res += (trigArmed ? "1" : "0");
233  ctrlWrite(res);
234  ctrlWrite("ok");
235  }
236  else if (str == "$trigSoft")
237  {
238  // Same as disarming
239  for (std::unique_ptr<grbl::GrblSim> const &instance : m_instances)
240  instance->send("M65P0");
241  trigArmed = 0;
242  ctrlWrite("ok");
243  }
244  else
245  throw Exception(smc::ErrorCode::InvalidArguments, "unknown command");
246  }
247  catch (Exception &ex)
248  {
249  error(s_loggerCat) << ex.what() << ": " << str;
250  ctrlWrite("error:52");
251  }
252 }
253 
254 } // namespace mgrbl
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