MotionLib  1.0.0
SamBuCa motion library
MFEPlatform.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-11-22T14:52:14
21 ** Author: Sylvain Fargier <sylvain.fargier@cern.ch>
22 */
23 
24 #include "MFEPlatform.hpp"
25 
26 #include <memory>
27 #include <mutex>
28 
29 #include <ccut/async.hpp>
30 #include <fmc_mfe/libfmc_mfe.h>
31 
32 #include "MFEAxis.hpp"
33 #include "MFEAxisPositionMonitor.hpp"
34 #include "MFEEdgeGpio.hpp"
35 #include "MFEPlatformData.hpp"
36 #include "MFEPositionSensor.hpp"
37 #include "Units.hpp"
38 #include "device/DeviceBase.hpp"
39 #include "platform/PlatformBase.hpp"
40 #include "platform/specialized/MGrblPlatform/MGrblPlatform.hpp"
41 #include "util/edge/EdgeGrblDeviceBase.hpp"
42 
43 // FIXME may get dynamic in build-info
44 #define MFE_RESOLVER_COUNT 8
45 
46 using namespace logger;
47 using namespace std::chrono;
48 
49 namespace smc {
50 namespace internal {
51 
52 static const std::string s_mfePrefix{"mfe://"};
53 const std::string MFEPlatform::s_resolverMonitorSuffix{"/monitor"};
54 const std::string MFEPlatform::s_loggerCat{"smc:platform:mfe"};
55 const std::string s_modeSettingName{"mode"};
56 const std::string MFEPlatform::s_resolverSettingPrefix{"resolver "};
57 const std::string MFEPlatform::s_stepLostConfig{"step-lost threshold"};
58 const std::set<std::string> MFEPlatform::s_resolverConfigHidden{
59  "step-lost threshold", "enable", "position"};
60 const std::set<std::string> MFEPlatform::s_monitorConfigHidden{
61  "position", "mode", "enable"};
62 
63 MFEPlatform::MFEPlatform(const ccut::yml::NodeRef &ref) :
64  MGrblPlatform{ref, new Context(ref)}
65 {
66  Context &ctx(static_cast<Context &>(*m_ctx));
67 
68  ctx.edge = std::dynamic_pointer_cast<edge::EdgeGrblDeviceBase>(ctx.device);
69 }
70 
71 MFEPlatform::~MFEPlatform() {}
72 
73 MGrblAxis::Shared MFEPlatform::createAxis(
74  const std::string &uid,
75  const std::shared_ptr<GrblPlatform> &grbl)
76 {
77  return std::make_shared<MFEAxis>(uid, shared_from_this(), grbl);
78 }
79 
80 PlatformBase::DeviceTypeList MFEPlatform::getSupportedDevices() const
81 {
82  return DeviceTypeList{DeviceType::AXIS,
83  DeviceType::TRIGGER,
84  DeviceType::GPIO,
85  DeviceType::PLATFORM,
86  DeviceType::POSITION_SENSOR,
87  DeviceType::AXIS_POSITION_MONITOR};
88 }
89 
90 PlatformBase::DeviceList MFEPlatform::generateDevices(
91  const DeviceTypeList &deviceType)
92 {
93  PlatformBase::DeviceList ret{MGrblPlatform::generateDevices(deviceType)};
94 
95  if (deviceType.count(DeviceType::GPIO))
96  {
97  std::shared_ptr<MFEPlatform> self(
98  std::static_pointer_cast<MFEPlatform>(shared_from_this()));
99 
100  std::future<std::list<DeviceBase::Shared>> f =
101  run<std::list<DeviceBase::Shared>>([self](ImmediateCmd &cmd,
102  MGrblPlatform::Context &) {
103  Context &ctx(static_cast<Context &>(*self->m_ctx));
104  if (!ctx.edge)
105  throw Exception(ErrorCode::Runtime,
106  "edge driver not available");
107 
108  std::list<DeviceBase::Shared> ret;
109  self->generateGpios(ctx, ret);
110  cmd.prom->get<std::list<DeviceBase::Shared>>().set_value(ret);
111  });
112  if (f.wait_for(milliseconds(3000)) != std::future_status::ready)
113  {
114  error(s_loggerCat) << "failed to generate Gpios";
115  }
116  else
117  {
118  std::list<DeviceBase::Shared> gpiosList{f.get()};
119  ret.insert(ret.end(), gpiosList.begin(), gpiosList.end());
120  }
121  }
122 
123  const bool hasAxisPositionMonitor = deviceType.count(
124  DeviceType::AXIS_POSITION_MONITOR);
125  if (deviceType.count(DeviceType::POSITION_SENSOR))
126  {
127  std::shared_ptr<MFEPlatform> self(
128  std::static_pointer_cast<MFEPlatform>(shared_from_this()));
129 
130  const std::string platformUrl = m_ctx->prefix + m_ctx->id;
131 
132  for (size_t i = 0; i < MFE_RESOLVER_COUNT; ++i)
133  {
134  const grbl::Axis grblAxis = static_cast<grbl::Axis>(i);
135  MFEPositionSensor::Shared pos = std::make_shared<MFEPositionSensor>(
136  platformUrl + "/resolver/" + grbl::to_string(grblAxis), self);
137  MFEAxisPositionMonitor::Shared mon;
138 
139  if (hasAxisPositionMonitor && deviceType.count(DeviceType::AXIS))
140  {
141  const std::string axisUid = platformUrl + "/axis/" +
142  grbl::to_string(grblAxis);
143  const auto it = std::find_if(
144  ret.begin(), ret.end(),
145  [&axisUid](const DeviceBase::Shared &device) {
146  return device->uid() == axisUid &&
147  device->type() == DeviceType::AXIS;
148  });
149  if (it != ret.end())
150  {
151  mon = std::make_shared<MFEAxisPositionMonitor>(
152  platformUrl + "/resolver/" + grbl::to_string(grblAxis) +
153  s_resolverMonitorSuffix,
154  std::dynamic_pointer_cast<Axis>(*it), pos, self);
155  }
156  else
157  {
158  error(s_loggerCat)
159  << "Failed to find axis for resolver: "
160  << ret.back()->uid() << ", axis: " << axisUid;
161  }
162  }
163  const std::string uid{pos->uid()};
164  std::future<void> f = run<void>([self, grblAxis, i,
165  uid](ImmediateCmd &cmd,
166  MGrblPlatform::Context &) {
167  Context &ctx(static_cast<Context &>(*self->m_ctx));
168  if (!ctx.edge)
169  throw Exception(ErrorCode::Runtime,
170  "edge driver not available");
171 
172  grbl::SettingId modeSettingId = -1;
173  self->runOnInstance(
174  ctx,
175  [&modeSettingId, grblAxis](GrblPlatform::Shared &,
176  GrblPlatform::Context &ctx) {
177  modeSettingId = ctx.findSetting(
178  "resolver " + s_modeSettingName,
179  grblAxis)
180  .id;
181  },
182  true);
183  {
184  std::lock_guard<std::mutex> guard{self->m_lock};
185  self->m_resolvers.emplace(
186  uid,
187  ResolverInfo{
188  grblAxis, modeSettingId,
189  units::value_t{units::steps_per_turn, -1.0}});
190  }
191  ctx.registers[uid + "/" + to_string(units::radians)] = RegInfo{
192  ctx.edge->getDrv()->reg_map->motion_core.res_angle, i, 0,
193  32};
194  ctx.registers[uid + "/" + to_string(units::steps)] = RegInfo{
195  ctx.edge->getDrv()->reg_map->motion_core.res_step, i, 0, 32};
196  ctx.registers[uid + s_resolverMonitorSuffix + "/diff"] = RegInfo{
197  ctx.edge->getDrv()->reg_map->motion_core.res_drv_diff, i, 0,
198  32};
199  ctx.registers[uid + s_resolverMonitorSuffix +
200  "/diff/max"] = RegInfo{
201  ctx.edge->getDrv()->reg_map->motion_core.res_drv_diff_max,
202  i, 0, 32};
203  ctx.registers[uid + s_resolverMonitorSuffix + "/slost/gcr"] =
204  RegInfo{ctx.edge->getDrv()->reg_map->motion_core.res_gcr,
205  24, uint32_t(1 << i), 32};
206  ctx.registers[uid + s_resolverMonitorSuffix + "/slost/gsr"] =
207  RegInfo{ctx.edge->getDrv()->reg_map->motion_core.res_gsr, 0,
208  uint32_t(1 << i), 32};
209  cmd.prom->get<void>().set_value();
210  });
211  if (f.wait_for(milliseconds(3000)) != std::future_status::ready)
212  {
213  error(s_loggerCat)
214  << "Failed to create registers for position sensor: "
215  << platformUrl + "/resolver/" + grbl::to_string(grblAxis);
216  pos.reset();
217  mon.reset();
218  }
219  /* load parts from config */
220  if (pos)
221  {
222  pos->getConfig(s_modeSettingName).wait();
223  ret.push_back(pos);
224  }
225  if (mon)
226  ret.push_back(mon);
227  }
228  }
229 
230  if (deviceType.count(DeviceType::AXIS_POSITION_MONITOR))
231  {
232  if (!deviceType.count(DeviceType::AXIS))
233  error(s_loggerCat) << "Can't probe for AxisPositionMonitor objects "
234  "when Axis are not probed";
235  else if (!deviceType.count(DeviceType::POSITION_SENSOR))
236  error(s_loggerCat) << "Can't probe for AxisPositionMonitor objects "
237  "when Position are not probed";
238  }
239  return ret;
240 }
241 
242 void MFEPlatform::generateGpios(Context &ctx, std::list<DeviceBase::Shared> &ret)
243 {
244  const std::string platformUrl = ctx.prefix + ctx.id + "/gpio";
245 
246  ret.emplace_back(std::make_shared<MFEEdgeGpio>(platformUrl + "/dinput",
247  shared_from_this()));
248  ctx.registers[ret.back()->uid()] = RegInfo{
249  ctx.edge->getDrv()->reg_map->motion_core.dinr, 0, 0, 32};
250 
251  ret.emplace_back(std::make_shared<MFEEdgeGpio>(platformUrl + "/limit",
252  shared_from_this()));
253  ctx.registers[ret.back()->uid()] = RegInfo{
254  ctx.edge->getDrv()->reg_map->grbl_gpio.lir, 0, 0, 32};
255 
256  ret.emplace_back(std::make_shared<MFEEdgeGpio>(
257  platformUrl + "/driver/ready", shared_from_this()));
258  ctx.registers[ret.back()->uid()] = RegInfo{
259  ctx.edge->getDrv()->reg_map->motion_core.dinr, 18, 0, 8};
260 
261  ret.emplace_back(std::make_shared<MFEEdgeGpio>(
262  platformUrl + "/driver/enable", shared_from_this()));
263  ctx.registers[ret.back()->uid()] = RegInfo{
264  ctx.edge->getDrv()->reg_map->grbl_gpio.en, 0, 0, 8};
265 
266  size_t axisCount;
267  runOnInstance(
268  ctx,
269  [&axisCount](GrblPlatform::Shared &, const GrblPlatform::Context &ctx) {
270  axisCount = ctx.binfo.axisCount;
271  },
272  true);
273 
274  for (size_t i = 0; i < axisCount; ++i)
275  {
276  const grbl::Axis grblAxis = static_cast<grbl::Axis>(i);
277  ret.emplace_back(std::make_shared<MFEEdgeGpio>(
278  platformUrl + "/limit/" + grbl::to_string(grblAxis) + "Max",
279  shared_from_this()));
280  ctx.registers[ret.back()->uid()] = RegInfo{
281  ctx.edge->getDrv()->reg_map->grbl_gpio.lir, i, 0x01, 1};
282 
283  ret.emplace_back(std::make_shared<MFEEdgeGpio>(
284  platformUrl + "/limit/" + grbl::to_string(grblAxis) + "Min",
285  shared_from_this()));
286  ctx.registers[ret.back()->uid()] = RegInfo{
287  ctx.edge->getDrv()->reg_map->grbl_gpio.lir, i + 8, 0x01, 1};
288 
289  ret.emplace_back(std::make_shared<MFEEdgeGpio>(
290  platformUrl + "/driver/ready/" + grbl::to_string(grblAxis),
291  shared_from_this()));
292  ctx.registers[ret.back()->uid()] = RegInfo{
293  ctx.edge->getDrv()->reg_map->motion_core.dinr, i + 18, 0x01, 1};
294 
295  ret.emplace_back(std::make_shared<MFEEdgeGpio>(
296  platformUrl + "/driver/enable/" + grbl::to_string(grblAxis),
297  shared_from_this()));
298  ctx.registers[ret.back()->uid()] = RegInfo{
299  ctx.edge->getDrv()->reg_map->grbl_gpio.en, i, 0x01, 1};
300  }
301 }
302 
303 void MFEPlatform::processUpdate(MGrblPlatform::Context &)
304 {
305  Context &ctx(static_cast<Context &>(*m_ctx));
306  if (!ctx.edge)
307  {
308  warning(s_loggerCat)
309  << "edge device not available, not updating devices";
310  return;
311  }
312 
313  try
314  {
315  std::set<DeviceId> notifications;
316  size_t resolverCount;
317  {
318  std::lock_guard<std::mutex> guard(m_mutex);
319  resolverCount = m_resolvers.size();
320  }
321 
322  if (resolverCount)
323  {
324  debug(s_loggerCat) << "updating resolvers";
325  std::vector<uint32_t> angles(resolverCount, 0);
326  std::vector<uint32_t> steps(resolverCount, 0);
327  std::vector<uint32_t> diff(resolverCount, 0);
328  uint32_t resGCR;
329  uint32_t resGSR;
330 
331  ctx.edge->read(ctx.edge->getDrv()->reg_map->motion_core.res_angle,
332  angles);
333  ctx.edge->read(ctx.edge->getDrv()->reg_map->motion_core.res_step,
334  steps);
335  ctx.edge->read(
336  ctx.edge->getDrv()->reg_map->motion_core.res_drv_diff, diff);
337  ctx.edge->read(ctx.edge->getDrv()->reg_map->motion_core.res_gsr,
338  resGSR);
339  ctx.edge->read(ctx.edge->getDrv()->reg_map->motion_core.res_gcr,
340  resGCR);
341 
342  {
343  std::lock_guard<std::mutex> guard(m_mutex);
344  for (auto &it : m_resolvers)
345  {
346  size_t index = size_t(it.second.grblAxis);
347  if (index >= resolverCount)
348  {
349  crit(s_loggerCat)
350  << "invalid index for resolver: " << index;
351  continue;
352  }
353  AxisPositionMonitor::State state;
354  const uint8_t axis_bit = 1 << index;
355  if (resGCR & (axis_bit << 24))
356  state = AxisPositionMonitor::State::DISABLED;
357  else if (resGSR & axis_bit)
358  state = AxisPositionMonitor::State::TRIGGERED;
359  else
360  state = AxisPositionMonitor::State::ENABLED;
361 
362  if (steps[index] != it.second.stepReg)
363  notifications.insert(it.first);
364  it.second.stepReg = steps[index];
365  it.second.angleReg = angles[index];
366 
367  if (state != it.second.monitorState)
368  {
369  info(s_loggerCat)
370  << "resolver step-lost state changed: " << it.first
371  << " is " << state;
372  notifications.insert(it.first + s_resolverMonitorSuffix);
373  }
374  it.second.monitorState = state;
375  it.second.diffReg = diff[index];
376  }
377  }
378  }
379 
380  /* send notifications */
381  notify(std::move(notifications));
382  }
383  catch (const std::exception &ex)
384  {
385  error(s_loggerCat) << "MFEPlatform error: " << ex.what();
386  }
387 
388  MGrblPlatform::processUpdate(ctx);
389 }
390 
391 MFEPlatform::Context::Context(const ccut::yml::NodeRef &ref) :
392  MGrblPlatform::Context("mfe://", ref)
393 {}
394 
395 bool MFEPlatform::Context::getRegInfo(const std::string &uid,
396  RegInfo &info) const
397 {
398  std::map<std::string, RegInfo>::const_iterator it{registers.find(uid)};
399 
400  if (it == registers.cend())
401  return false;
402 
403  info = it->second;
404  return true;
405 }
406 
407 const MFEPlatform::ResolverInfo &MFEPlatform::_getResolverInfo(
408  const std::string &uid) const
409 {
410  std::map<std::string, ResolverInfo>::const_iterator it{
411  m_resolvers.find(uid)};
412  if (it == m_resolvers.cend())
413  throw Exception(ErrorCode::InvalidArguments,
414  "invalid position object: " + uid);
415  else
416  return it->second;
417 }
418 
419 grbl::Axis MFEPlatform::getResolverAxis(const std::string &uid) const
420 {
421  std::lock_guard<std::mutex> l(m_lock);
422  return _getResolverInfo(uid).grblAxis;
423 }
424 
425 grbl::Axis MFEPlatform::getResolverMonitorAxis(const std::string &uid) const
426 {
427  const std::string resolverUid = uid.substr(
428  0, uid.length() - s_resolverMonitorSuffix.length());
429  return getResolverAxis(resolverUid);
430 }
431 
432 void MFEPlatform::updateResolverSetting(const std::string &uid,
433  grbl::SettingId settingId,
434  const std::string &value)
435 {
436  std::unique_lock<std::mutex> l(m_lock);
437  std::map<std::string, ResolverInfo>::iterator it{m_resolvers.find(uid)};
438  if (it == m_resolvers.cend())
439  throw Exception(ErrorCode::InvalidArguments,
440  "invalid position object: " + uid);
441 
442  if (settingId == it->second.modeSettingId)
443  {
444  it->second.mode.value = std::stof(value);
445  l.unlock();
446 
447  info(s_loggerCat) << "updating resolver " << uid << " mode: " << value;
448  }
449 }
450 
451 units::value_t MFEPlatform::getResolverPosition(const std::string &uid,
452  const units::unit_t unit) const
453 {
454  std::lock_guard<std::mutex> l(m_lock);
455  switch (unit)
456  {
457  case units::steps:
458  {
459  return units::value_t{unit,
460  double(*reinterpret_cast<const int32_t *>(
461  &_getResolverInfo(uid).stepReg))};
462  }
463  case units::radians:
464  {
465  return units::value_t{
466  unit,
467  *reinterpret_cast<const float *>(reinterpret_cast<const void *>(
468  &_getResolverInfo(uid).angleReg))};
469  }
470  default:
471  throw Exception(ErrorCode::IncompatibleUnits,
472  "failed to convert position");
473  }
474 }
475 
476 units::value_t MFEPlatform::getResolverMode(const std::string &uid) const
477 {
478  std::lock_guard<std::mutex> l(m_lock);
479  return _getResolverInfo(uid).mode;
480 }
481 
482 MFEAxisPositionMonitor::State MFEPlatform::getResolverMonitorState(
483  const std::string &uid,
484  uint32_t &diff)
485 {
486  std::lock_guard<std::mutex> l(m_lock);
487  const ResolverInfo &info = _getResolverInfo(uid);
488  diff = info.diffReg;
489  return info.monitorState;
490 }
491 
492 } // namespace internal
493 } // namespace smc
Exception thrown by MotionController in case of issues with command.
Definition: Exception.hpp:61
void updateResolverSetting(const std::string &resolverId, grbl::SettingId settingId, const std::string &value)
hook invoked on getSetting/setSetting on resolvers
grbl::Axis getResolverMonitorAxis(const std::string &uid) const
Get the axis associated with the given AxisPositionMonitor.
grbl::Axis getResolverAxis(const std::string &uid) const
Get the axis associated to resolver.
const std::string & to_string(Axis::State state)
convert State to string
Definition: Axis.cpp:78
main motion-lib namespace
Definition: Client.cpp:30
bool getRegInfo(const std::string &uid, RegInfo &info) const
Get the RegInfo object.
units::Value mode
mode in steps_per_turn