Bus.cpp
Go to the documentation of this file.
1 #include "Bus.h"
2 
3 #include <algorithm>
4 #include <iomanip>
5 
6 #include <ethercat.h>
7 
12 
15 
20 
21 #include "ErrorReporting.h"
23 
25 {
26  static constexpr std::uint8_t DEFAULT_ECAT_GROUP = 0;
27 
28  Bus&
30  {
31  static Bus _instance;
32  return _instance;
33  }
34 
35  std::string
36  Bus::logErrorsToFile(std::filesystem::path path, unsigned int lastSeconds)
37  {
38  auto ret = Reporting::getErrorReporting().dumpToFile(path, lastSeconds);
39  if (ret.has_value())
40  {
41  return ret.value().string();
42  }
43  else
44  {
45  return std::string();
46  }
47  }
48 
49  Bus::Bus() : busState(EtherCATState::init), currentGroup(DEFAULT_ECAT_GROUP), errorHandler(this)
50  {
51  setTag("ethercat::Bus");
52  }
53 
54  bool
55  Bus::setPDOMappings()
56  {
58  bool retVal = true;
59  int byteSum = 0;
60 
61  for (std::unique_ptr<SlaveInterface>& slave : slaves)
62  {
63  ARMARX_INFO << "Checking mapping for slave " << slave->getSlaveNumber()
64  << " name: " << slave->getSlaveIdentifier().getName();
65  if (!slave->hasPDOMapping())
66  {
67  ARMARX_INFO << "No mapping needed for " << slave->getSlaveIdentifier().getName();
68  continue;
69  }
70  byteSum +=
71  ec_slave[slave->getSlaveNumber()].Obytes + ec_slave[slave->getSlaveNumber()].Ibytes;
72  if (ec_slave[slave->getSlaveNumber()].outputs == nullptr ||
73  ec_slave[slave->getSlaveNumber()].inputs == nullptr)
74  {
75  BUS_ERROR(iterationCount,
76  "There is a nullpointer in the mapping of slave %d",
77  slave->getSlaveNumber());
78 
79  ARMARX_IMPORTANT << "current slave" << slave->getSlaveNumber();
80  ARMARX_IMPORTANT << "slaveCount: " << ec_slavecount;
81  ARMARX_IMPORTANT << "iomap ptr: 0x" << std::hex << &ioMap << std::dec;
82  ARMARX_IMPORTANT << "sn:" << slave->getSlaveNumber() << std::flush;
83  ARMARX_IMPORTANT << "out: 0x" << std::hex
84  << reinterpret_cast<long>(
85  ec_slave[slave->getSlaveNumber()].outputs)
86  << std::dec << std::flush;
87  ARMARX_IMPORTANT << "in: 0x" << std::hex
88  << reinterpret_cast<long>(ec_slave[slave->getSlaveNumber()].inputs)
89  << std::dec << std::flush;
90  ARMARX_IMPORTANT << "in diff: "
91  << static_cast<long>(ec_slave[slave->getSlaveNumber()].inputs -
92  ec_slave[0].outputs)
93  << std::flush;
94  ARMARX_IMPORTANT << "-------------------------------------------------------";
95 
96 
97  auto ec_errorToString = [](ec_errort const& error) -> std::string
98  {
99  std::stringstream result;
100  result << VAROUT(error.Etype) << "\n"
101  << VAROUT(error.Signal) << "\n"
102  << VAROUT(error.Slave) << "\n"
103  << VAROUT(error.Index) << "\n"
104  << VAROUT(error.SubIdx) << "\n"
105  << "\n";
106  return result.str();
107  };
108 
109  ec_errort error_type;
110  while (ec_poperror(&error_type))
111  {
112  BUS_WARNING(
113  iterationCount, "SOEM error: %s", ec_errorToString(error_type).c_str());
114  }
115  retVal = false;
116  }
117  else
118  {
119  //setting pdo mappings slave inputs are master outputs and vice versa
120  slave->setInputPDO(ec_slave[slave->getSlaveNumber()].outputs);
121  slave->setOutputPDO(ec_slave[slave->getSlaveNumber()].inputs);
122  }
123  }
124  ARMARX_VERBOSE << "PDO size: " << byteSum;
125  return retVal;
126  }
127 
128  bool
129  Bus::createDevices()
130  {
131  auto printAdditionalInformation =
132  [&](const std::string info,
133  const std::vector<std::pair<std::string, std::string>> reasonAndSolutions)
134  {
135  std::this_thread::sleep_for(std::chrono::milliseconds(1));
136  std::stringstream ss;
137  ss << "\n" << info << "\n\n";
138  ss << "Possible reasons are:\n";
139  for (const auto& [reason, solution] : reasonAndSolutions)
140  {
141  ss << "- " << reason << "\n";
142  ss << "(" << solution << ")\n";
143  }
144  ARMARX_IMPORTANT << ss.str();
145  };
146 
147 
148  ARMARX_TRACE;
149 
150  // Error reporter for slave construction
152 
153  ARMARX_VERBOSE << "Constructing slaves.";
154  for (std::uint16_t currentSlaveIndex = 1; currentSlaveIndex <= ec_slavecount;
155  ++currentSlaveIndex)
156  {
157  ARMARX_TRACE;
158 
159  auto createSlaveIdentifierFromESI =
160  [&](std::uint16_t slaveIndex) -> std::optional<SlaveIdentifier>
161  {
162  auto esiHeaderBinary =
164  if (esiHeaderBinary.has_value())
165  {
166  ESIParser p;
167  ESIHeader header = p.parseHeader(esiHeaderBinary.value());
168 
169  SlaveIdentifier sid;
170  sid.slaveIndex = static_cast<std::int16_t>(slaveIndex);
171  sid.vendorID = header.vendorID;
172  sid.productCode = header.productCode;
173  sid.serialNumber = header.serialNumber;
174  sid.revisionNumber = header.revisionNumber;
175  return std::move(sid);
176  }
177  else
178  {
179  return std::nullopt;
180  }
181  };
182 
183  auto maybeSid = createSlaveIdentifierFromESI(currentSlaveIndex);
184  if (!maybeSid.has_value())
185  {
186  BUS_ERROR_LOCAL(reporter,
187  iterationCount,
188  "Failed to create slaveIdentifier from ESI for slave at index %d",
189  currentSlaveIndex);
190  continue;
191  }
192  SlaveIdentifier slaveIdentifier = maybeSid.value();
193 
194  struct
195  {
196  bool found = false;
197  std::string name = "";
198  } foundSlaveInfo;
199 
200  try
201  {
202  BUS_INFO(iterationCount, "Number of slaveFactories is %d", slaveFactories.size());
203  for (SlaveFactory& factory : slaveFactories)
204  {
205  std::unique_ptr<SlaveInterface> slave = factory(slaveIdentifier);
206 
207  if (slave)
208  {
209  if (not foundSlaveInfo.found)
210  {
211  foundSlaveInfo.name = slave->getSlaveIdentifier().getName();
212  slave->setLocalMinimumLoggingLevel(this->getEffectiveLoggingLevel());
213  slaves.push_back(std::move(slave));
214  foundSlaveInfo.found = true;
215  }
216  else
217  {
218  SLAVE_ERROR_LOCAL(reporter,
219  slaveIdentifier,
220  "Slave already constructed as an `%s`, "
221  "but can also be constructed as an `%s`.",
222  foundSlaveInfo.name.c_str(),
223  slave->getSlaveIdentifier().getNameAsCStr());
224  }
225  }
226  }
227  }
228  catch (LocalException& e)
229  {
230  SLAVE_ERROR_LOCAL(reporter,
231  slaveIdentifier,
232  "Error during slave creation; Reason: %s",
233  e.getReason().c_str());
234  continue;
235  }
236 
237  if (not foundSlaveInfo.found)
238  {
240  reporter, slaveIdentifier, "Could not find a corresponding factory");
241  }
242  }
243 
244  // Not all slaves on the bus could be created correctly
245  // In the sprit of "fail early, fail fast", we do not procede with the device creation
246  if (reporter.hasErrors())
247  {
248  printAdditionalInformation(
249  "One or more slave objects could not be created from the physical slaves found on "
250  "the bus.",
251  {{"No factory is known for a found slave",
252  "The corresponding factory must be registered in the specialization of the "
253  "RTUnit by calling bus.registerSlaveFactory(...) in its constructor"},
254  {"The found slave has different ESI informations (vendorID, productCode and "
255  "serialNumber) than expected by all registered slave factories",
256  "reflash the EEPROM with correct data or adapt the function "
257  "isSlaveIdentifierAccepted() in the corresponding slave class"},
258  {"Corrupted ESI information in the EEPROM of the slave",
259  "Reflash the EEPROM with correct data"}});
260  return false;
261  }
262 
263  BUS_INFO(iterationCount, "Number of slaves pushed into slavesView is %d", slaves.size());
264  // constructing a view of observerptrs on the slaves
265  std::transform(slaves.begin(),
266  slaves.end(),
267  std::back_inserter(slavesView),
268  [](auto& slave) { return std::experimental::make_observer(slave.get()); });
269 
270  // New error reporter for device construction
272 
273  ARMARX_VERBOSE << "Constructing devices.";
274  for (const auto& [_, deviceConfig] : hwconfig.deviceConfigs)
275  {
276  try
277  {
278  const std::string deviceClassName = deviceConfig->getType();
279  const std::string deviceInstanceName = deviceConfig->getName();
280  ARMARX_DEBUG << "Handling: " << deviceClassName << " name: " << deviceInstanceName
281  << ".";
282 
283  if (deviceFactories.find(deviceClassName) != deviceFactories.end())
284  {
285  auto factory = deviceFactories.at(deviceClassName);
286  ARMARX_CHECK_NOT_NULL(robot)
287  << "Robot is null! You must set the robot via Bus::setRobot()!";
288 
289  std::shared_ptr<DeviceInterface> device = factory(*deviceConfig, robot);
290 
291  // Add devices
292  if (device)
293  {
294  devices.push_back(std::move(device));
295  }
296  else
297  {
298  BUS_WARNING_LOCAL(reporter,
299  iterationCount,
300  "Could not create device instance '%s' of class '%s'",
301  deviceInstanceName.c_str(),
302  deviceClassName.c_str());
303  }
304  }
305  else
306  {
307  BUS_ERROR_LOCAL(reporter,
308  iterationCount,
309  "No device factory found for xml-node `%s` with name `%s` in "
310  "hardware config.",
311  deviceClassName.c_str(),
312  deviceInstanceName.c_str());
313  }
314  }
315  catch (LocalException& e)
316  {
317  BUS_ERROR_LOCAL(reporter,
318  iterationCount,
319  "Exception during device creation. Device class: %s; Reason: %s",
320  deviceConfig->getName().c_str(),
321  e.getReason().c_str());
322  continue;
323  }
324  catch (...)
325  {
327  }
328  }
329 
330  ARMARX_VERBOSE << "Checking if hardware config does not contain unnecessary items.";
331  std::vector<std::string> errorsList;
332  if (not hwconfig.checkAllItemsRead(errorsList))
333  {
334  // TODO: Maybe do this on a per-device basis and use DEVICE_FATAL_AND_THROW
335 
336  for (auto& message : errorsList)
337  {
339  }
340  // TODO: Is this the right way to do it?
341  throw std::runtime_error(
342  "The hardware_config did not match the code for the devices, see log");
343  }
344 
345  // There where errors during the creation of the devices.
346  // Better return here instead of trying to continue with possible malformed list of devices.
347  if (reporter.hasErrors())
348  {
349  printAdditionalInformation(
350  "One or more devices could not created from the provided HardwareConfig.",
351  {{"No factory known for device description",
352  "The corresponding device factory must be registered in the specialization of "
353  "the RTUnit by calling bus.registerDeviceFactory(...) in its constructor. It is "
354  "important that the first parameter fits exactly the name used in the "
355  "HardwareConfig"},
356  {"Exception during the construction of the device",
357  "Make sure that there are no programming errors in the constructor of the "
358  "device"}});
359  return false;
360  }
361 
362  // New error reporter for assigning and validating
364 
365  ARMARX_VERBOSE << "Assigning slaves to devices.";
366  for (std::unique_ptr<SlaveInterface>& slave : slaves)
367  {
368  ARMARX_TRACE;
369 
370  bool slaveAssigned = false;
371  for (std::shared_ptr<DeviceInterface>& device : devices)
372  {
373  DeviceInterface::TryAssignResult result = device->tryAssign(*slave);
374  switch (result)
375  {
377  slaveAssigned = true;
378  slave->setParentDeviceName(device->getDeviceName());
380  // Okay, just continue.
381  break;
383  BUS_FATAL_AND_THROW(iterationCount,
384  "Tried to assign ambiguous slave with name `%s` (idx: "
385  "%u) again to device (%s) with name `%s`.",
386  slave->getSlaveIdentifier().getNameAsCStr(),
387  slave->getSlaveIdentifier().slaveIndex,
388  device->getClassName().c_str(),
389  device->rtGetDeviceName());
390  }
391  }
392 
393  // Slaves without PDOs dont need a device to be assigned to.
394  if (not slaveAssigned && slave->hasPDOMapping())
395  {
396  BUS_ERROR_LOCAL(reporter,
397  iterationCount,
398  "Could not find a device to assign following slave to:\n%s",
399  slave->getSlaveIdentifier().toString().c_str());
400  }
401  }
402 
403  // There where errors during the assigment of the slaves to the devices.
404  // Most likely this is due to the fact, that there are slaves which do not have been assigned.
405  // It does not make sense to continue since not all bus members will be controlled correctly.
406  if (reporter.hasErrors())
407  {
408  printAdditionalInformation(
409  "Not all found slaves could not be assigned to devices",
410  {{"Unexpected slaves are part of the bus",
411  "Disconnect slaves that have not been described in the HardwareConfig from the "
412  "bus or add additional entries to the HardwareConfig to map the whole bus"},
413  {"SlaveIdentifier in the HardwareConfig do not match the actual slaves",
414  "Make sure, that the slave identifiers for all salves in the HardwareConfig are "
415  "correct. This includes vendorID, productCode and serialNumer."}});
416  return false;
417  }
418 
419  ARMARX_VERBOSE << "Sanity checking that devices have all needed slaves assigned.";
420  for (std::shared_ptr<DeviceInterface>& device : devices)
421  {
422  DeviceInterface::AllAssignedResult result = device->onAllAssigned();
423  switch (result)
424  {
426  // Okay, just continue.
427  break;
430  iterationCount,
431  "Device `%s` ("
432  "%s) reports not all slaves valid. Maybe a slave is missing on the bus",
433  device->getClassName().c_str(),
434  device->rtGetDeviceName());
435  }
436  }
437 
438  ARMARX_VERBOSE << "Adding SlaveErrorRegistersDevice to slaves.";
439  for (auto& slave : slaves)
440  {
441  if (slave)
442  slave->setErrorRegistersDevice(std::make_shared<SlaveErrorRegistersDevice>(
443  "SlaveIdx_" + std::to_string(slave->getSlaveIdentifier().slaveIndex) + "." +
444  slave->getSlaveIdentifier().getName(),
445  slave->getSlaveIdentifier().slaveIndex));
446  }
447 
448 
449  return true;
450  }
451 
452  void
453  Bus::setSocketFileDescriptor(int socketFileDescriptor)
454  {
455  this->socketFileDescriptor = socketFileDescriptor;
456  }
457 
458  void
459  Bus::setIfName(const std::string& ifname)
460  {
461  this->ifname = ifname;
462  }
463 
464  bool
465  Bus::rtUpdateBus(bool doErrorHandling, size_t iteration)
466  {
467  ARMARX_TRACE;
468  if (iterationCount != iteration - 1)
469  {
470  BUS_WARNING(
471  iteration,
472  "Updating the bus has been skipped. The number of the last rt-thread iteration the "
473  "bus has been updated was %d and the current iteration is %d.",
474  iterationCount,
475  iteration);
476  }
477  iterationCount = iteration;
478  if (busState != EtherCATState::op)
479  {
480  BUS_WARNING(iterationCount,
481  "UpdateBus can not be executed because the bus is not in op-mode");
482  return false;
483  }
484 
485  // handle emergency stop release
486  BUS_TIMING_START(bus_rtUpdateBus_isEmergencyStopActive);
487  bool const emergencyStopActive = rtIsEmergencyStopActive();
488  BUS_TIMING_CEND(bus_rtUpdateBus_isEmergencyStopActive, iterationCount, 0.3);
489 
490  // the elmo sometimes go from emergency stop state back into fault state for a moment - the time variable is the fix for that
491  BUS_TIMING_START(bus_rtUpdateBus_recoverFromEmergencyStop)
492  if ((not emergencyStopActive and emergencyStopWasActivated))
493  {
494  bool recovered = true;
495  for (std::unique_ptr<SlaveInterface>& slave : slaves)
496  {
497  recovered &= slave->recoverFromEmergencyStop();
498  }
499  if (recovered)
500  {
501  BUS_WARNING(iterationCount, "Recovered from STO (Safe Torque Off)");
502  emergencyStopWasActivated = false;
503  }
504  }
505  else if (emergencyStopActive and not emergencyStopWasActivated)
506  {
507  BUS_WARNING(iterationCount, "STO (Safe Torque Off) active");
508  emergencyStopWasActivated = emergencyStopActive;
509  }
510  BUS_TIMING_CEND(bus_rtUpdateBus_recoverFromEmergencyStop, iterationCount, 0.3)
511  const auto delay = (armarx::rtNow() - busUpdateLastUpdateTime);
512  if (delay.toMilliSecondsDouble() > 40)
513  {
514  BUS_WARNING(iterationCount,
515  "Update bus was not called for a long time: %ld ms",
516  delay.toMilliSeconds());
517  }
518 
519  //send and receive process data
520  BUS_TIMING_START(bus_rtUpdateBus_updatePDO)
521  rtUpdatePDO();
522  busUpdateLastUpdateTime = armarx::rtNow();
523  BUS_TIMING_CEND(bus_rtUpdateBus_updatePDO, iterationCount, 0.6)
524 
525  // Execute every slave.
526  BUS_TIMING_START(bus_rtUpdateBus_executeSlaves)
527  for (std::unique_ptr<SlaveInterface>& slave : slaves)
528  {
529  slave->execute();
530  }
531  BUS_TIMING_CEND(bus_rtUpdateBus_executeSlaves, iterationCount, 0.3)
532 
533  //error handling
534  if (doErrorHandling)
535  {
536  if (!emergencyStopActive)
537  {
538  BUS_TIMING_START(bus_rtUpdateBus_slaveErrorHandling)
539  errorHandler.rtHandleSlaveErrors();
540  BUS_TIMING_CEND(bus_rtUpdateBus_slaveErrorHandling, iterationCount, 0.3)
541  }
542  BUS_TIMING_START(bus_rtUpdateBus_busErrorHandling)
543  errorHandler.rtHandleBusErrors();
544  BUS_TIMING_CEND(bus_rtUpdateBus_busErrorHandling, iterationCount, 0.3)
545  }
546 
547  // Update error registers
548  BUS_TIMING_START(bus_rtUpdateBus_updateErrorRegisters)
549  if (!errorHandler.hasError() && errorRegisterReadingScheduler)
550  {
551  if (errorRegisterReadingScheduler->allRegistersUpdated())
552  {
553  auto data_ref = errorRegisterReadingScheduler->getRegisterData();
554  for (const auto& data : data_ref)
555  {
556  auto index = data.slaveIndex;
557  this->getSlaveAtIndex(index)->getErrorRegistersDevice()->updateValues(&data);
558  }
559  }
560  errorRegisterReadingScheduler->startReadingNextRegisters();
561  }
562  BUS_TIMING_CEND(bus_rtUpdateBus_updateErrorRegisters, iterationCount, 0.3)
563 
564  // Update functional state
565  BUS_TIMING_START(bus_rtUpdateBus_updateFunctionalState)
566  if (errorHandler.isReinitializationActive())
567  {
568  functionalState = BusFunctionalState::Reinitializing;
569  }
570  else if (emergencyStopActive)
571  {
572  functionalState = BusFunctionalState::EmergencyStop;
573  }
574  else
575  {
576  functionalState = BusFunctionalState::Running;
577  }
578  BUS_TIMING_CEND(bus_rtUpdateBus_updateFunctionalState, iterationCount, 0.3)
579 
580  return true;
581  }
582 
583  void
585  {
586  if (busState == EtherCATState::init)
587  {
588  BUS_WARNING(iterationCount, "Bus is already shutdown");
589  }
590  else
591  {
592  ARMARX_IMPORTANT << "Reading EtherCAT error registers from all slaves.";
593  readAllErrorCounters();
594 
595  ARMARX_INFO << "Bus is shutting down";
596 
597  //shutdown the slaves if the bus hasn't died
598  if (busState == EtherCATState::op)
599  {
600  bool found;
601  do
602  {
603  found = false;
604  for (std::unique_ptr<SlaveInterface>& slave : slaves)
605  {
606  ARMARX_INFO << deactivateSpam(1, std::to_string(slave->getSlaveNumber()))
607  << "shutting down slave "
608  << slave->getSlaveIdentifier().getName() << " ("
609  << slave->getSlaveNumber() << "/" << slaves.size() << ")";
610  found |= !slave->shutdown();
611  }
612  rtUpdatePDO();
613  } while (found);
614  }
615 
616  // shutting down bus
617  // requesting init for all slaves
618  if (!switchBusToInit())
619  {
620  ARMARX_DEBUG << "Could not switch all slaves to Init.";
621  }
622  }
623 
624  // closing bus
625  ec_close();
626 
627  socketInitialized = false;
628 
629  ARMARX_DEBUG << "Bus shutdown";
630 
631  functionalState = BusFunctionalState::Shutdown;
632  }
633 
636  {
637  return pdoValidity;
638  }
639 
640  bool
642  {
643  static const std::map<EtherCATState, std::function<bool(Bus*)>> transitions = {
644  {EtherCATState::preOp, &Bus::_preOpToInit},
645  {EtherCATState::safeOp, &Bus::_safeOpToInit},
646  {EtherCATState::op, &Bus::_opToInit},
648  [](Bus* b)
649  {
650  BUS_WARNING(b->iterationCount, "Bus is already in init");
651  return true;
652  }}};
653 
654  ARMARX_VERBOSE << "Switching bus to init";
655 
656  return transitions.at(busState)(this);
657  }
658 
659  bool
661  {
662  static const std::map<EtherCATState, std::function<bool(Bus*)>> transitions = {
663  {EtherCATState::init, &Bus::_initToPreOp},
664  {EtherCATState::safeOp, &Bus::_safeOpToPreOp},
665  {EtherCATState::op, &Bus::_opToPreOp},
667  [](Bus* b)
668  {
669  BUS_WARNING(b->iterationCount, "Bus is already in preOp");
670  return true;
671  }},
672  };
673 
674  ARMARX_VERBOSE << "Switching bus to preOp";
675 
676  return transitions.at(busState)(this);
677  }
678 
679  bool
681  {
682  static const std::map<EtherCATState, std::function<bool(Bus*)>> transitions = {
683  {EtherCATState::init, &Bus::_initToSafeOp},
684  {EtherCATState::preOp, &Bus::_preOpToSafeOp},
685  {EtherCATState::op, &Bus::_opToSafeOp},
687  [](Bus* b)
688  {
689  BUS_WARNING(b->iterationCount, "Bus is already in safeOp");
690  return true;
691  }},
692  };
693 
694  ARMARX_VERBOSE << "Switching bus to safeOp";
695 
696  return transitions.at(busState)(this);
697  }
698 
699  bool
701  {
702  static const std::map<EtherCATState, std::function<bool(Bus*)>> transitions = {
703  {EtherCATState::init, &Bus::_initToOp},
704  {EtherCATState::preOp, &Bus::_preOpToOp},
705  {EtherCATState::safeOp, &Bus::_safeOpToOp},
707  [](Bus* b)
708  {
709  BUS_WARNING(b->iterationCount, "Bus is already in op");
710  return true;
711  }}};
712 
713  return transitions.at(busState)(this);
714  }
715 
716  bool
717  Bus::_initToPreOp()
718  {
719  ARMARX_VERBOSE << "Start switching bus from 'Init' to 'PreOp'";
721  {
722  ARMARX_VERBOSE << "Finish switching bus from 'Init' to 'PreOp'";
723  };
724 
726 
727  if (!socketInitialized && !initSocket())
728  {
729  BUS_ERROR(iterationCount, "Could not init socket. Abort.");
730  return false;
731  }
732 
733  if (ec_config_init(FALSE) <= 0)
734  {
735  BUS_ERROR(iterationCount, "No slaves found on bus");
736  return false;
737  }
738 
739  bool ret = changeBusState(EtherCATState::preOp);
740  if (ret)
741  {
742  // SDOs can now be read and written (flag is used by SDOHandler)
743  sdoAccessAvailable = true;
744  ARMARX_INFO << ec_slavecount << " slaves found and set from Init to PreOp";
745  }
746 
747  return ret;
748  }
749 
750  bool
751  Bus::_initToSafeOp()
752  {
754 
755  return _initToPreOp() and _preOpToSafeOp();
756  }
757 
758  bool
759  Bus::_initToOp()
760  {
762 
763  return _initToPreOp() and _preOpToSafeOp() and _safeOpToOp();
764  }
765 
766  bool
767  Bus::_preOpToInit()
768  {
769  ARMARX_VERBOSE << "Start switching bus from 'PreOp' to 'Init'";
771  {
772  ARMARX_VERBOSE << "Finish switching bus from 'PreOp' to 'Init'";
773  };
774 
776 
777  return changeBusState(EtherCATState::init);
778  }
779 
780  bool
781  Bus::_preOpToSafeOp()
782  {
783  ARMARX_VERBOSE << "Start switching bus from 'PreOp' to 'SafeOp'";
785  {
786  ARMARX_VERBOSE << "Finish switching bus from 'PreOp' to 'SafeOp'";
787  };
788 
790 
791  //prepare Devices to be read to switch to Safe-Op
792  if (slaves.size() > 0)
793  {
794  if (!validateBus())
795  {
796  shutdown();
798  iterationCount,
799  "Bus could not be validated. This indicates a severe hardware-error, "
800  "since the amount of slaves on the bus changed during the initialization "
801  "of the bus which can only happen if a PCB is behaving incorrectly or "
802  "(most likely) a cable is broken. Shutdown bus!");
803  return false;
804  }
805  }
806  else
807  {
808  // Create devices and populate members.
809  if (!this->createDevices())
810  {
811  BUS_ERROR(iterationCount, "Error during slave and device creation");
812  return false;
813  }
814 
815  if (slaves.size() < 1)
816  {
817  BUS_ERROR(iterationCount, "There are no usable devices on the bus!");
818  return false;
819  }
820  ARMARX_INFO << "Devices were created";
821  }
822 
823  for (std::unique_ptr<SlaveInterface>& slave : slaves)
824  {
825  slave->doMappings();
826  }
827 
828  ARMARX_TRACE;
829 
830  for (std::unique_ptr<SlaveInterface>& slave : slaves)
831  {
832  slave->prepareForSafeOp();
833  }
834 
835  ARMARX_TRACE;
836 
837  for (std::unique_ptr<SlaveInterface>& slave : slaves)
838  {
839  slave->finishPreparingForSafeOp();
840  }
841 
842  ARMARX_TRACE;
843 
844  int actualMappedSize = ec_config_map(ioMap.data());
845 
846  //calculating Workcounter after mapping to have an error indication later
847  int expectedWKC = (ec_group[0].outputsWKC * 2) + ec_group[0].inputsWKC;
848  ARMARX_VERBOSE << "Calculated workcounter: " << expectedWKC << std::endl;
849 
850  errorHandler.init(expectedWKC);
851 
852  ///give the devices their mapping
853  if (!setPDOMappings())
854  {
855  BUS_ERROR(
856  iterationCount,
857  "Couldn't map the PDO, maybe the the pc is under to much load. Check if there are "
858  "other performance hungry programs running.\nOr just try to start again");
859  return false;
860  }
861 
862  bool ret = changeBusState(EtherCATState::safeOp);
863 
864  if (ret)
865  {
866  ARMARX_INFO << "IOmapping done, size: " << actualMappedSize
867  << " - all Slaves are in SAFE-OP now\n";
868  }
869 
870  // Run further initialization code for devices
871  for (const auto& device : devices)
872  {
873  device->postSwitchToSafeOp();
874  }
875 
876  ARMARX_TRACE;
877 
878  // Slaves inputs (EtherCAT naming convention) are transfered, outputs NOT.
879  // This means, the slave objects can receive updated data but cannot yet send
880  // data to the slaves via PDO
881  rtUpdatePDO();
882 
883  pdoValidity = PDOValidity::OnlyInputs;
884 
885  return ret;
886  }
887 
888  bool
889  Bus::_preOpToOp()
890  {
891  return _preOpToSafeOp() and _safeOpToOp();
892  }
893 
894  bool
895  Bus::_safeOpToInit()
896  {
897  ARMARX_VERBOSE << "Start switching bus from 'SafeOp' to 'Init'";
899  {
900  ARMARX_VERBOSE << "Finish switching bus from 'SafeOp' to 'Init'";
901  };
902 
904 
905  return changeBusState(EtherCATState::init);
906  }
907 
908  bool
909  Bus::_safeOpToPreOp()
910  {
911  ARMARX_VERBOSE << "Start switching bus from 'SafeOp' to 'PreOp'";
913  {
914  ARMARX_VERBOSE << "Finish switching bus from 'SafeOp' to 'PreOp'";
915  };
916 
918 
919  return changeBusState(EtherCATState::preOp);
920  }
921 
922  bool
923  Bus::_safeOpToOp()
924  {
925  ARMARX_VERBOSE << "Start switching bus from 'SafeOp' to 'Op'";
927  {
928  ARMARX_VERBOSE << "Finish switching bus from 'SafeOp' to 'Op'";
929  };
930 
932 
933  // Give the slaves some time to prepare.
934  for (std::unique_ptr<SlaveInterface>& slave : slaves)
935  {
936  slave->prepareForOp();
937  }
938 
939  for (std::unique_ptr<SlaveInterface>& slave : slaves)
940  {
941  slave->finishPreparingForOp();
942  }
943 
944  // Send one valid process data to make outputs in slaves happy
945  rtUpdatePDO();
946 
947  if (changeBusState(EtherCATState::op))
948  {
949  ARMARX_INFO << "All slaves in op!";
950  }
951  else
952  {
953  return false;
954  }
955 
956  pdoValidity = PDOValidity::Both;
957  pdoAccessAvailable = true;
958 
959  ARMARX_TRACE;
960 
961  // Some devices might have additional initialization to do after all slaves
962  // are in op.
963  for (std::shared_ptr<DeviceInterface>& device : devices)
964  {
965  device->postSwitchToOp();
966  }
967 
968  ARMARX_TRACE;
969 
970 
971  //preparing devices to run
972  size_t slaveReadyCounter = 0;
973  while (slaveReadyCounter != slaves.size())
974  {
975  rtUpdatePDO();
976  slaveReadyCounter = 0;
977  std::string missingSlaves;
978  for (std::unique_ptr<SlaveInterface>& slave : slaves)
979  {
980  if (slave->prepareForRun())
981  {
982  slaveReadyCounter++;
983  }
984  else
985  {
986  missingSlaves += slave->getSlaveIdentifier().getName() +
987  " (idx: " + std::to_string(slave->getSlaveNumber()) + "), ";
988  }
989  }
990  ARMARX_INFO << deactivateSpam(1) << "Waiting for "
991  << (slaves.size() - slaveReadyCounter) << "/" << slaves.size()
992  << " slaves to get ready: " << missingSlaves;
993  }
994 
995  std::stringstream slaveInfo;
996  for (std::unique_ptr<SlaveInterface>& slave : slaves)
997  {
998  slaveInfo << "#" << slave->getSlaveNumber() << ": "
999  << slave->getSlaveIdentifier().getName() << "\n";
1000  }
1001  ARMARX_IMPORTANT << "Used slaves: \n" << slaveInfo.str();
1002 
1003  busUpdateLastUpdateTime = armarx::rtNow();
1004 
1005  functionalState = BusFunctionalState::Running;
1006 
1007  return true;
1008  }
1009 
1010  bool
1011  Bus::_opToInit()
1012  {
1013  ARMARX_VERBOSE << "Start switching bus from 'Op' to 'Init'";
1015  {
1016  ARMARX_VERBOSE << "Finish switching bus from 'Op' to 'Init'";
1017  };
1018 
1020 
1021  return changeBusState(EtherCATState::init);
1022  }
1023 
1024  bool
1025  Bus::_opToPreOp()
1026  {
1027  ARMARX_VERBOSE << "Start switching bus from 'Op' to 'PreOp'";
1029  {
1030  ARMARX_VERBOSE << "Finish switching bus from 'Op' to 'PreOp'";
1031  };
1032 
1034 
1035  return changeBusState(EtherCATState::preOp);
1036  }
1037 
1038  bool
1039  Bus::_opToSafeOp()
1040  {
1041  ARMARX_VERBOSE << "Start switching bus from 'Op' to 'SafeOp'";
1043  {
1044  ARMARX_VERBOSE << "Finish switching bus from 'Op' to 'SafeOp'";
1045  };
1046 
1048 
1049 
1050  return changeBusState(EtherCATState::safeOp);
1051  }
1052 
1053  bool
1054  Bus::changeBusState(EtherCATState state)
1055  {
1056  ARMARX_TRACE;
1058  {
1059  busState = readStates();
1060  };
1061 
1062  if (state <= EtherCATState::preOp)
1063  {
1064  pdoValidity = PDOValidity::None;
1065  }
1066 
1067  if (state != EtherCATState::op)
1068  {
1069  pdoAccessAvailable = false;
1070  }
1071 
1072  EtherCATState retState = readStates();
1073  if (retState == state)
1074  {
1075  ARMARX_VERBOSE << "All slaves are already in requested state " << state;
1076  return true;
1077  }
1078  else
1079  {
1080  IceUtil::Time const start = armarx::rtNow();
1081  EtherCATState const retState = changeStateOfBus(state, true);
1082  IceUtil::Time const end = armarx::rtNow();
1083  IceUtil::Time const elapsed = (end - start);
1084 
1085  if (retState != state)
1086  {
1087  ARMARX_INFO << "One or more slaves appear to not have reached the desired state. "
1088  << "If the desired state is lower than the actual one this is fine.";
1089  }
1090 
1091  if (retState < state)
1092  {
1093  readStates();
1094  for (std::uint16_t i = 1; i < ec_slavecount + 1; i++)
1095  {
1096  EtherCATState const actualState = ec_slave[i].state;
1097  if (actualState != state)
1098  {
1099  SLAVE_WARNING(
1100  getSlaveAtIndex(i)->getSlaveIdentifier(),
1101  "Slave did not reach requested state %s after %ld µs (start = "
1102  "%ld µs since epoch, end = %ld µs since epoch). Actual state: %s AL: "
1103  "%s",
1104  state.c_str(),
1105  elapsed.toMicroSeconds(),
1106  start.toMicroSeconds(),
1107  end.toMicroSeconds(),
1108  actualState.c_str(),
1109  ec_ALstatuscode2string(ec_slave[i].ALstatuscode));
1110  }
1111  }
1112 
1113  return false;
1114  }
1115  }
1116 
1117  return true;
1118  }
1119 
1120  bool
1121  Bus::validateBus() const
1122  {
1123  if (slaves.empty())
1124  {
1125  BUS_WARNING(iterationCount, "No slaves have been created. Cannot validate the bus.");
1126  return false;
1127  }
1128 
1129  int w = 0x0000;
1130  int slaveCount = ec_BRD(0x0000, ECT_REG_TYPE, sizeof(w), &w, EC_TIMEOUTSAFE);
1131  if (slaveCount == EC_NOFRAME)
1132  {
1133  BUS_ERROR(iterationCount, "EC_NOFRAME when trying to get slave count.");
1134  return false;
1135  }
1136 
1137  if (static_cast<std::uint16_t>(slaveCount) != slaves.size())
1138  {
1139  BUS_ERROR(iterationCount,
1140  "Could not validate bus! Amount of found slaves is not equal to the previous "
1141  "created slaves. (Found: %u, Expected: %u)",
1142  slaveCount,
1143  slaves.size());
1144  return false;
1145  }
1146 
1147  // If the amount of found slaves is the expected amount, we assume that the bus is fine
1148  // and every slaves is still available.
1149  // It is extremly unlikely, that the physical position of two or more slaves
1150  // on the bus have changed.
1151  return true;
1152  }
1153 
1154  bool
1155  Bus::initSocket()
1156  {
1157  if (socketInitialized)
1158  {
1159  BUS_ERROR(iterationCount, "Socket is already initialized.");
1160  return false;
1161  }
1162 
1163  if (socketFileDescriptor == -1)
1164  {
1165  BUS_WARNING(iterationCount, "socketFileDescriptor is -1 - did you forget to set it?");
1166  return false;
1167  }
1168 
1169  if (!ifname.empty())
1170  {
1171  ARMARX_IMPORTANT << "ec_init(" << ifname << ")";
1172  if (!ec_init(ifname.c_str()))
1173  {
1174  BUS_ERROR(iterationCount,
1175  "Could not init EtherCAT on %s\nExecute as root\n",
1176  ifname.c_str());
1177  return false;
1178  }
1179  }
1180  else if (socketFileDescriptor != -1)
1181  {
1182  ARMARX_INFO << "Using socketFileDescriptor " << socketFileDescriptor
1183  << " to open raw socket";
1184  //initialise SOEM, open socket
1185  if (!ec_init_wsock(socketFileDescriptor))
1186  {
1187  BUS_WARNING(iterationCount,
1188  "No socket connection on %u\nExecute as root\n",
1189  socketFileDescriptor);
1190  return false;
1191  }
1192  }
1193  else
1194  {
1195  BUS_WARNING(iterationCount, "Either socketFileDescriptor or ifname need to be set");
1196  return false;
1197  }
1198 
1199  //We succeed
1200  ARMARX_INFO << "Started SOEM with socketFileDescriptor: " << socketFileDescriptor;
1201 
1202  socketInitialized = true;
1203  return true;
1204  }
1205 
1206  void
1207  Bus::readAllErrorCounters()
1208  {
1209  std::vector<RegisterDataList> registerData;
1210  for (const auto& slave : slaves)
1211  {
1212  registerData.push_back(
1213  RegisterDataList{static_cast<std::uint16_t>(slave->getSlaveIdentifier().slaveIndex),
1214  {
1247  }});
1248  }
1249 
1250 
1251  EtherCATFrameList* frameList =
1253  &registerData);
1254 
1255  int leftRetries = 3;
1256  bool readRegistersSuccessful = false;
1257 
1258  do
1259  {
1260  readRegistersSuccessful = readRegisters(frameList);
1261 
1262  if (not readRegistersSuccessful)
1263  {
1264  leftRetries -= 1;
1265 
1266  if (leftRetries == 0)
1267  {
1268  ARMARX_ERROR << "Repeatedly failed reading error registers. Giving up.";
1269  return;
1270  }
1271  else
1272  {
1273  ARMARX_ERROR << "Failed reading error registers. Left retries: "
1274  << leftRetries;
1275  }
1276  }
1277 
1278  } while (not readRegistersSuccessful);
1279 
1281  &registerData);
1282 
1283  struct ErrorCounters
1284  {
1285  std::uint8_t invalidFrame = 0;
1286  std::uint8_t rxError = 0; // Or physical error count.
1287  std::uint8_t forwardedRxError = 0; // Error counter of a predecessor.
1288  std::uint8_t linkLost = 0;
1289  };
1290 
1291  std::vector<ErrorCounters> counts(4);
1292 
1293  for (const auto& d : registerData)
1294  {
1295  auto copyCounts = [&](datatypes::RegisterEnum type) -> std::uint8_t
1296  { return std::get<datatypes::EtherCATDataType::UNSIGNED8>(d.registerData.at(type)); };
1297 
1298  using RE = datatypes::RegisterEnum;
1299 
1300  counts[0].invalidFrame = copyCounts(RE::FRAME_ERROR_COUNTER_PORT_0);
1301  counts[0].rxError = copyCounts(RE::PHYSICAL_ERROR_COUNTER_PORT_0);
1302  counts[0].forwardedRxError = copyCounts(RE::PREVIOUS_ERROR_COUNTER_PORT_0);
1303  counts[0].linkLost = copyCounts(RE::LOST_LINK_COUNTER_PORT_0);
1304 
1305  counts[1].invalidFrame = copyCounts(RE::FRAME_ERROR_COUNTER_PORT_1);
1306  counts[1].rxError = copyCounts(RE::PHYSICAL_ERROR_COUNTER_PORT_1);
1307  counts[1].forwardedRxError = copyCounts(RE::PREVIOUS_ERROR_COUNTER_PORT_1);
1308  counts[1].linkLost = copyCounts(RE::LOST_LINK_COUNTER_PORT_1);
1309 
1310  counts[2].invalidFrame = copyCounts(RE::FRAME_ERROR_COUNTER_PORT_2);
1311  counts[2].rxError = copyCounts(RE::PHYSICAL_ERROR_COUNTER_PORT_2);
1312  counts[2].forwardedRxError = copyCounts(RE::PREVIOUS_ERROR_COUNTER_PORT_2);
1313  counts[2].linkLost = copyCounts(RE::LOST_LINK_COUNTER_PORT_2);
1314 
1315  counts[3].invalidFrame = copyCounts(RE::FRAME_ERROR_COUNTER_PORT_3);
1316  counts[3].rxError = copyCounts(RE::PHYSICAL_ERROR_COUNTER_PORT_3);
1317  counts[3].forwardedRxError = copyCounts(RE::PREVIOUS_ERROR_COUNTER_PORT_3);
1318  counts[3].linkLost = copyCounts(RE::LOST_LINK_COUNTER_PORT_3);
1319 
1320  auto slave = getSlaveAtIndex(d.slaveIndex);
1321  for (unsigned int i = 0; i < 4; i++)
1322  {
1323  if (counts[i].invalidFrame > 0 || counts[i].rxError > 0 ||
1324  counts[i].forwardedRxError > 0 || counts[i].linkLost > 0)
1325  {
1326  SLAVE_ERROR(slave->getSlaveIdentifier(),
1327  "Error counters on port %u\n"
1328  " - invalid frame: \t\t%u\n"
1329  " - rx error: \t\t%u\n"
1330  " - forwarded rx error: \t%u\n"
1331  " - link lost: \t\t%u",
1332  i,
1333  counts[i].invalidFrame,
1334  counts[i].rxError,
1335  counts[i].forwardedRxError,
1336  counts[i].linkLost);
1337  }
1338  }
1339  }
1340  }
1341 
1342  EtherCATState
1344  {
1345  return busState;
1346  }
1347 
1350  {
1351  return functionalState;
1352  }
1353 
1354  void
1356  {
1357  slaveFactories.push_back(factory);
1358  }
1359 
1360  void
1361  Bus::registerDeviceFactory(const std::string& name, const DeviceFactory& factory)
1362  {
1363  deviceFactories[name] = factory;
1364  }
1365 
1366  void
1368  {
1370  parser.parse();
1371  this->hwconfig = parser.getHardwareConfig();
1372  }
1373 
1374  std::vector<std::experimental::observer_ptr<SlaveInterface>>
1376  {
1377  return slavesView;
1378  std::vector<SlaveInterface*> slaves_raw;
1379  // std::transform(slaves.begin(),
1380  // slaves.end(),
1381  // std::back_inserter(slaves_raw),
1382  // [](auto& slave) { return slave.get(); });
1383  // return slaves_raw;
1384  }
1385 
1386  const std::vector<std::shared_ptr<DeviceInterface>>&
1388  {
1389  return devices;
1390  }
1391 
1392  Bus::~Bus()
1393  {
1394  }
1395 
1397  Bus::getSlaveAtIndex(std::uint16_t slaveIndex) const
1398  {
1399  for (const std::unique_ptr<SlaveInterface>& slave : slaves)
1400  {
1401  if (slave->getSlaveNumber() == slaveIndex)
1402  {
1403  return std::experimental::make_observer(slave.get());
1404  }
1405  }
1406 
1407  return nullptr;
1408  }
1409 
1410  bool
1412  {
1413  bool found = false;
1414  for (const std::unique_ptr<SlaveInterface>& slave : slaves)
1415  {
1416  if (!errorHandler.isSlaveLostOrDuringReinitialization(slave.get()) &&
1417  slave->isEmergencyStopActive())
1418  {
1419  // dont break so that isEmergencyStopActive executed for each operational slave
1420  found = true;
1421  }
1422  }
1423  return found;
1424  }
1425 
1426  bool
1428  {
1429  BUS_TIMING_START(bus_rtHasError)
1430  const bool hasError = errorHandler.hasError();
1431  BUS_TIMING_CEND(bus_rtHasError, iterationCount, 0.5)
1432  return hasError;
1433  }
1434 
1435  void
1437  {
1438  this->robot = robot;
1439  }
1440 
1441  void
1442  Bus::configureErrorCountersReading(bool enable, unsigned int periodInMS)
1443  {
1444  if (!enable)
1445  {
1446  errorRegisterReadingScheduler.reset();
1447  }
1448  else
1449  {
1450  errorRegisterReadingScheduler = std::make_unique<SlaveRegisterReadingScheduler>(
1451  static_cast<std::uint16_t>(slaves.size()),
1452  periodInMS,
1453  std::vector<datatypes::RegisterEnum>{
1454  datatypes::RegisterEnum::LOST_LINK_COUNTER_PORT_0,
1455  datatypes::RegisterEnum::LOST_LINK_COUNTER_PORT_1,
1456  datatypes::RegisterEnum::FRAME_ERROR_COUNTER_PORT_0,
1457  datatypes::RegisterEnum::FRAME_ERROR_COUNTER_PORT_1,
1458  datatypes::RegisterEnum::PHYSICAL_ERROR_COUNTER_PORT_0,
1459  datatypes::RegisterEnum::PHYSICAL_ERROR_COUNTER_PORT_1,
1460  datatypes::RegisterEnum::PREVIOUS_ERROR_COUNTER_PORT_0,
1461  datatypes::RegisterEnum::PREVIOUS_ERROR_COUNTER_PORT_1});
1462  }
1463  }
1464 
1465  std::uint64_t
1467  {
1468  return iterationCount;
1469  }
1470 
1471 } // namespace armarx::control::ethercat
armarx::control::ethercat::Bus::switchBusToPreOp
bool switchBusToPreOp()
Definition: Bus.cpp:660
ARMARX_VERBOSE
#define ARMARX_VERBOSE
Definition: Logging.h:180
armarx::control::ethercat::BusErrorHandler::rtHandleSlaveErrors
void rtHandleSlaveErrors()
Definition: BusErrorHandler.cpp:44
SLAVE_WARNING
#define SLAVE_WARNING(sid,...)
Definition: ErrorReporting.h:303
armarx::control::ethercat::BusIO::rtUpdatePDO
void rtUpdatePDO()
Updates the PDO of all slaves.
Definition: BusIO.cpp:113
armarx::control::ethercat::Bus::setSocketFileDescriptor
void setSocketFileDescriptor(int socketFileDescriptor)
Definition: Bus.cpp:453
armarx::aron::ret
ReaderT::InputType T & ret
Definition: rw.h:21
armarx::control::ethercat::BusIO::changeStateOfBus
EtherCATState changeStateOfBus(EtherCATState state, bool validate=true)
Definition: BusIO.cpp:411
armarx::control::ethercat::BusErrorHandler::rtHandleBusErrors
void rtHandleBusErrors()
Definition: BusErrorHandler.cpp:37
ARMARX_IMPORTANT
#define ARMARX_IMPORTANT
Definition: Logging.h:183
armarx::control::ethercat::PDOValidity::None
@ None
armarx::control::ethercat::BusIO::ioMap
IOMap ioMap
Definition: BusIO.h:211
armarx::control::ethercat::EtherCATState
This class is a wrapper around an enum containing the different EtherCAT states.
Definition: EtherCATState.h:25
armarx::control::ethercat::Bus::configureErrorCountersReading
void configureErrorCountersReading(bool enable, unsigned int periodInMS)
configureErrorCountersReading configure stuff
Definition: Bus.cpp:1442
OnScopeExit.h
BUS_WARNING
#define BUS_WARNING(bin,...)
Definition: ErrorReporting.h:274
index
uint8_t index
Definition: EtherCATFrame.h:59
BUS_ERROR
#define BUS_ERROR(bin,...)
Definition: ErrorReporting.h:277
RtTiming.h
armarx::control::ethercat::BusFunctionalState
BusFunctionalState
Definition: Bus.h:40
ARMARX_CHECK_NOT_NULL
#define ARMARX_CHECK_NOT_NULL(ptr)
This macro evaluates whether ptr is not null and if it turns out to be false it will throw an Express...
Definition: ExpressionException.h:206
armarx::control::ethercat::ESIHandler::readESIBinaryBlob
std::optional< std::vector< std::byte > > readESIBinaryBlob(std::uint16_t slaveIndex, std::uint16_t startAddress, std::uint16_t endAddress) const
Definition: ESI.cpp:16
armarx::control::ethercat::Bus::rtGetEtherCATState
EtherCATState rtGetEtherCATState() const
Definition: Bus.cpp:1343
armarx::control::ethercat::Bus::logErrorsToFile
std::string logErrorsToFile(std::filesystem::path path, unsigned int lastSeconds)
Definition: Bus.cpp:36
armarx::control::ethercat::BusErrorHandler::init
void init(int wkc)
Definition: BusErrorHandler.cpp:94
IceStorm::Parser::parse
int parse(FILE *, bool)
armarx::control::ethercat::DeviceInterface::TryAssignResult::unknown
@ unknown
Used if the slave is unknown to this device. (Expected)
std::experimental::fundamentals_v2::make_observer
observer_ptr< _Tp > make_observer(_Tp *__p) noexcept
armarx::control::ethercat::BusIO::pdoAccessAvailable
bool pdoAccessAvailable
Definition: BusIO.h:203
armarx::control::ethercat::datatypes::RegisterEnum
RegisterEnum
The RegisterEnum enum encodes the slave registers that every slave offers.
Definition: SlaveRegisters.h:37
armarx::control::ethercat::datatypes::RegisterEnum::PREVIOUS_ERROR_COUNTER_PORT_0
@ PREVIOUS_ERROR_COUNTER_PORT_0
BUS_INFO
#define BUS_INFO(bin,...)
Definition: ErrorReporting.h:271
ARMARX_ON_SCOPE_EXIT
#define ARMARX_ON_SCOPE_EXIT
Executes given code when the enclosing scope is left.
Definition: OnScopeExit.h:112
armarx::control::ethercat::Bus::rtHasError
bool rtHasError() const
Definition: Bus.cpp:1427
message
message(STATUS "Boost-Library-Dir: " "${Boost_LIBRARY_DIRS}") message(STATUS "Boost-LIBRARIES
Definition: CMakeLists.txt:8
armarx::control::ethercat::BusFunctionalState::Reinitializing
@ Reinitializing
armarx::control::ethercat::Bus::switchBusToInit
bool switchBusToInit()
Definition: Bus.cpp:641
armarx::control::ethercat::SlaveRegisterReadingScheduler::updateRegisterDataFromEtherCATFrameList
static void updateRegisterDataFromEtherCATFrameList(const EtherCATFrameList *frameList, std::vector< RegisterDataList > *requestedRegisters)
Definition: SlaveRegisterReadingScheduler.cpp:38
armarx::control::ethercat::BusErrorHandler::hasError
bool hasError() const
Definition: BusErrorHandler.cpp:76
armarx::control::ethercat::reporting::Reporting::dumpToFile
std::optional< std::filesystem::path > dumpToFile(std::filesystem::path file, std::uint64_t lastNSeconds=1000000)
Definition: ErrorReporting.cpp:120
std::experimental::fundamentals_v2::observer_ptr
Definition: ManagedIceObject.h:53
armarx::control::ethercat::BusIO::readStates
EtherCATState readStates()
Definition: BusIO.cpp:419
armarx::MultiNodeRapidXMLReader
Definition: MultiNodeRapidXMLReader.h:34
armarx::control::ethercat::datatypes::RegisterEnum::LOST_LINK_COUNTER_PORT_2
@ LOST_LINK_COUNTER_PORT_2
armarx::control::ethercat::Bus::switchBusToSafeOp
bool switchBusToSafeOp()
Definition: Bus.cpp:680
Bus.h
armarx::control::ethercat::datatypes::RegisterEnum::PHYSICAL_ERROR_COUNTER_PORT_3
@ PHYSICAL_ERROR_COUNTER_PORT_3
armarx::control::ethercat::ESI_Header_StartAdress
constexpr std::uint16_t ESI_Header_StartAdress
Definition: ESI.h:35
armarx::control::ethercat::DeviceInterface::TryAssignResult::assigned
@ assigned
Used if the slave was adpoted by this device. (Expected)
ARMARX_TRACE
#define ARMARX_TRACE
Definition: trace.h:69
armarx::control::ethercat::Bus
Brief description of class Bus.
Definition: Bus.h:55
armarx::control::ethercat::datatypes::RegisterEnum::FRAME_ERROR_COUNTER_PORT_1
@ FRAME_ERROR_COUNTER_PORT_1
armarx::control::ethercat::DeviceInterface::AllAssignedResult::slavesMissing
@ slavesMissing
Used if slaves are missing. (Error)
armarx::control::ethercat::datatypes::RegisterEnum::LOST_LINK_COUNTER_PORT_1
@ LOST_LINK_COUNTER_PORT_1
BUS_ERROR_LOCAL
#define BUS_ERROR_LOCAL(reporter, bin,...)
Definition: ErrorReporting.h:286
armarx::control::ethercat::ESI_Header_EndAdress
constexpr std::uint16_t ESI_Header_EndAdress
Definition: ESI.h:36
armarx::control::hardware_config::HardwareConfig::checkAllItemsRead
bool checkAllItemsRead(std::vector< std::string > &errors) const
This method is called when the config is completely read form the HardwareConfig file.
Definition: HardwareConfig.cpp:42
DeviceInterface.h
armarx::control::ethercat::Bus::SlaveFactory
std::function< std::unique_ptr< SlaveInterface >(SlaveIdentifier sid)> SlaveFactory
Definition: Bus.h:130
armarx::control::ethercat::datatypes::RegisterEnumTypeContainer
std::variant< EtherCATDataType::INTEGER8, EtherCATDataType::INTEGER16, EtherCATDataType::INTEGER32, EtherCATDataType::INTEGER64, EtherCATDataType::UNSIGNED8, EtherCATDataType::UNSIGNED16, EtherCATDataType::UNSIGNED32, EtherCATDataType::UNSIGNED64 > RegisterEnumTypeContainer
Definition: SlaveRegisters.h:160
armarx::control::ethercat::Bus::setRobot
void setRobot(const VirtualRobot::RobotPtr &robot)
Definition: Bus.cpp:1436
armarx::control::ethercat::reporting::Reporting::getErrorReporting
static Reporting & getErrorReporting()
Definition: ErrorReporting.cpp:82
armarx::control::ethercat::datatypes::RegisterEnum::PREVIOUS_ERROR_COUNTER_PORT_2
@ PREVIOUS_ERROR_COUNTER_PORT_2
armarx::flush
const LogSender::manipulator flush
Definition: LogSender.h:251
armarx::control::ethercat::Bus::rtGetFunctionalState
BusFunctionalState rtGetFunctionalState() const
Definition: Bus.cpp:1349
data
uint8_t data[1]
Definition: EtherCATFrame.h:68
armarx::control::hardware_config::ConfigParser
Definition: ConfigParser.h:19
ARMARX_DEBUG
#define ARMARX_DEBUG
Definition: Logging.h:177
BUS_TIMING_CEND
#define BUS_TIMING_CEND(name, bin, thresholdMs)
Definition: Timing.h:181
DefaultRapidXmlReader.h
armarx::control::ethercat::PDOValidity
PDOValidity
Definition: Bus.h:33
armarx::control::ethercat
Definition: Bus.cpp:24
SLAVE_ERROR
#define SLAVE_ERROR(sid,...)
Definition: ErrorReporting.h:306
armarx::control::ethercat::EtherCATState::preOp
@ preOp
Pre-operational state.
Definition: EtherCATState.h:40
armarx::control::ethercat::reporting::Reporting::getErrorReporter
Reporter getErrorReporter()
Definition: ErrorReporting.cpp:89
armarx::control::ethercat::Bus::getBus
static Bus & getBus()
This returns the one and only Bus object.
Definition: Bus.cpp:29
armarx::control::ethercat::datatypes::RegisterEnum::PREVIOUS_ERROR_COUNTER_PORT_1
@ PREVIOUS_ERROR_COUNTER_PORT_1
armarx::control::ethercat::Bus::getSlaves
std::vector< std::experimental::observer_ptr< SlaveInterface > > getSlaves() const
Returns all identifiied slaves on the bus.
Definition: Bus.cpp:1375
armarx::control::ethercat::Bus::registerSlaveFactory
void registerSlaveFactory(const SlaveFactory &factory)
Definition: Bus.cpp:1355
armarx::control::ethercat::SlaveRegisterReadingScheduler::createEtherCATFrameListFromRegisterDataList
static EtherCATFrameList * createEtherCATFrameListFromRegisterDataList(const std::vector< RegisterDataList > *requestedRegisters)
Definition: SlaveRegisterReadingScheduler.cpp:20
SlaveInterface.h
armarx::control::ethercat::datatypes::RegisterEnum::LOST_LINK_COUNTER_PORT_3
@ LOST_LINK_COUNTER_PORT_3
ARMARX_ERROR
#define ARMARX_ERROR
Definition: Logging.h:189
SlaveErrorRegistersDevice.h
armarx::control::ethercat::DeviceInterface::TryAssignResult::alreadyAssigned
@ alreadyAssigned
Used if the slave was already adopted before. (Error)
armarx::armem::Time
armarx::core::time::DateTime Time
Definition: forward_declarations.h:13
armarx::to_string
const std::string & to_string(const std::string &s)
Definition: StringHelpers.h:40
Timing.h
armarx::Logging::getEffectiveLoggingLevel
MessageTypeT getEffectiveLoggingLevel() const
Definition: Logging.cpp:117
ErrorReporting.h
armarx::control::ethercat::datatypes::RegisterEnum::PHYSICAL_ERROR_COUNTER_PORT_0
@ PHYSICAL_ERROR_COUNTER_PORT_0
armarx::control::ethercat::EtherCATState::safeOp
@ safeOp
Safe-operational state.
Definition: EtherCATState.h:46
ExpressionException.h
armarx::control::ethercat::datatypes::RegisterEnum::FRAME_ERROR_COUNTER_PORT_2
@ FRAME_ERROR_COUNTER_PORT_2
armarx::control::ethercat::BusIO::sdoAccessAvailable
bool sdoAccessAvailable
Definition: BusIO.h:202
SlaveRegisterReadingScheduler.h
armarx::control::ethercat::DeviceInterface::AllAssignedResult::ok
@ ok
Used if assignments were successful. (Expected)
armarx::control::hardware_config::HardwareConfig::deviceConfigs
std::map< std::string, std::shared_ptr< DeviceConfig > > deviceConfigs
Definition: HardwareConfig.h:59
BUS_FATAL_AND_THROW
#define BUS_FATAL_AND_THROW(bin,...)
Definition: ErrorReporting.h:280
armarx::control::ethercat::BusFunctionalState::Running
@ Running
armarx::control::ethercat::BusErrorHandler::isReinitializationActive
bool isReinitializationActive() const
Definition: BusErrorHandler.cpp:82
IceStorm::parser
Parser * parser
Definition: Parser.cpp:33
armarx::control::ethercat::Bus::shutdown
void shutdown()
Shuts the bus down by executing the shutdown-hook for each slave and afterwards switch all slaves to ...
Definition: Bus.cpp:584
armarx::control::ethercat::DeviceInterface::AllAssignedResult
AllAssignedResult
Definition: DeviceInterface.h:107
ARMARX_INFO
#define ARMARX_INFO
Definition: Logging.h:174
armarx::control::ethercat::Bus::registerDeviceFactory
void registerDeviceFactory(const std::string &name, const DeviceFactory &factory)
Definition: Bus.cpp:1361
armarx::control::ethercat::BusFunctionalState::Shutdown
@ Shutdown
armarx::control::ethercat::PDOValidity::Both
@ Both
armarx::control::ethercat::EtherCATState::init
@ init
Initial state after switch a EtherCAT slave on.
Definition: EtherCATState.h:37
VAROUT
#define VAROUT(x)
Definition: StringHelpers.h:182
BUS_WARNING_LOCAL
#define BUS_WARNING_LOCAL(reporter, bin,...)
Definition: ErrorReporting.h:284
armarx::control::ethercat::Bus::switchBusToOp
bool switchBusToOp()
Definition: Bus.cpp:700
armarx::transform
auto transform(const Container< InputT, Alloc > &in, OutputT(*func)(InputT const &)) -> Container< OutputT, typename std::allocator_traits< Alloc >::template rebind_alloc< OutputT > >
Convenience function (with less typing) to transform a container of type InputT into the same contain...
Definition: algorithm.h:315
armarx::control::ethercat::Bus::setIfName
void setIfName(const std::string &ifname)
Definition: Bus.cpp:459
armarx::control::ethercat::datatypes::RegisterEnum::PHYSICAL_ERROR_COUNTER_PORT_1
@ PHYSICAL_ERROR_COUNTER_PORT_1
armarx::control::ethercat::Bus::getDevices
const std::vector< std::shared_ptr< DeviceInterface > > & getDevices() const
Returns all initialized devices.
Definition: Bus.cpp:1387
armarx::control::ethercat::Bus::getSlaveAtIndex
std::experimental::observer_ptr< SlaveInterface > getSlaveAtIndex(std::uint16_t slaveIndex) const
Definition: Bus.cpp:1397
armarx::control::ethercat::PDOValidity::OnlyInputs
@ OnlyInputs
SLAVE_ERROR_LOCAL
#define SLAVE_ERROR_LOCAL(reporter, sid,...)
Definition: ErrorReporting.h:315
RapidXmlReader.h
armarx::control::ethercat::BusErrorHandler::isSlaveLostOrDuringReinitialization
bool isSlaveLostOrDuringReinitialization(SlaveInterface *slave) const
Definition: BusErrorHandler.cpp:88
armarx::control::ethercat::Bus::DeviceFactory
std::function< std::shared_ptr< DeviceInterface >(hardware_config::DeviceConfig &config, const VirtualRobot::RobotPtr &robot)> DeviceFactory
Definition: Bus.h:136
armarx::control::ethercat::Bus::rtUpdateBus
bool rtUpdateBus(bool doErrorHandling=true, size_t iterationCount=0)
Updates all information on the bus, so all commands will be send to the Bus and all sensor and other ...
Definition: Bus.cpp:465
armarx::Logging::deactivateSpam
SpamFilterDataPtr deactivateSpam(float deactivationDurationSec=10.0f, const std::string &identifier="", bool deactivate=true) const
disables the logging for the current line for the given amount of seconds.
Definition: Logging.cpp:92
ControlThreadOutputBuffer.h
armarx::control::ethercat::DeviceInterface::TryAssignResult
TryAssignResult
Definition: DeviceInterface.h:86
armarx::Logging::setTag
void setTag(const LogTag &tag)
Definition: Logging.cpp:55
armarx::control::ethercat::Bus::setAndParseHardwareConfig
void setAndParseHardwareConfig(const MultiNodeRapidXMLReader &hwconfig)
Definition: Bus.cpp:1367
ARMARX_CHECK_EQUAL
#define ARMARX_CHECK_EQUAL(lhs, rhs)
This macro evaluates whether lhs is equal (==) rhs and if it turns out to be false it will throw an E...
Definition: ExpressionException.h:130
armarx::handleExceptions
void handleExceptions()
Definition: Exception.cpp:141
armarx::control::ethercat::EtherCATState::op
@ op
Operational state.
Definition: EtherCATState.h:49
BUS_TIMING_START
#define BUS_TIMING_START(name)
Definition: Timing.h:163
armarx::control::ethercat::Bus::rtIsEmergencyStopActive
bool rtIsEmergencyStopActive() const
Definition: Bus.cpp:1411
armarx::control::ethercat::Bus::getPDOValidity
PDOValidity getPDOValidity() const
Definition: Bus.cpp:635
armarx::control::ethercat::datatypes::RegisterEnum::PHYSICAL_ERROR_COUNTER_PORT_2
@ PHYSICAL_ERROR_COUNTER_PORT_2
armarx::control::ethercat::BusFunctionalState::EmergencyStop
@ EmergencyStop
armarx::control::ethercat::Bus::getIterationCount
std::uint64_t getIterationCount() const
Definition: Bus.cpp:1466
armarx::control::ethercat::datatypes::RegisterEnum::FRAME_ERROR_COUNTER_PORT_0
@ FRAME_ERROR_COUNTER_PORT_0
armarx::control::ethercat::datatypes::RegisterEnum::PREVIOUS_ERROR_COUNTER_PORT_3
@ PREVIOUS_ERROR_COUNTER_PORT_3
armarx::control::ethercat::datatypes::RegisterEnum::FRAME_ERROR_COUNTER_PORT_3
@ FRAME_ERROR_COUNTER_PORT_3
armarx::control::ethercat::BusIO::readRegisters
bool readRegisters(std::vector< RegisterDataList > &registerData)
Definition: BusIO.cpp:433
VirtualRobot::RobotPtr
std::shared_ptr< class Robot > RobotPtr
Definition: Bus.h:18
armarx::control::ethercat::datatypes::RegisterEnum::LOST_LINK_COUNTER_PORT_0
@ LOST_LINK_COUNTER_PORT_0
armarx::rtNow
IceUtil::Time rtNow()
Definition: RtTiming.h:40