26 static constexpr std::uint8_t DEFAULT_ECAT_GROUP = 0;
41 return ret.value().string();
49 Bus::Bus() : busState(
EtherCATState::init), currentGroup(DEFAULT_ECAT_GROUP), errorHandler(this)
61 for (std::unique_ptr<SlaveInterface>& slave : slaves)
63 ARMARX_INFO <<
"Checking mapping for slave " << slave->getSlaveNumber()
64 <<
" name: " << slave->getSlaveIdentifier().getName();
65 if (!slave->hasPDOMapping())
67 ARMARX_INFO <<
"No mapping needed for " << slave->getSlaveIdentifier().getName();
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)
76 "There is a nullpointer in the mapping of slave %d",
77 slave->getSlaveNumber());
84 <<
reinterpret_cast<long>(
85 ec_slave[slave->getSlaveNumber()].outputs)
88 <<
reinterpret_cast<long>(ec_slave[slave->getSlaveNumber()].inputs)
91 <<
static_cast<long>(ec_slave[slave->getSlaveNumber()].inputs -
94 ARMARX_IMPORTANT <<
"-------------------------------------------------------";
97 auto ec_errorToString = [](ec_errort
const& error) -> std::string
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"
109 ec_errort error_type;
110 while (ec_poperror(&error_type))
113 iterationCount,
"SOEM error: %s", ec_errorToString(error_type).c_str());
120 slave->setInputPDO(ec_slave[slave->getSlaveNumber()].outputs);
121 slave->setOutputPDO(ec_slave[slave->getSlaveNumber()].inputs);
131 auto printAdditionalInformation =
132 [&](
const std::string info,
133 const std::vector<std::pair<std::string, std::string>> reasonAndSolutions)
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)
141 ss <<
"- " << reason <<
"\n";
142 ss <<
"(" << solution <<
")\n";
154 for (std::uint16_t currentSlaveIndex = 1; currentSlaveIndex <= ec_slavecount;
159 auto createSlaveIdentifierFromESI =
160 [&](std::uint16_t slaveIndex) -> std::optional<SlaveIdentifier>
162 auto esiHeaderBinary =
164 if (esiHeaderBinary.has_value())
167 ESIHeader header = p.parseHeader(esiHeaderBinary.value());
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);
183 auto maybeSid = createSlaveIdentifierFromESI(currentSlaveIndex);
184 if (!maybeSid.has_value())
188 "Failed to create slaveIdentifier from ESI for slave at index %d",
192 SlaveIdentifier slaveIdentifier = maybeSid.value();
197 std::string name =
"";
202 BUS_INFO(iterationCount,
"Number of slaveFactories is %d", slaveFactories.size());
205 std::unique_ptr<SlaveInterface> slave = factory(slaveIdentifier);
209 if (not foundSlaveInfo.found)
212 << slave->getSlaveIdentifier().toString() <<
".";
214 foundSlaveInfo.name = slave->getSlaveIdentifier().getName();
216 slaves.push_back(std::move(slave));
217 foundSlaveInfo.found =
true;
223 "Slave already constructed as an `%s`, "
224 "but can also be constructed as an `%s`.",
225 foundSlaveInfo.name.c_str(),
226 slave->getSlaveIdentifier().getNameAsCStr());
231 catch (LocalException& e)
235 "Error during slave creation; Reason: %s",
236 e.getReason().c_str());
240 if (not foundSlaveInfo.found)
243 reporter, slaveIdentifier,
"Could not find a corresponding factory");
249 if (reporter.hasErrors())
251 printAdditionalInformation(
252 "One or more slave objects could not be created from the physical slaves found on "
254 {{
"No factory is known for a found slave",
255 "The corresponding factory must be registered in the specialization of the "
256 "RTUnit by calling bus.registerSlaveFactory(...) in its constructor"},
257 {
"The found slave has different ESI informations (vendorID, productCode and "
258 "serialNumber) than expected by all registered slave factories",
259 "reflash the EEPROM with correct data or adapt the function "
260 "isSlaveIdentifierAccepted() in the corresponding slave class"},
261 {
"Corrupted ESI information in the EEPROM of the slave",
262 "Reflash the EEPROM with correct data"}});
266 BUS_INFO(iterationCount,
"Number of slaves pushed into slavesView is %d", slaves.size());
270 std::back_inserter(slavesView),
271 [](
auto& slave) { return std::experimental::make_observer(slave.get()); });
281 const std::string deviceClassName = deviceConfig->getType();
282 const std::string deviceInstanceName = deviceConfig->getName();
283 ARMARX_INFO <<
"Handling: " << deviceClassName <<
" name: " << deviceInstanceName
286 if (deviceFactories.find(deviceClassName) != deviceFactories.end())
288 auto factory = deviceFactories.at(deviceClassName);
290 <<
"Robot is null! You must set the robot via Bus::setRobot()!";
292 std::shared_ptr<DeviceInterface> device = factory(*deviceConfig, robot);
297 ARMARX_INFO <<
"Created device " << device->getDeviceName() <<
" ("
298 << device->getClassName() <<
").";
299 devices.push_back(std::move(device));
305 "Could not create device instance '%s' of class '%s'",
306 deviceInstanceName.c_str(),
307 deviceClassName.c_str());
314 "No device factory found for xml-node `%s` with name `%s` in "
316 deviceClassName.c_str(),
317 deviceInstanceName.c_str());
320 catch (LocalException& e)
324 "Exception during device creation. Device class: %s; Reason: %s",
325 deviceConfig->getName().c_str(),
326 e.getReason().c_str());
335 ARMARX_VERBOSE <<
"Checking if hardware config does not contain unnecessary items.";
336 std::vector<std::string> errorsList;
341 for (
auto&
message : errorsList)
346 throw std::runtime_error(
347 "The hardware_config did not match the code for the devices, see log");
352 if (reporter.hasErrors())
354 printAdditionalInformation(
355 "One or more devices could not created from the provided HardwareConfig.",
356 {{
"No factory known for device description",
357 "The corresponding device factory must be registered in the specialization of "
358 "the RTUnit by calling bus.registerDeviceFactory(...) in its constructor. It is "
359 "important that the first parameter fits exactly the name used in the "
361 {
"Exception during the construction of the device",
362 "Make sure that there are no programming errors in the constructor of the "
371 for (std::unique_ptr<SlaveInterface>& slave : slaves)
375 bool slaveAssigned =
false;
376 for (std::shared_ptr<DeviceInterface>& device : devices)
382 slaveAssigned =
true;
383 slave->setParentDeviceName(device->getDeviceName());
385 << slave->getSlaveIdentifier().toMinimalString()
386 <<
" to device " << device->getDeviceName() <<
".";
393 "Tried to assign ambiguous slave with name `%s` (idx: "
394 "%u) again to device (%s) with name `%s`.",
395 slave->getSlaveIdentifier().getNameAsCStr(),
396 slave->getSlaveIdentifier().slaveIndex,
397 device->getClassName().c_str(),
398 device->rtGetDeviceName());
403 if (not slaveAssigned && slave->hasPDOMapping())
407 "Could not find a device to assign following slave to:\n%s",
408 slave->getSlaveIdentifier().toString().c_str());
415 if (reporter.hasErrors())
417 printAdditionalInformation(
418 "Not all found slaves could not be assigned to devices",
419 {{
"Unexpected slaves are part of the bus",
420 "Disconnect slaves that have not been described in the HardwareConfig from the "
421 "bus or add additional entries to the HardwareConfig to map the whole bus"},
422 {
"SlaveIdentifier in the HardwareConfig do not match the actual slaves",
423 "Make sure, that the slave identifiers for all salves in the HardwareConfig are "
424 "correct. This includes vendorID, productCode and serialNumer."}});
428 ARMARX_INFO <<
"Sanity checking that devices have all needed slaves assigned.";
429 for (std::shared_ptr<DeviceInterface>& device : devices)
441 "%s) reports not all slaves valid. Maybe a slave is missing on the bus",
442 device->getClassName().c_str(),
443 device->rtGetDeviceName());
448 for (
auto& slave : slaves)
451 slave->setErrorRegistersDevice(std::make_shared<SlaveErrorRegistersDevice>(
452 "SlaveIdx_" +
std::to_string(slave->getSlaveIdentifier().slaveIndex) +
"." +
453 slave->getSlaveIdentifier().getName(),
454 slave->getSlaveIdentifier().slaveIndex));
464 this->socketFileDescriptor = socketFileDescriptor;
470 this->ifname = ifname;
477 if (iterationCount != iteration - 1)
481 "Updating the bus has been skipped. The number of the last rt-thread iteration the "
482 "bus has been updated was %d and the current iteration is %d.",
486 iterationCount = iteration;
490 "UpdateBus can not be executed because the bus is not in op-mode");
497 BUS_TIMING_CEND(bus_rtUpdateBus_isEmergencyStopActive, iterationCount, 0.3);
501 if ((not emergencyStopActive and emergencyStopWasActivated))
503 bool recovered =
true;
504 for (std::unique_ptr<SlaveInterface>& slave : slaves)
506 recovered &= slave->recoverFromEmergencyStop();
510 BUS_WARNING(iterationCount,
"Recovered from STO (Safe Torque Off)");
511 emergencyStopWasActivated =
false;
514 else if (emergencyStopActive and not emergencyStopWasActivated)
516 BUS_WARNING(iterationCount,
"STO (Safe Torque Off) active");
517 emergencyStopWasActivated = emergencyStopActive;
519 BUS_TIMING_CEND(bus_rtUpdateBus_recoverFromEmergencyStop, iterationCount, 0.3)
520 const auto delay = (
armarx::rtNow() - busUpdateLastUpdateTime);
521 if (delay.toMilliSecondsDouble() > 40)
524 "Update bus was not called for a long time: %ld ms",
525 delay.toMilliSeconds());
536 for (std::unique_ptr<SlaveInterface>& slave : slaves)
545 if (!emergencyStopActive)
549 BUS_TIMING_CEND(bus_rtUpdateBus_slaveErrorHandling, iterationCount, 0.3)
558 if (!errorHandler.
hasError() && errorRegisterReadingScheduler)
560 if (errorRegisterReadingScheduler->allRegistersUpdated())
562 auto data_ref = errorRegisterReadingScheduler->getRegisterData();
563 for (
const auto&
data : data_ref)
569 errorRegisterReadingScheduler->startReadingNextRegisters();
571 BUS_TIMING_CEND(bus_rtUpdateBus_updateErrorRegisters, iterationCount, 0.3)
579 else if (emergencyStopActive)
587 BUS_TIMING_CEND(bus_rtUpdateBus_updateFunctionalState, iterationCount, 0.3)
597 BUS_WARNING(iterationCount,
"Bus is already shutdown");
602 readAllErrorCounters();
613 for (std::unique_ptr<SlaveInterface>& slave : slaves)
616 <<
"shutting down slave "
617 << slave->getSlaveIdentifier().getName() <<
" ("
618 << slave->getSlaveNumber() <<
"/" << slaves.size() <<
")";
619 found |= !slave->shutdown();
636 socketInitialized =
false;
652 static const std::map<
EtherCATState, std::function<bool(
Bus*)>> transitions = {
659 BUS_WARNING(b->iterationCount,
"Bus is already in init");
665 return transitions.at(busState)(
this);
671 static const std::map<
EtherCATState, std::function<bool(
Bus*)>> transitions = {
678 BUS_WARNING(b->iterationCount,
"Bus is already in preOp");
685 return transitions.at(busState)(
this);
691 static const std::map<
EtherCATState, std::function<bool(
Bus*)>> transitions = {
698 BUS_WARNING(b->iterationCount,
"Bus is already in safeOp");
705 return transitions.at(busState)(
this);
711 static const std::map<
EtherCATState, std::function<bool(
Bus*)>> transitions = {
718 BUS_WARNING(b->iterationCount,
"Bus is already in op");
722 return transitions.at(busState)(
this);
736 if (!socketInitialized && !initSocket())
738 BUS_ERROR(iterationCount,
"Could not init socket. Abort.");
742 if (ec_config_init(FALSE) <= 0)
744 BUS_ERROR(iterationCount,
"No slaves found on bus");
753 ARMARX_INFO << ec_slavecount <<
" slaves found and set from Init to PreOp";
764 return _initToPreOp() and _preOpToSafeOp();
772 return _initToPreOp() and _preOpToSafeOp() and _safeOpToOp();
790 Bus::_preOpToSafeOp()
795 ARMARX_VERBOSE <<
"Finish switching bus from 'PreOp' to 'SafeOp'";
801 if (slaves.size() > 0)
808 "Bus could not be validated. This indicates a severe hardware-error, "
809 "since the amount of slaves on the bus changed during the initialization "
810 "of the bus which can only happen if a PCB is behaving incorrectly or "
811 "(most likely) a cable is broken. Shutdown bus!");
818 if (!this->createDevices())
820 BUS_ERROR(iterationCount,
"Error during slave and device creation");
824 if (slaves.size() < 1)
826 BUS_ERROR(iterationCount,
"There are no usable devices on the bus!");
832 for (std::unique_ptr<SlaveInterface>& slave : slaves)
839 for (std::unique_ptr<SlaveInterface>& slave : slaves)
841 slave->prepareForSafeOp();
846 for (std::unique_ptr<SlaveInterface>& slave : slaves)
848 slave->finishPreparingForSafeOp();
853 int actualMappedSize = ec_config_map(
ioMap.data());
855 if (actualMappedSize >
ioMap.size())
857 ARMARX_ERROR <<
"Requested IO map size is " << actualMappedSize <<
", however only "
859 <<
" have been statically pre-allocated. Increase the IO map size.";
864 int expectedWKC = (ec_group[0].outputsWKC * 2) + ec_group[0].inputsWKC;
867 errorHandler.
init(expectedWKC);
870 if (!setPDOMappings())
874 "Couldn't map the PDO, maybe the the pc is under to much load. Check if there are "
875 "other performance hungry programs running.\nOr just try to start again");
883 ARMARX_INFO <<
"IOmapping done, size: " << actualMappedSize
884 <<
" - all Slaves are in SAFE-OP now\n";
888 for (
const auto& device : devices)
890 device->postSwitchToSafeOp();
908 return _preOpToSafeOp() and _safeOpToOp();
926 Bus::_safeOpToPreOp()
931 ARMARX_VERBOSE <<
"Finish switching bus from 'SafeOp' to 'PreOp'";
951 for (std::unique_ptr<SlaveInterface>& slave : slaves)
953 slave->prepareForOp();
956 for (std::unique_ptr<SlaveInterface>& slave : slaves)
958 slave->finishPreparingForOp();
980 for (std::shared_ptr<DeviceInterface>& device : devices)
982 device->postSwitchToOp();
989 size_t slaveReadyCounter = 0;
990 while (slaveReadyCounter != slaves.size())
993 slaveReadyCounter = 0;
994 std::string missingSlaves;
995 for (std::unique_ptr<SlaveInterface>& slave : slaves)
997 if (slave->prepareForRun())
1003 missingSlaves += slave->getSlaveIdentifier().getName() +
1008 << (slaves.size() - slaveReadyCounter) <<
"/" << slaves.size()
1009 <<
" slaves to get ready: " << missingSlaves;
1012 std::stringstream slaveInfo;
1013 for (std::unique_ptr<SlaveInterface>& slave : slaves)
1015 slaveInfo <<
"#" << slave->getSlaveNumber() <<
": "
1016 << slave->getSlaveIdentifier().getName() <<
"\n";
1071 Bus::changeBusState(EtherCATState state)
1090 if (retState == state)
1092 ARMARX_VERBOSE <<
"All slaves are already in requested state " << state;
1102 if (retState != state)
1104 ARMARX_INFO <<
"One or more slaves appear to not have reached the desired state. "
1105 <<
"If the desired state is lower than the actual one this is fine.";
1108 if (retState < state)
1111 for (std::uint16_t i = 1; i < ec_slavecount + 1; i++)
1113 EtherCATState
const actualState = ec_slave[i].state;
1114 if (actualState != state)
1118 "Slave did not reach requested state %s after %ld µs (start = "
1119 "%ld µs since epoch, end = %ld µs since epoch). Actual state: %s AL: "
1122 elapsed.toMicroSeconds(),
1123 start.toMicroSeconds(),
1124 end.toMicroSeconds(),
1125 actualState.c_str(),
1126 ec_ALstatuscode2string(ec_slave[i].ALstatuscode));
1138 Bus::validateBus()
const
1142 BUS_WARNING(iterationCount,
"No slaves have been created. Cannot validate the bus.");
1147 int slaveCount = ec_BRD(0x0000, ECT_REG_TYPE,
sizeof(w), &w, EC_TIMEOUTSAFE);
1148 if (slaveCount == EC_NOFRAME)
1150 BUS_ERROR(iterationCount,
"EC_NOFRAME when trying to get slave count.");
1154 if (
static_cast<std::uint16_t
>(slaveCount) != slaves.size())
1157 "Could not validate bus! Amount of found slaves is not equal to the previous "
1158 "created slaves. (Found: %u, Expected: %u)",
1174 if (socketInitialized)
1176 BUS_ERROR(iterationCount,
"Socket is already initialized.");
1180 if (socketFileDescriptor == -1)
1182 BUS_WARNING(iterationCount,
"socketFileDescriptor is -1 - did you forget to set it?");
1186 if (!ifname.empty())
1189 if (!ec_init(ifname.c_str()))
1192 "Could not init EtherCAT on %s\nExecute as root\n",
1197 else if (socketFileDescriptor != -1)
1199 ARMARX_INFO <<
"Using socketFileDescriptor " << socketFileDescriptor
1200 <<
" to open raw socket";
1202 if (!ec_init_wsock(socketFileDescriptor))
1205 "No socket connection on %u\nExecute as root\n",
1206 socketFileDescriptor);
1212 BUS_WARNING(iterationCount,
"Either socketFileDescriptor or ifname need to be set");
1217 ARMARX_INFO <<
"Started SOEM with socketFileDescriptor: " << socketFileDescriptor;
1219 socketInitialized =
true;
1224 Bus::readAllErrorCounters()
1226 std::vector<RegisterDataList> registerData;
1227 for (
const auto& slave : slaves)
1229 registerData.push_back(
1230 RegisterDataList{
static_cast<std::uint16_t
>(slave->getSlaveIdentifier().slaveIndex),
1268 EtherCATFrameList* frameList =
1272 int leftRetries = 3;
1273 bool readRegistersSuccessful =
false;
1279 if (not readRegistersSuccessful)
1283 if (leftRetries == 0)
1285 ARMARX_ERROR <<
"Repeatedly failed reading error registers. Giving up.";
1290 ARMARX_ERROR <<
"Failed reading error registers. Left retries: "
1295 }
while (not readRegistersSuccessful);
1300 struct ErrorCounters
1302 std::uint8_t invalidFrame = 0;
1303 std::uint8_t rxError = 0;
1304 std::uint8_t forwardedRxError = 0;
1305 std::uint8_t linkLost = 0;
1308 std::vector<ErrorCounters> counts(4);
1310 for (
const auto& d : registerData)
1313 {
return std::get<datatypes::EtherCATDataType::UNSIGNED8>(d.registerData.at(type)); };
1317 counts[0].invalidFrame = copyCounts(RE::FRAME_ERROR_COUNTER_PORT_0);
1318 counts[0].rxError = copyCounts(RE::PHYSICAL_ERROR_COUNTER_PORT_0);
1319 counts[0].forwardedRxError = copyCounts(RE::PREVIOUS_ERROR_COUNTER_PORT_0);
1320 counts[0].linkLost = copyCounts(RE::LOST_LINK_COUNTER_PORT_0);
1322 counts[1].invalidFrame = copyCounts(RE::FRAME_ERROR_COUNTER_PORT_1);
1323 counts[1].rxError = copyCounts(RE::PHYSICAL_ERROR_COUNTER_PORT_1);
1324 counts[1].forwardedRxError = copyCounts(RE::PREVIOUS_ERROR_COUNTER_PORT_1);
1325 counts[1].linkLost = copyCounts(RE::LOST_LINK_COUNTER_PORT_1);
1327 counts[2].invalidFrame = copyCounts(RE::FRAME_ERROR_COUNTER_PORT_2);
1328 counts[2].rxError = copyCounts(RE::PHYSICAL_ERROR_COUNTER_PORT_2);
1329 counts[2].forwardedRxError = copyCounts(RE::PREVIOUS_ERROR_COUNTER_PORT_2);
1330 counts[2].linkLost = copyCounts(RE::LOST_LINK_COUNTER_PORT_2);
1332 counts[3].invalidFrame = copyCounts(RE::FRAME_ERROR_COUNTER_PORT_3);
1333 counts[3].rxError = copyCounts(RE::PHYSICAL_ERROR_COUNTER_PORT_3);
1334 counts[3].forwardedRxError = copyCounts(RE::PREVIOUS_ERROR_COUNTER_PORT_3);
1335 counts[3].linkLost = copyCounts(RE::LOST_LINK_COUNTER_PORT_3);
1338 for (
unsigned int i = 0; i < 4; i++)
1340 if (counts[i].invalidFrame > 0 || counts[i].rxError > 0 ||
1341 counts[i].forwardedRxError > 0 || counts[i].linkLost > 0)
1344 "Error counters on port %u\n"
1345 " - invalid frame: \t\t%u\n"
1346 " - rx error: \t\t%u\n"
1347 " - forwarded rx error: \t%u\n"
1348 " - link lost: \t\t%u",
1350 counts[i].invalidFrame,
1352 counts[i].forwardedRxError,
1353 counts[i].linkLost);
1368 return functionalState;
1374 slaveFactories.push_back(factory);
1380 deviceFactories[name] = factory;
1388 this->hwconfig =
parser.getHardwareConfig();
1391 std::vector<std::experimental::observer_ptr<SlaveInterface>>
1395 std::vector<SlaveInterface*> slaves_raw;
1403 const std::vector<std::shared_ptr<DeviceInterface>>&
1416 for (
const std::unique_ptr<SlaveInterface>& slave : slaves)
1418 if (slave->getSlaveNumber() == slaveIndex)
1431 for (
const std::unique_ptr<SlaveInterface>& slave : slaves)
1434 slave->isEmergencyStopActive())
1447 const bool hasError = errorHandler.
hasError();
1455 this->robot = robot;
1463 errorRegisterReadingScheduler.reset();
1467 errorRegisterReadingScheduler = std::make_unique<SlaveRegisterReadingScheduler>(
1468 static_cast<std::uint16_t
>(slaves.size()),
1470 std::vector<datatypes::RegisterEnum>{
1471 datatypes::RegisterEnum::LOST_LINK_COUNTER_PORT_0,
1472 datatypes::RegisterEnum::LOST_LINK_COUNTER_PORT_1,
1473 datatypes::RegisterEnum::FRAME_ERROR_COUNTER_PORT_0,
1474 datatypes::RegisterEnum::FRAME_ERROR_COUNTER_PORT_1,
1475 datatypes::RegisterEnum::PHYSICAL_ERROR_COUNTER_PORT_0,
1476 datatypes::RegisterEnum::PHYSICAL_ERROR_COUNTER_PORT_1,
1477 datatypes::RegisterEnum::PREVIOUS_ERROR_COUNTER_PORT_0,
1478 datatypes::RegisterEnum::PREVIOUS_ERROR_COUNTER_PORT_1});
1485 return iterationCount;