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)
211 foundSlaveInfo.name = slave->getSlaveIdentifier().getName();
213 slaves.push_back(std::move(slave));
214 foundSlaveInfo.found =
true;
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());
228 catch (LocalException& e)
232 "Error during slave creation; Reason: %s",
233 e.getReason().c_str());
237 if (not foundSlaveInfo.found)
240 reporter, slaveIdentifier,
"Could not find a corresponding factory");
246 if (reporter.hasErrors())
248 printAdditionalInformation(
249 "One or more slave objects could not be created from the physical slaves found on "
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"}});
263 BUS_INFO(iterationCount,
"Number of slaves pushed into slavesView is %d", slaves.size());
267 std::back_inserter(slavesView),
268 [](
auto& slave) { return std::experimental::make_observer(slave.get()); });
278 const std::string deviceClassName = deviceConfig->getType();
279 const std::string deviceInstanceName = deviceConfig->getName();
280 ARMARX_DEBUG <<
"Handling: " << deviceClassName <<
" name: " << deviceInstanceName
283 if (deviceFactories.find(deviceClassName) != deviceFactories.end())
285 auto factory = deviceFactories.at(deviceClassName);
287 <<
"Robot is null! You must set the robot via Bus::setRobot()!";
289 std::shared_ptr<DeviceInterface> device = factory(*deviceConfig, robot);
294 devices.push_back(std::move(device));
300 "Could not create device instance '%s' of class '%s'",
301 deviceInstanceName.c_str(),
302 deviceClassName.c_str());
309 "No device factory found for xml-node `%s` with name `%s` in "
311 deviceClassName.c_str(),
312 deviceInstanceName.c_str());
315 catch (LocalException& e)
319 "Exception during device creation. Device class: %s; Reason: %s",
320 deviceConfig->getName().c_str(),
321 e.getReason().c_str());
330 ARMARX_VERBOSE <<
"Checking if hardware config does not contain unnecessary items.";
331 std::vector<std::string> errorsList;
336 for (
auto&
message : errorsList)
341 throw std::runtime_error(
342 "The hardware_config did not match the code for the devices, see log");
347 if (reporter.hasErrors())
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 "
356 {
"Exception during the construction of the device",
357 "Make sure that there are no programming errors in the constructor of the "
366 for (std::unique_ptr<SlaveInterface>& slave : slaves)
370 bool slaveAssigned =
false;
371 for (std::shared_ptr<DeviceInterface>& device : devices)
377 slaveAssigned =
true;
378 slave->setParentDeviceName(device->getDeviceName());
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());
394 if (not slaveAssigned && slave->hasPDOMapping())
398 "Could not find a device to assign following slave to:\n%s",
399 slave->getSlaveIdentifier().toString().c_str());
406 if (reporter.hasErrors())
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."}});
419 ARMARX_VERBOSE <<
"Sanity checking that devices have all needed slaves assigned.";
420 for (std::shared_ptr<DeviceInterface>& device : devices)
432 "%s) reports not all slaves valid. Maybe a slave is missing on the bus",
433 device->getClassName().c_str(),
434 device->rtGetDeviceName());
439 for (
auto& slave : slaves)
442 slave->setErrorRegistersDevice(std::make_shared<SlaveErrorRegistersDevice>(
443 "SlaveIdx_" +
std::to_string(slave->getSlaveIdentifier().slaveIndex) +
"." +
444 slave->getSlaveIdentifier().getName(),
445 slave->getSlaveIdentifier().slaveIndex));
455 this->socketFileDescriptor = socketFileDescriptor;
461 this->ifname = ifname;
468 if (iterationCount != iteration - 1)
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.",
477 iterationCount = iteration;
481 "UpdateBus can not be executed because the bus is not in op-mode");
488 BUS_TIMING_CEND(bus_rtUpdateBus_isEmergencyStopActive, iterationCount, 0.3);
492 if ((not emergencyStopActive and emergencyStopWasActivated))
494 bool recovered =
true;
495 for (std::unique_ptr<SlaveInterface>& slave : slaves)
497 recovered &= slave->recoverFromEmergencyStop();
501 BUS_WARNING(iterationCount,
"Recovered from STO (Safe Torque Off)");
502 emergencyStopWasActivated =
false;
505 else if (emergencyStopActive and not emergencyStopWasActivated)
507 BUS_WARNING(iterationCount,
"STO (Safe Torque Off) active");
508 emergencyStopWasActivated = emergencyStopActive;
510 BUS_TIMING_CEND(bus_rtUpdateBus_recoverFromEmergencyStop, iterationCount, 0.3)
511 const auto delay = (
armarx::rtNow() - busUpdateLastUpdateTime);
512 if (delay.toMilliSecondsDouble() > 40)
515 "Update bus was not called for a long time: %ld ms",
516 delay.toMilliSeconds());
527 for (std::unique_ptr<SlaveInterface>& slave : slaves)
536 if (!emergencyStopActive)
540 BUS_TIMING_CEND(bus_rtUpdateBus_slaveErrorHandling, iterationCount, 0.3)
549 if (!errorHandler.
hasError() && errorRegisterReadingScheduler)
551 if (errorRegisterReadingScheduler->allRegistersUpdated())
553 auto data_ref = errorRegisterReadingScheduler->getRegisterData();
554 for (
const auto&
data : data_ref)
560 errorRegisterReadingScheduler->startReadingNextRegisters();
562 BUS_TIMING_CEND(bus_rtUpdateBus_updateErrorRegisters, iterationCount, 0.3)
570 else if (emergencyStopActive)
578 BUS_TIMING_CEND(bus_rtUpdateBus_updateFunctionalState, iterationCount, 0.3)
588 BUS_WARNING(iterationCount,
"Bus is already shutdown");
593 readAllErrorCounters();
604 for (std::unique_ptr<SlaveInterface>& slave : slaves)
607 <<
"shutting down slave "
608 << slave->getSlaveIdentifier().getName() <<
" ("
609 << slave->getSlaveNumber() <<
"/" << slaves.size() <<
")";
610 found |= !slave->shutdown();
627 socketInitialized =
false;
643 static const std::map<
EtherCATState, std::function<bool(
Bus*)>> transitions = {
650 BUS_WARNING(b->iterationCount,
"Bus is already in init");
656 return transitions.at(busState)(
this);
662 static const std::map<
EtherCATState, std::function<bool(
Bus*)>> transitions = {
669 BUS_WARNING(b->iterationCount,
"Bus is already in preOp");
676 return transitions.at(busState)(
this);
682 static const std::map<
EtherCATState, std::function<bool(
Bus*)>> transitions = {
689 BUS_WARNING(b->iterationCount,
"Bus is already in safeOp");
696 return transitions.at(busState)(
this);
702 static const std::map<
EtherCATState, std::function<bool(
Bus*)>> transitions = {
709 BUS_WARNING(b->iterationCount,
"Bus is already in op");
713 return transitions.at(busState)(
this);
727 if (!socketInitialized && !initSocket())
729 BUS_ERROR(iterationCount,
"Could not init socket. Abort.");
733 if (ec_config_init(FALSE) <= 0)
735 BUS_ERROR(iterationCount,
"No slaves found on bus");
744 ARMARX_INFO << ec_slavecount <<
" slaves found and set from Init to PreOp";
755 return _initToPreOp() and _preOpToSafeOp();
763 return _initToPreOp() and _preOpToSafeOp() and _safeOpToOp();
781 Bus::_preOpToSafeOp()
786 ARMARX_VERBOSE <<
"Finish switching bus from 'PreOp' to 'SafeOp'";
792 if (slaves.size() > 0)
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!");
809 if (!this->createDevices())
811 BUS_ERROR(iterationCount,
"Error during slave and device creation");
815 if (slaves.size() < 1)
817 BUS_ERROR(iterationCount,
"There are no usable devices on the bus!");
823 for (std::unique_ptr<SlaveInterface>& slave : slaves)
830 for (std::unique_ptr<SlaveInterface>& slave : slaves)
832 slave->prepareForSafeOp();
837 for (std::unique_ptr<SlaveInterface>& slave : slaves)
839 slave->finishPreparingForSafeOp();
844 int actualMappedSize = ec_config_map(
ioMap.data());
847 int expectedWKC = (ec_group[0].outputsWKC * 2) + ec_group[0].inputsWKC;
848 ARMARX_VERBOSE <<
"Calculated workcounter: " << expectedWKC << std::endl;
850 errorHandler.
init(expectedWKC);
853 if (!setPDOMappings())
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");
866 ARMARX_INFO <<
"IOmapping done, size: " << actualMappedSize
867 <<
" - all Slaves are in SAFE-OP now\n";
871 for (
const auto& device : devices)
873 device->postSwitchToSafeOp();
891 return _preOpToSafeOp() and _safeOpToOp();
909 Bus::_safeOpToPreOp()
914 ARMARX_VERBOSE <<
"Finish switching bus from 'SafeOp' to 'PreOp'";
934 for (std::unique_ptr<SlaveInterface>& slave : slaves)
936 slave->prepareForOp();
939 for (std::unique_ptr<SlaveInterface>& slave : slaves)
941 slave->finishPreparingForOp();
963 for (std::shared_ptr<DeviceInterface>& device : devices)
965 device->postSwitchToOp();
972 size_t slaveReadyCounter = 0;
973 while (slaveReadyCounter != slaves.size())
976 slaveReadyCounter = 0;
977 std::string missingSlaves;
978 for (std::unique_ptr<SlaveInterface>& slave : slaves)
980 if (slave->prepareForRun())
986 missingSlaves += slave->getSlaveIdentifier().getName() +
991 << (slaves.size() - slaveReadyCounter) <<
"/" << slaves.size()
992 <<
" slaves to get ready: " << missingSlaves;
995 std::stringstream slaveInfo;
996 for (std::unique_ptr<SlaveInterface>& slave : slaves)
998 slaveInfo <<
"#" << slave->getSlaveNumber() <<
": "
999 << slave->getSlaveIdentifier().getName() <<
"\n";
1054 Bus::changeBusState(EtherCATState state)
1073 if (retState == state)
1075 ARMARX_VERBOSE <<
"All slaves are already in requested state " << state;
1085 if (retState != state)
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.";
1091 if (retState < state)
1094 for (std::uint16_t i = 1; i < ec_slavecount + 1; i++)
1096 EtherCATState
const actualState = ec_slave[i].state;
1097 if (actualState != state)
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: "
1105 elapsed.toMicroSeconds(),
1106 start.toMicroSeconds(),
1107 end.toMicroSeconds(),
1108 actualState.c_str(),
1109 ec_ALstatuscode2string(ec_slave[i].ALstatuscode));
1121 Bus::validateBus()
const
1125 BUS_WARNING(iterationCount,
"No slaves have been created. Cannot validate the bus.");
1130 int slaveCount = ec_BRD(0x0000, ECT_REG_TYPE,
sizeof(w), &w, EC_TIMEOUTSAFE);
1131 if (slaveCount == EC_NOFRAME)
1133 BUS_ERROR(iterationCount,
"EC_NOFRAME when trying to get slave count.");
1137 if (
static_cast<std::uint16_t
>(slaveCount) != slaves.size())
1140 "Could not validate bus! Amount of found slaves is not equal to the previous "
1141 "created slaves. (Found: %u, Expected: %u)",
1157 if (socketInitialized)
1159 BUS_ERROR(iterationCount,
"Socket is already initialized.");
1163 if (socketFileDescriptor == -1)
1165 BUS_WARNING(iterationCount,
"socketFileDescriptor is -1 - did you forget to set it?");
1169 if (!ifname.empty())
1172 if (!ec_init(ifname.c_str()))
1175 "Could not init EtherCAT on %s\nExecute as root\n",
1180 else if (socketFileDescriptor != -1)
1182 ARMARX_INFO <<
"Using socketFileDescriptor " << socketFileDescriptor
1183 <<
" to open raw socket";
1185 if (!ec_init_wsock(socketFileDescriptor))
1188 "No socket connection on %u\nExecute as root\n",
1189 socketFileDescriptor);
1195 BUS_WARNING(iterationCount,
"Either socketFileDescriptor or ifname need to be set");
1200 ARMARX_INFO <<
"Started SOEM with socketFileDescriptor: " << socketFileDescriptor;
1202 socketInitialized =
true;
1207 Bus::readAllErrorCounters()
1209 std::vector<RegisterDataList> registerData;
1210 for (
const auto& slave : slaves)
1212 registerData.push_back(
1213 RegisterDataList{
static_cast<std::uint16_t
>(slave->getSlaveIdentifier().slaveIndex),
1251 EtherCATFrameList* frameList =
1255 int leftRetries = 3;
1256 bool readRegistersSuccessful =
false;
1262 if (not readRegistersSuccessful)
1266 if (leftRetries == 0)
1268 ARMARX_ERROR <<
"Repeatedly failed reading error registers. Giving up.";
1273 ARMARX_ERROR <<
"Failed reading error registers. Left retries: "
1278 }
while (not readRegistersSuccessful);
1283 struct ErrorCounters
1285 std::uint8_t invalidFrame = 0;
1286 std::uint8_t rxError = 0;
1287 std::uint8_t forwardedRxError = 0;
1288 std::uint8_t linkLost = 0;
1291 std::vector<ErrorCounters> counts(4);
1293 for (
const auto& d : registerData)
1296 {
return std::get<datatypes::EtherCATDataType::UNSIGNED8>(d.registerData.at(type)); };
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);
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);
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);
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);
1321 for (
unsigned int i = 0; i < 4; i++)
1323 if (counts[i].invalidFrame > 0 || counts[i].rxError > 0 ||
1324 counts[i].forwardedRxError > 0 || counts[i].linkLost > 0)
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",
1333 counts[i].invalidFrame,
1335 counts[i].forwardedRxError,
1336 counts[i].linkLost);
1351 return functionalState;
1357 slaveFactories.push_back(factory);
1363 deviceFactories[name] = factory;
1371 this->hwconfig =
parser.getHardwareConfig();
1374 std::vector<std::experimental::observer_ptr<SlaveInterface>>
1378 std::vector<SlaveInterface*> slaves_raw;
1386 const std::vector<std::shared_ptr<DeviceInterface>>&
1399 for (
const std::unique_ptr<SlaveInterface>& slave : slaves)
1401 if (slave->getSlaveNumber() == slaveIndex)
1414 for (
const std::unique_ptr<SlaveInterface>& slave : slaves)
1417 slave->isEmergencyStopActive())
1430 const bool hasError = errorHandler.
hasError();
1438 this->robot = robot;
1446 errorRegisterReadingScheduler.reset();
1450 errorRegisterReadingScheduler = std::make_unique<SlaveRegisterReadingScheduler>(
1451 static_cast<std::uint16_t
>(slaves.size()),
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});
1468 return iterationCount;