Go to the documentation of this file.
35 #include <VirtualRobot/Nodes/RobotNode.h>
36 #include <VirtualRobot/RobotNodeSet.h>
37 #include <VirtualRobot/Tools/Gravity.h>
62 static constexpr
unsigned int MAX_SAFE_STACK = (8 * 1024);
76 if (mlockall(MCL_CURRENT | MCL_FUTURE) == -1)
82 unsigned char dummy[MAX_SAFE_STACK];
83 memset(dummy, 0, MAX_SAFE_STACK);
95 node->setEnforceJointLimits(
false);
97 std::stringstream massesInfo;
98 for (
int i = 0; i < static_cast<int>(
rtRobotBodySet->getSize()); ++i)
101 massesInfo <<
"\t" << node->getName() <<
": " << node->getMass() <<
" kg\n";
106 std::filesystem::path(
properties.periodicLoggingDirectory).is_absolute());
128 RTUnit::onDisconnectRobotUnit()
130 ARMARX_INFO << "RTUnit disconnects now";
134 RTUnit::onExitRobotUnit()
136 ARMARX_INFO << "RTUnit is exiting now ";
138 RTUtility::stopLowLatencyMode();
140 ARMARX_INFO << "RTUnit exit complete";
144 RTUnit::initializeKinematicUnit()
146 using IfaceT = KinematicUnitInterface;
148 auto guard = getGuard();
149 throwIfStateIsNot(RobotUnitState::InitializingUnits, __FUNCTION__);
150 //check if unit is already added
151 if (getUnit(IfaceT::ice_staticId()))
156 auto unit = createKinematicSubUnit(getIceProperties()->clone(),
157 ControlModes::Position1DoF,
158 properties.useTorqueVelocityModeAsDefault
159 ? ControlModes::VelocityTorque
160 : ControlModes::Velocity1DoF);
164 addUnit(std::move(unit));
169 RTUnit::startRTThread()
171 ARMARX_INFO << "starting control task";
173 if (rtTask.joinable())
178 rtTask = std::thread{[this]
187 ARMARX_FATAL << "RT Thread caused an uncaught exception:\n"
188 << GetHandledExceptionString();
194 RTUnit::joinControlThread()
196 ARMARX_INFO << "stopping control task";
199 ARMARX_INFO << "rt task stopped";
203 RTUnit::publish(StringVariantBaseMap debugObserverMap, StringVariantBaseMap timingMap)
205 RobotUnit::publish(std::move(debugObserverMap), std::move(timingMap));
208 armarx::PropertyDefinitionsPtr
209 RTUnit::createPropertyDefinitions()
211 PropertyDefinitionsPtr def = RobotUnit::createPropertyDefinitions();
214 properties.busConfigFilePath, "BusConfigFilePath", "Location of the BusConfigFile");
215 def->optional(properties.socketFileDescriptor,
216 "SocketFileDescriptor",
217 "Socketfiledescriptor on which the ethercat connection is running");
219 properties.ethercatInterfaceName,
220 "EthercatInterfaceName",
221 "Name of the ethercat socket. If set to non-empty string, this will be used over "
222 "SocketFileDescriptor");
223 def->optional(properties.useTorqueVelocityModeAsDefault,
224 "UseTorqueVelocityModeAsDefault",
225 "If true, the KinematicUnit will use TorqueVelocity mode for velocity mode");
226 def->optional(properties.rtLoopTimeInUS,
228 "Desired duration of one interation in the real-time control loop");
229 def->optional(properties.rTLoopTimingCheckToleranceFactor,
230 "RTLoopTimingCheckToleranceFactor",
231 "Factor by which the timing checks are multiplied. Higher value -> less "
236 def->optional(properties.periodicLoggingDirectory,
237 "PeriodicLoggingDirectory",
238 "This absolute path to a directory is used for storing periodic log-files.");
240 properties.periodicLoggingHistorySize,
241 "PeriodicLoggingHistorySize",
242 "The amount of older iterations of the periodic logging files that are kept.");
243 def->optional(properties.periodicLoggingIntervalInSeconds,
244 "PeriodicLoggingIntervalInSeconds",
245 "The duration in seconds of one periodic logging interval. After that "
246 "duration, a new set of log-files will be created");
247 def->optional(properties.periodicLoggingMode,
248 "PeriodicLoggingMode",
249 "Select in which mode the periodic logging should be executed.")
250 .map("None", PeriodicLoggingMode::None)
251 .map("OnlyBusErrors", PeriodicLoggingMode::OnlyBusErrors)
252 .map("OnlyRtLogging", PeriodicLoggingMode::OnlyRtLogging)
253 .map("Both", PeriodicLoggingMode::Both);
256 // EtherCAT error counters
257 def->optional(properties.enableErrorCountersReading,
258 "ErrorCountersReading.Enable",
259 "Whether to periodically read the error counters from all slaves and "
260 "provide them as sensor values");
261 def->optional(properties.errorCountersReadingPeriodInMS,
262 "ErrorCountersReading.PeriodInMS",
263 "Wait time in milliseconds between two full reads of the error counters of "
265 def->optional(properties.resetErrorRegistersAtStartup,
266 "ErrorCountersReading.ResetAtStartup",
267 "Whether to reset all error counters on all slaves after starting the bus.");
270 def->optional(properties.robotJointsNodeSet,
271 "nodeSet.robotJoints",
272 "The node set which contains the actuated joints.");
273 def->optional(properties.robotColNodeSet,
275 "The node set for e.g. gravity computation.");
277 def->optional(ethercatTimeouts.registerRead_us,
278 "ethercat.timeouts.register_read",
279 "EtherCAT timeout for a register frame to read certain registers in µs.");
280 def->optional(ethercatTimeouts.stateCheck_us,
281 "ethercat.timeouts.state_check",
282 "EtherCAT timeout to check for the current EtherCAT state of slaves in µs.");
283 def->optional(ethercatTimeouts.sdoRead_us,
284 "ethercat.timeouts.sdo_read",
285 "EtherCAT timeout to read an SDO in µs.");
286 def->optional(ethercatTimeouts.sdoWrite_us,
287 "ethercat.timeouts.sdo_write",
288 "EtherCAT timeout to write an SDO in µs.");
295 ARMARX_INFO << "DTOR of RTUnit";
297 backupLoggingRunning.store(false);
298 if (backupLoggingTask.joinable())
300 backupLoggingTask.join();
307 ARMARX_INFO << "Control task running";
308 Bus& bus = Bus::getBus();
310 // Start periodic logging of rt-values and bus errors
311 startBackupLogging();
315 ARMARX_INFO << "Leaving rtRun scope";
316 if (bus.rtGetEtherCATState() != EtherCATState::init)
320 backupLoggingRunning.store(false); // This will automatically dump the latest BusErrors
321 if (backupLoggingTask.joinable())
323 backupLoggingTask.join();
327 bool busStartSucceeded = false;
332 ARMARX_DEBUG << "initBus";
333 busStartSucceeded = initBusRTThread();
337 ARMARX_ERROR << "Initializing EtherCAT bus RT thread failed with exception: "
338 << armarx::GetHandledExceptionString();
342 ARMARX_INFO << "initBus finished with: " << busStartSucceeded;
343 if (!busStartSucceeded)
348 bool initializationDone = false;
349 bool initializationFailed = false;
350 ARMARX_DEBUG << "Async init starting now";
351 std::thread unitInitTask = std::thread{
356 finishDeviceInitialization();
357 // rtReadSensorDeviceValues(TimeUtil::GetTime(), IceUtil::Time::microSeconds(1)); // initialize sensor values
358 initializeDefaultUnits();
359 ARMARX_IMPORTANT << "Setting up custom Units";
360 finishUnitInitialization();
361 ARMARX_IMPORTANT << "Finishing setting up custom Units...DONE";
362 ARMARX_INFO << "Sleeping a moment to let everything settle in";
364 initializationDone = true;
369 << "Shutting down - RobotUnit Init Thread caused an uncaught exception:\n"
370 << GetHandledExceptionString();
371 initializationFailed = true;
374 unitInitTask.detach();
375 if (initializationFailed)
379 if (busStartSucceeded)
381 //setting the timestamps for the pdo update, at the moment we only have on
382 currentPDOUpdateTimestamp = armarx::rtNow();
383 CycleUtil cycleStats(IceUtil::Time::microSeconds(properties.rtLoopTimeInUS));
384 cycleStats.setBusyWaitShare(0.1f);
385 while (!initializationDone)
387 // auto realDeltaT = currentPDOUpdateTimestamp - lastPDOUpdateTimestamp;
388 // auto cappedDeltaT = IceUtil::Time::microSeconds(boost::algorithm::clamp(realDeltaT.toMicroSeconds(), 1, 2000)); // TODO: Make property
389 // bus.updateBus(false);
390 // rtReadSensorDeviceValues(currentPDOUpdateTimestamp, cappedDeltaT);
391 // lastPDOUpdateTimestamp = currentPDOUpdateTimestamp;
392 // currentPDOUpdateTimestamp = TimeUtil::GetTime();
393 cycleStats.waitForCycleDuration();
394 ARMARX_INFO << deactivateSpam(1) << "Waiting for unit initialization!";
396 ARMARX_IMPORTANT << "RobotUnit is now ready";
400 ARMARX_WARNING << "Bus was not started!";
404 ARMARX_DEBUG << "Setting up gravity calculation";
405 // init data structs for gravity calculation
406 for (int i = 0; i < static_cast<int>(rtRobotJointSet->getSize()); i++)
408 auto selectedJoint = getSensorDevice(rtRobotJointSet->getNode(i)->getName());
411 auto sensorValue = const_cast<SensorValue1DoFGravityTorque*>(
412 selectedJoint->getSensorValue()->asA<SensorValue1DoFGravityTorque>());
413 ARMARX_DEBUG << "will calculate gravity for robot node "
414 << rtRobotJointSet->getNode(i)->getName();
415 nodeJointDataVec.push_back(
416 std::make_pair(rtRobotJointSet->getNode(i), sensorValue));
420 ARMARX_INFO << "Joint " << rtRobotJointSet->getNode(i)->getName() << " not found";
421 nodeJointDataVec.push_back(std::make_pair(rtRobotJointSet->getNode(i), nullptr));
425 controlLoopRTThread();
427 while (getObjectScheduler() && !getObjectScheduler()->isTerminationRequested())
429 ARMARX_INFO << deactivateSpam() << "Waiting for shutdown";
434 MultiNodeRapidXMLReader
435 RTUnit::ReadHardwareConfigFile(std::string busConfigFilePath, std::string robotName)
438 if (!ArmarXDataPath::getAbsolutePath(busConfigFilePath, busConfigFilePath))
440 throw LocalException("could not find BusConfigFilePath: ") << busConfigFilePath;
442 ARMARX_CHECK(std::filesystem::is_regular_file(busConfigFilePath))
443 << "The bus config file `" << busConfigFilePath << "` does not exist!";
445 ARMARX_INFO_S << "Read the hw config from " << busConfigFilePath;
447 //reading the config for the Bus and create all the robot objects in the robot container
448 auto busConfigFilePathDir = std::filesystem::path(busConfigFilePath).parent_path();
451 ARMARX_INFO_S << "Reading file `" << busConfigFilePath << "`";
452 auto rapidXmlReaderPtr = RapidXmlReader::FromFile(busConfigFilePath);
453 ARMARX_CHECK_NOT_NULL(rapidXmlReaderPtr) << "Failed to read `" << busConfigFilePath << "`";
456 auto rootNode = rapidXmlReaderPtr->getRoot("HardwareConfig");
457 ARMARX_CHECK(rootNode.is_valid());
458 ARMARX_CHECK(rootNode.attribute_value("robotName") == robotName);
460 MultiNodeRapidXMLReader multiNode({rootNode});
461 for (RapidXmlReaderNode& includeNode : rootNode.nodes("include"))
463 ARMARX_CHECK(includeNode.has_attribute("file"))
464 << "File `" << busConfigFilePath
465 << "` contains invalid `include` tag. The `file` attribute is missing.";
466 const auto relPath = includeNode.attribute_value("file");
468 std::filesystem::path filepath = (busConfigFilePathDir / relPath);
469 if (!std::filesystem::exists(filepath))
472 if (!ArmarXDataPath::getAbsolutePath(relPath, absPath))
474 throw LocalException("Could not find config file at path ") << relPath;
479 auto includedNode = RapidXmlReader::FromFile(filepath.string());
480 ARMARX_CHECK(includedNode->getRoot("HardwareConfig").is_valid())
481 << "Invalid root node for XML file `" << filepath.string() << "`";
482 ARMARX_CHECK(includedNode->getRoot("HardwareConfig").attribute_value("robotName") ==
484 multiNode.addNode(includedNode->getRoot("HardwareConfig"));
490 RTUnit::initBusRTThread()
493 Bus& bus = Bus::getBus();
494 bus.setSocketFileDescriptor(properties.socketFileDescriptor);
495 bus.setIfName(properties.ethercatInterfaceName);
496 if (!bus.switchBusToSafeOp())
498 ARMARX_ERROR << "Bringing bus to EtherCAT state 'safe-operational
' failed.";
502 auto addDevices = [&](auto device)
504 auto cd = std::dynamic_pointer_cast<ControlDevice>(device);
507 addControlDevice(cd);
509 auto sd = std::dynamic_pointer_cast<SensorDevice>(device);
516 for (auto& device : bus.getDevices())
519 for (const auto& subDevice : device->getSubDevices())
521 addDevices(subDevice);
525 if (properties.resetErrorRegistersAtStartup)
527 for (const auto& slave : bus.getSlaves())
529 bus.resetErrorRegisters(
530 static_cast<std::uint16_t>(slave->getSlaveIdentifier().slaveIndex));
534 if (properties.enableErrorCountersReading)
536 bus.configureErrorCountersReading(properties.enableErrorCountersReading,
537 properties.errorCountersReadingPeriodInMS);
538 // Add special slave error counters sensor devices
539 for (const auto& slave : bus.getSlaves())
541 auto sd = std::dynamic_pointer_cast<SensorDevice>(slave->getErrorRegistersDevice());
549 ARMARX_INFO << "EtherCAT bus is started";
554 RTUnit::controlLoopRTThread()
556 Bus& bus = Bus::getBus();
559 finishControlThreadInitialization();
561 RTUtility::pinThreadToCPU(0);
562 RTUtility::elevateThreadPriority(RTUtility::RT_THREAD_PRIORITY);
563 RTUtility::startLowLatencyMode();
565 //to not get any strange start values
566 currentPDOUpdateTimestamp = armarx::rtNow();
567 CycleUtil cycleKeeper(IceUtil::Time::microSeconds(properties.rtLoopTimeInUS));
568 cycleKeeper.setBusyWaitShare(1.0f);
569 ARMARX_CHECK_EXPRESSION(rtGetRobot());
570 ARMARX_CHECK_EXPRESSION(rtRobotJointSet);
571 ARMARX_CHECK_EXPRESSION(rtRobotBodySet);
572 ARMARX_CHECK_EXPRESSION(rtRobotJointSet->getSize() > 0);
573 VirtualRobot::Gravity gravity(rtGetRobot(), rtRobotJointSet, rtRobotBodySet);
575 const std::uint64_t loopDurationThreshold =
576 properties.rtLoopTimeInUS / 1000 * properties.rTLoopTimingCheckToleranceFactor;
579 std::vector<float> gravityValues(rtRobotJointSet->getSize());
580 ARMARX_CHECK_EQUAL(rtRobotJointSet->getSize(), nodeJointDataVec.size());
584 if (!bus.switchBusToOp())
586 BUS_FATAL_AND_THROW(bus.getIterationCount(),
587 "Switching bus to EtherCAT state 'operational
' failed");
591 ARMARX_INFO << "RT control loop started";
592 EmergencyStopState lastSoftwareEmergencyStopState = rtGetEmergencyStopState();
594 auto lastMonoticTimestamp = armarx::rtNow();
595 auto currentMonotonicTimestamp = lastMonoticTimestamp;
596 currentPDOUpdateTimestamp = armarx::rtNow();
598 for (; taskRunning; ++_iterationCount)
600 const auto loopStartTime = armarx::rtNow();
601 rtGetRTThreadTimingsSensorDevice().rtMarkRtLoopStart();
603 auto realDeltaT = currentMonotonicTimestamp - lastMonoticTimestamp;
604 auto cappedDeltaT = IceUtil::Time::microSeconds(
605 std::clamp<long>(realDeltaT.toMicroSeconds(), 1, 2000)); // TODO: Make property
606 if (rtIsCalibrating())
608 BUS_TIMING_START(calibrateActivateControlers)
609 rtCalibrateActivateControlers();
610 BUS_TIMING_CEND(calibrateActivateControlers,
611 bus.getIterationCount(),
612 0.3 * loopDurationThreshold)
614 BUS_TIMING_START(switchControllerSetup)
615 rtSwitchControllerSetup(SwitchControllerMode::RTThread);
617 switchControllerSetup, bus.getIterationCount(), 0.3 * loopDurationThreshold)
619 BUS_TIMING_START(resetAllTargets)
622 resetAllTargets, bus.getIterationCount(), 0.3 * loopDurationThreshold)
624 BUS_TIMING_START(calibrateInRt)
625 _calibrationStatus = rtCalibrate();
627 calibrateInRt, bus.getIterationCount(), 0.3 * loopDurationThreshold)
629 BUS_TIMING_START(handleInvalidTargets)
630 rtHandleInvalidTargets();
632 handleInvalidTargets, bus.getIterationCount(), 0.3 * loopDurationThreshold)
634 BUS_TIMING_START(runJointControllers)
635 rtRunJointControllers(currentPDOUpdateTimestamp, cappedDeltaT);
637 runJointControllers, bus.getIterationCount(), 0.3 * loopDurationThreshold)
641 BUS_TIMING_START(RunControllers)
642 BUS_TIMING_START(SwitchControllers)
643 rtSwitchControllerSetup();
645 SwitchControllers, bus.getIterationCount(), 0.3 * loopDurationThreshold)
646 BUS_TIMING_START(ResettingTargets)
649 ResettingTargets, bus.getIterationCount(), 0.3 * loopDurationThreshold)
650 BUS_TIMING_START(RunNJointControllers)
651 rtRunNJointControllers(currentPDOUpdateTimestamp, cappedDeltaT);
653 RunNJointControllers, bus.getIterationCount(), 0.3 * loopDurationThreshold)
654 BUS_TIMING_START(CheckInvalidTargets)
655 rtHandleInvalidTargets();
657 CheckInvalidTargets, bus.getIterationCount(), 0.3 * loopDurationThreshold)
659 BUS_TIMING_START(RunJointControllers)
660 rtRunJointControllers(currentPDOUpdateTimestamp, cappedDeltaT);
662 RunJointControllers, bus.getIterationCount(), 0.3 * loopDurationThreshold)
664 RunControllers, bus.getIterationCount(), 0.3 * loopDurationThreshold)
668 BUS_TIMING_START(rtBusSendReceive)
669 rtGetRTThreadTimingsSensorDevice().rtMarkRtBusSendReceiveStart();
670 if (bus.rtGetEtherCATState() == EtherCATState::op)
673 BUS_TIMING_START(getEmergencyStopState)
674 auto currentSoftwareEmergencyStopState = rtGetEmergencyStopState();
676 getEmergencyStopState, bus.getIterationCount(), 0.5 * loopDurationThreshold)
677 if (lastSoftwareEmergencyStopState ==
678 EmergencyStopState::eEmergencyStopActive &&
679 currentSoftwareEmergencyStopState ==
680 EmergencyStopState::eEmergencyStopInactive)
684 lastSoftwareEmergencyStopState = currentSoftwareEmergencyStopState;
686 BUS_TIMING_START(rtUpdateBus)
687 bus.rtUpdateBus(true, _iterationCount);
689 rtUpdateBus, bus.getIterationCount(), 0.6 * loopDurationThreshold)
690 if (bus.rtIsEmergencyStopActive() || bus.rtHasError())
692 rtSetEmergencyStopState(EmergencyStopState::eEmergencyStopActive);
695 currentPDOUpdateTimestamp = armarx::rtNow();
697 rtGetRTThreadTimingsSensorDevice().rtMarkRtBusSendReceiveEnd();
698 BUS_TIMING_CEND(rtBusSendReceive, bus.getIterationCount(), 0.55)
699 BUS_TIMING_START(ReadSensorValues)
700 rtReadSensorDeviceValues(currentPDOUpdateTimestamp, cappedDeltaT);
702 ReadSensorValues, bus.getIterationCount(), 0.7 * loopDurationThreshold)
703 lastMonoticTimestamp = currentMonotonicTimestamp;
704 currentMonotonicTimestamp = armarx::rtNow();
706 BUS_TIMING_START(Publish)
707 rtUpdateSensorAndControlBuffer(currentPDOUpdateTimestamp, cappedDeltaT);
708 BUS_TIMING_CEND(Publish, bus.getIterationCount(), 0.15 * loopDurationThreshold)
710 BUS_TIMING_START(ComputeGravityTorques)
711 gravity.computeGravityTorque(gravityValues);
713 for (auto& node : nodeJointDataVec)
715 auto torque = gravityValues.at(i);
718 node.second->gravityTorque = -torque;
724 ComputeGravityTorques, bus.getIterationCount(), 0.2 * loopDurationThreshold)
727 rtGetRTThreadTimingsSensorDevice().rtMarkRtLoopPreSleep();
729 const auto loopPreSleepTime = armarx::rtNow();
730 BUS_TIMING_START(RTLoopWaiting)
731 cycleKeeper.waitForCycleDuration();
732 BUS_TIMING_CEND(RTLoopWaiting, bus.getIterationCount(), loopDurationThreshold)
733 const auto loopPostSleepTime = armarx::rtNow();
735 const auto loopDuration = armarx::rtNow() - loopStartTime;
737 if (bus.rtGetFunctionalState() == BusFunctionalState::Running &&
738 loopDuration.toMicroSecondsDouble() >
739 (properties.rtLoopTimeInUS + 500) *
740 properties.rTLoopTimingCheckToleranceFactor)
742 const SensorValueRTThreadTimings* sval =
743 rtGetRTThreadTimingsSensorDevice()
745 ->asA<SensorValueRTThreadTimings>();
746 ARMARX_CHECK_NOT_NULL(sval);
748 bus.getIterationCount(),
749 "RT loop is under 1kHz control frequency: %.1f \n"
750 "-- cycle time PDO timestamp %.1f µs\n"
752 "-- thread timing sensor value: \n"
753 "---- rtSwitchControllerSetup Duration %.1f µs\t"
754 " RoundTripTime %.1f µs\n"
755 "---- rtRunNJointControllers Duration %.1f µs\t"
756 " RoundTripTime %.1f µs\n"
757 "---- rtHandleInvalidTargets Duration %.1f µs\t"
758 " RoundTripTime %.1f µs\n"
759 "---- rtRunJointControllers Duration %.1f µs\t"
760 " RoundTripTime %.1f µs\n"
761 "---- rtBusSendReceive Duration %.1f µs\t"
762 " RoundTripTime %.1f µs\n"
763 "---- rtReadSensorDeviceValues Duration %.1f µs\t"
764 " RoundTripTime %.1f µs\n"
765 "---- rtUpdateSensorAndControlBuffer Duration %.1f µs\t"
766 " RoundTripTime %.1f µs\n"
767 "---- rtResetAllTargets Duration %.1f µs\t"
768 " RoundTripTime %.1f µs\n"
769 "---- rtLoop Duration %.1f µs\t"
770 " RoundTripTime %.1f µs\n",
771 realDeltaT.toMicroSecondsDouble(),
772 loopDuration.toMicroSecondsDouble(),
773 (loopPostSleepTime - loopPreSleepTime).toMicroSecondsDouble(),
774 sval->rtSwitchControllerSetupDuration.toMicroSecondsDouble(),
775 sval->rtSwitchControllerSetupRoundTripTime.toMicroSecondsDouble(),
776 sval->rtRunNJointControllersDuration.toMicroSecondsDouble(),
777 sval->rtRunNJointControllersRoundTripTime.toMicroSecondsDouble(),
778 sval->rtHandleInvalidTargetsDuration.toMicroSecondsDouble(),
779 sval->rtHandleInvalidTargetsRoundTripTime.toMicroSecondsDouble(),
780 sval->rtRunJointControllersDuration.toMicroSecondsDouble(),
781 sval->rtRunJointControllersRoundTripTime.toMicroSecondsDouble(),
782 sval->rtBusSendReceiveDuration.toMicroSecondsDouble(),
783 sval->rtBusSendReceiveRoundTripTime.toMicroSecondsDouble(),
784 sval->rtReadSensorDeviceValuesDuration.toMicroSecondsDouble(),
785 sval->rtReadSensorDeviceValuesRoundTripTime.toMicroSecondsDouble(),
786 sval->rtUpdateSensorAndControlBufferDuration.toMicroSecondsDouble(),
787 sval->rtUpdateSensorAndControlBufferRoundTripTime.toMicroSecondsDouble(),
788 sval->rtResetAllTargetsDuration.toMicroSecondsDouble(),
789 sval->rtResetAllTargetsRoundTripTime.toMicroSecondsDouble(),
790 sval->rtLoopDuration.toMicroSecondsDouble(),
791 sval->rtLoopRoundTripTime.toMicroSecondsDouble());
794 ARMARX_IMPORTANT << "RT loop stopped";
795 ARMARX_INFO << "Execution stats: Average: "
796 << cycleKeeper.getAverageDuration().toMilliSecondsDouble()
797 << " max: " << cycleKeeper.getMaximumDuration().toMilliSecondsDouble()
798 << " min: " << cycleKeeper.getMinimumDuration().toMilliSecondsDouble();
799 //switching off the bus and be sure that the bus will receive
802 catch (UserException& e)
804 ARMARX_FATAL << "exception in control thread!"
806 << e.what() << "\nreason:\n"
807 << e.reason << "\n\tname: " << e.ice_id() << "\n\tfile: " << e.ice_file()
808 << "\n\tline: " << e.ice_line() << "\n\tstack: " << e.ice_stackTrace()
812 catch (Ice::Exception& e)
814 ARMARX_FATAL << "exception in control thread!\nwhat:\n"
815 << e.what() << "\n\tname: " << e.ice_id() << "\n\tfile: " << e.ice_file()
816 << "\n\tline: " << e.ice_line() << "\n\tstack: " << e.ice_stackTrace()
820 catch (std::exception& e)
822 ARMARX_FATAL << "exception in control thread!\nwhat:\n" << e.what() << std::flush;
827 ARMARX_FATAL << "exception in control thread!" << std::flush;
830 ARMARX_INFO << "Leaving control loop function";
834 RTUnit::startBackupLogging()
836 ARMARX_IMPORTANT << "startBackupLogging()";
838 if (properties.periodicLoggingMode == PeriodicLoggingMode::None)
840 ARMARX_IMPORTANT << "Periodic backup logging deactivated.";
844 backupLoggingTask = std::thread{[this]
848 backupLoggingRunning.store(true);
849 periodicBackupLoggingRun();
859 RTUnit::periodicBackupLoggingRun()
861 // This loop is responsible for keeping the last x minutes of Sensor- and ControlData and BusErrors in temporary files
862 // Each x minutes a new instance of rtLogging is started and therefore a new file generated.
863 // Similary each x minutes the BusErrors from that timeframe are dumped into a file.
867 std::filesystem::path rtLogPath;
868 std::filesystem::path busErrorLogPath;
871 std::queue<FilePaths> fileHistory;
872 IceUtil::Time lastIterationStart = armarx::rtNow();
874 std::string robotUnitLogPathFormat =
875 std::filesystem::path(properties.periodicLoggingDirectory)
876 .append("rt_log_{DateTime}.csv")
878 std::filesystem::path busErrorLogPath =
879 std::filesystem::path(properties.periodicLoggingDirectory).append("bus_errors.txt");
882 SimpleRemoteReferenceCounterBasePtr token;
883 while (backupLoggingRunning.load())
886 if (fileHistory.size() > properties.periodicLoggingHistorySize)
888 FilePaths p = fileHistory.front();
889 std::filesystem::remove(p.rtLogPath);
890 std::filesystem::remove(p.busErrorLogPath);
894 lastIterationStart = armarx::rtNow();
895 while (backupLoggingRunning.load() &&
898 IceUtil::Time::seconds(properties.periodicLoggingIntervalInSeconds))
900 if (!token && this->getRobotUnitState() == RobotUnitState::Running &&
901 (properties.periodicLoggingMode == PeriodicLoggingMode::OnlyRtLogging ||
902 properties.periodicLoggingMode == PeriodicLoggingMode::Both))
904 ARMARX_DEBUG << "Start new periodic log iteration";
905 token = this->startRtLogging(robotUnitLogPathFormat, {"*"});
907 std::this_thread::sleep_for(std::chrono::milliseconds(100));
912 std::string rtLogActualPath;
913 if (backupLoggingRunning.load() && token)
915 rtLogActualPath = token->getId();
916 this->stopRtLogging(token);
922 std::string busErrorLogActualPath;
923 busErrorLogActualPath = Bus::getBus().logErrorsToFile(
924 busErrorLogPath, properties.periodicLoggingIntervalInSeconds);
926 if (properties.periodicLoggingMode == PeriodicLoggingMode::OnlyRtLogging ||
927 properties.periodicLoggingMode == PeriodicLoggingMode::None)
929 std::filesystem::remove(busErrorLogActualPath);
932 if (busErrorLogActualPath.empty())
934 // No errors happend in the last interval therefore we can delete the
935 // rtLog as well to save disk space
936 std::filesystem::remove(rtLogActualPath);
940 fileHistory.push({rtLogActualPath, busErrorLogActualPath});
944 // Special case for last rtLog in case of shutdown
945 // This rt log gets always saved no matter if the corresponding bus error log is empty or not
946 // We have to check it by hand and delete if no bus errors happend
947 if (token && (fileHistory.empty() || fileHistory.back().rtLogPath != token->getId()))
949 std::filesystem::remove(token->getId());
952 } // namespace armarx::control::ethercat
VirtualRobot::RobotNodeSetPtr rtRobotBodySet
armarx::control::ethercat::Timeouts ethercatTimeouts
#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...
struct armarx::control::ethercat::RTUnit::@55 properties
RobotUnitState getRobotUnitState() const
Returns the RobotUnit's State.
void setTimeouts(Timeouts const &timeouts)
Brief description of class Bus.
void setRobot(const VirtualRobot::RobotPtr &robot)
const VirtualRobot::RobotPtr & rtGetRobot() const
Returns the simox robot used in the control thread.
void setLocalMinimumLoggingLevel(MessageTypeT level)
With setLocalMinimumLoggingLevel the minimum verbosity-level of log-messages can be set.
static Bus & getBus()
This returns the one and only Bus object.
MessageTypeT getEffectiveLoggingLevel() const
#define ARMARX_CHECK_EXPRESSION(expression)
This macro evaluates the expression and if it turns out to be false it will throw an ExpressionExcept...
void onInitRobotUnit() override
called in onInitComponent
VirtualRobot::RobotPtr robot
void onConnectRobotUnit() override
called in onConnectComponent
VirtualRobot::RobotNodeSetPtr rtRobotJointSet