Go to the documentation of this file.
35 #include <VirtualRobot/RobotNodeSet.h>
36 #include <VirtualRobot/Tools/Gravity.h>
61 static constexpr
unsigned int MAX_SAFE_STACK = (8 * 1024);
75 if (mlockall(MCL_CURRENT | MCL_FUTURE) == -1)
81 unsigned char dummy[MAX_SAFE_STACK];
82 memset(dummy, 0, MAX_SAFE_STACK);
94 node->setEnforceJointLimits(
false);
96 std::stringstream massesInfo;
97 for (
int i = 0; i < static_cast<int>(
rtRobotBodySet->getSize()); ++i)
100 massesInfo <<
"\t" << node->getName() <<
": " << node->getMass() <<
" kg\n";
105 std::filesystem::path(
properties.periodicLoggingDirectory).is_absolute());
127 RTUnit::onDisconnectRobotUnit()
129 ARMARX_INFO << "RTUnit disconnects now";
133 RTUnit::onExitRobotUnit()
135 ARMARX_INFO << "RTUnit is exiting now ";
137 RTUtility::stopLowLatencyMode();
139 ARMARX_INFO << "RTUnit exit complete";
143 RTUnit::initializeKinematicUnit()
145 using IfaceT = KinematicUnitInterface;
147 auto guard = getGuard();
148 throwIfStateIsNot(RobotUnitState::InitializingUnits, __FUNCTION__);
149 //check if unit is already added
150 if (getUnit(IfaceT::ice_staticId()))
155 auto unit = createKinematicSubUnit(getIceProperties()->clone(),
156 ControlModes::Position1DoF,
157 properties.useTorqueVelocityModeAsDefault
158 ? ControlModes::VelocityTorque
159 : ControlModes::Velocity1DoF);
163 addUnit(std::move(unit));
168 RTUnit::startRTThread()
170 ARMARX_INFO << "starting control task";
172 if (rtTask.joinable())
177 rtTask = std::thread{[this]
186 ARMARX_FATAL << "RT Thread caused an uncaught exception:\n"
187 << GetHandledExceptionString();
193 RTUnit::joinControlThread()
195 ARMARX_INFO << "stopping control task";
198 ARMARX_INFO << "rt task stopped";
202 RTUnit::publish(StringVariantBaseMap debugObserverMap, StringVariantBaseMap timingMap)
204 RobotUnit::publish(std::move(debugObserverMap), std::move(timingMap));
207 armarx::PropertyDefinitionsPtr
208 RTUnit::createPropertyDefinitions()
210 PropertyDefinitionsPtr def = RobotUnit::createPropertyDefinitions();
213 properties.busConfigFilePath, "BusConfigFilePath", "Location of the BusConfigFile");
214 def->optional(properties.socketFileDescriptor,
215 "SocketFileDescriptor",
216 "Socketfiledescriptor on which the ethercat connection is running");
218 properties.ethercatInterfaceName,
219 "EthercatInterfaceName",
220 "Name of the ethercat socket. If set to non-empty string, this will be used over "
221 "SocketFileDescriptor");
222 def->optional(properties.useTorqueVelocityModeAsDefault,
223 "UseTorqueVelocityModeAsDefault",
224 "If true, the KinematicUnit will use TorqueVelocity mode for velocity mode");
225 def->optional(properties.rtLoopTimeInUS,
227 "Desired duration of one interation in the real-time control loop");
228 def->optional(properties.rTLoopTimingCheckToleranceFactor,
229 "RTLoopTimingCheckToleranceFactor",
230 "Factor by which the timing checks are multiplied. Higher value -> less "
235 def->optional(properties.periodicLoggingDirectory,
236 "PeriodicLoggingDirectory",
237 "This absolute path to a directory is used for storing periodic log-files.");
239 properties.periodicLoggingHistorySize,
240 "PeriodicLoggingHistorySize",
241 "The amount of older iterations of the periodic logging files that are kept.");
242 def->optional(properties.periodicLoggingIntervalInSeconds,
243 "PeriodicLoggingIntervalInSeconds",
244 "The duration in seconds of one periodic logging interval. After that "
245 "duration, a new set of log-files will be created");
246 def->optional(properties.periodicLoggingMode,
247 "PeriodicLoggingMode",
248 "Select in which mode the periodic logging should be executed.")
249 .map("None", PeriodicLoggingMode::None)
250 .map("OnlyBusErrors", PeriodicLoggingMode::OnlyBusErrors)
251 .map("OnlyRtLogging", PeriodicLoggingMode::OnlyRtLogging)
252 .map("Both", PeriodicLoggingMode::Both);
255 // EtherCAT error counters
256 def->optional(properties.enableErrorCountersReading,
257 "ErrorCountersReading.Enable",
258 "Whether to periodically read the error counters from all slaves and "
259 "provide them as sensor values");
260 def->optional(properties.errorCountersReadingPeriodInMS,
261 "ErrorCountersReading.PeriodInMS",
262 "Wait time in milliseconds between two full reads of the error counters of "
264 def->optional(properties.resetErrorRegistersAtStartup,
265 "ErrorCountersReading.ResetAtStartup",
266 "Whether to reset all error counters on all slaves after starting the bus.");
269 def->optional(properties.robotJointsNodeSet, "nodeSet.robotJoints", "The node set which contains the actuated joints.");
270 def->optional(properties.robotColNodeSet, "nodeSet.robotCol", "The node set for e.g. gravity computation.");
272 def->optional(ethercatTimeouts.registerRead_us,
273 "ethercat.timeouts.register_read",
274 "EtherCAT timeout for a register frame to read certain registers in µs.");
275 def->optional(ethercatTimeouts.stateCheck_us,
276 "ethercat.timeouts.state_check",
277 "EtherCAT timeout to check for the current EtherCAT state of slaves in µs.");
278 def->optional(ethercatTimeouts.sdoRead_us,
279 "ethercat.timeouts.sdo_read",
280 "EtherCAT timeout to read an SDO in µs.");
281 def->optional(ethercatTimeouts.sdoWrite_us,
282 "ethercat.timeouts.sdo_write",
283 "EtherCAT timeout to write an SDO in µs.");
290 ARMARX_INFO << "DTOR of RTUnit";
292 backupLoggingRunning.store(false);
293 if (backupLoggingTask.joinable())
295 backupLoggingTask.join();
302 ARMARX_INFO << "Control task running";
303 Bus& bus = Bus::getBus();
305 // Start periodic logging of rt-values and bus errors
306 startBackupLogging();
310 ARMARX_INFO << "Leaving rtRun scope";
311 if (bus.rtGetEtherCATState() != EtherCATState::init)
315 backupLoggingRunning.store(false); // This will automatically dump the latest BusErrors
316 if (backupLoggingTask.joinable())
318 backupLoggingTask.join();
322 bool busStartSucceeded = false;
327 ARMARX_DEBUG << "initBus";
328 busStartSucceeded = initBusRTThread();
332 ARMARX_ERROR << "Initializing EtherCAT bus RT thread failed with exception: "
333 << armarx::GetHandledExceptionString();
337 ARMARX_INFO << "initBus finished with: " << busStartSucceeded;
338 if (!busStartSucceeded)
343 bool initializationDone = false;
344 bool initializationFailed = false;
345 ARMARX_DEBUG << "Async init starting now";
346 std::thread unitInitTask = std::thread{
351 finishDeviceInitialization();
352 // rtReadSensorDeviceValues(TimeUtil::GetTime(), IceUtil::Time::microSeconds(1)); // initialize sensor values
353 initializeDefaultUnits();
354 ARMARX_IMPORTANT << "Setting up custom Units";
355 finishUnitInitialization();
356 ARMARX_IMPORTANT << "Finishing setting up custom Units...DONE";
357 ARMARX_INFO << "Sleeping a moment to let everything settle in";
359 initializationDone = true;
364 << "Shutting down - RobotUnit Init Thread caused an uncaught exception:\n"
365 << GetHandledExceptionString();
366 initializationFailed = true;
369 unitInitTask.detach();
370 if (initializationFailed)
374 if (busStartSucceeded)
376 //setting the timestamps for the pdo update, at the moment we only have on
377 currentPDOUpdateTimestamp = armarx::rtNow();
378 CycleUtil cycleStats(IceUtil::Time::microSeconds(properties.rtLoopTimeInUS));
379 cycleStats.setBusyWaitShare(0.1f);
380 while (!initializationDone)
382 // auto realDeltaT = currentPDOUpdateTimestamp - lastPDOUpdateTimestamp;
383 // auto cappedDeltaT = IceUtil::Time::microSeconds(boost::algorithm::clamp(realDeltaT.toMicroSeconds(), 1, 2000)); // TODO: Make property
384 // bus.updateBus(false);
385 // rtReadSensorDeviceValues(currentPDOUpdateTimestamp, cappedDeltaT);
386 // lastPDOUpdateTimestamp = currentPDOUpdateTimestamp;
387 // currentPDOUpdateTimestamp = TimeUtil::GetTime();
388 cycleStats.waitForCycleDuration();
389 ARMARX_INFO << deactivateSpam(1) << "Waiting for unit initialization!";
391 ARMARX_IMPORTANT << "RobotUnit is now ready";
395 ARMARX_WARNING << "Bus was not started!";
399 ARMARX_DEBUG << "Setting up gravity calculation";
400 // init data structs for gravity calculation
401 for (int i = 0; i < static_cast<int>(rtRobotJointSet->getSize()); i++)
403 auto selectedJoint = getSensorDevice(rtRobotJointSet->getNode(i)->getName());
406 auto sensorValue = const_cast<SensorValue1DoFGravityTorque*>(
407 selectedJoint->getSensorValue()->asA<SensorValue1DoFGravityTorque>());
408 ARMARX_DEBUG << "will calculate gravity for robot node "
409 << rtRobotJointSet->getNode(i)->getName();
410 nodeJointDataVec.push_back(
411 std::make_pair(rtRobotJointSet->getNode(i), sensorValue));
415 ARMARX_INFO << "Joint " << rtRobotJointSet->getNode(i)->getName() << " not found";
416 nodeJointDataVec.push_back(std::make_pair(rtRobotJointSet->getNode(i), nullptr));
420 controlLoopRTThread();
422 while (getObjectScheduler() && !getObjectScheduler()->isTerminationRequested())
424 ARMARX_INFO << deactivateSpam() << "Waiting for shutdown";
429 MultiNodeRapidXMLReader
430 RTUnit::ReadHardwareConfigFile(std::string busConfigFilePath, std::string robotName)
433 if (!ArmarXDataPath::getAbsolutePath(busConfigFilePath, busConfigFilePath))
435 throw LocalException("could not find BusConfigFilePath: ") << busConfigFilePath;
437 ARMARX_CHECK(std::filesystem::is_regular_file(busConfigFilePath))
438 << "The bus config file `" << busConfigFilePath << "` does not exist!";
440 ARMARX_INFO_S << "Read the hw config from " << busConfigFilePath;
442 //reading the config for the Bus and create all the robot objects in the robot container
443 auto busConfigFilePathDir = std::filesystem::path(busConfigFilePath).parent_path();
446 ARMARX_INFO_S << "Reading file `" << busConfigFilePath << "`";
447 auto rapidXmlReaderPtr = RapidXmlReader::FromFile(busConfigFilePath);
448 ARMARX_CHECK_NOT_NULL(rapidXmlReaderPtr) << "Failed to read `" << busConfigFilePath << "`";
451 auto rootNode = rapidXmlReaderPtr->getRoot("HardwareConfig");
452 ARMARX_CHECK(rootNode.is_valid());
453 ARMARX_CHECK(rootNode.attribute_value("robotName") == robotName);
455 MultiNodeRapidXMLReader multiNode({rootNode});
456 for (RapidXmlReaderNode& includeNode : rootNode.nodes("include"))
458 ARMARX_CHECK(includeNode.has_attribute("file"))
459 << "File `" << busConfigFilePath
460 << "` contains invalid `include` tag. The `file` attribute is missing.";
461 const auto relPath = includeNode.attribute_value("file");
463 std::filesystem::path filepath = (busConfigFilePathDir / relPath);
464 if (!std::filesystem::exists(filepath))
467 if (!ArmarXDataPath::getAbsolutePath(relPath, absPath))
469 throw LocalException("Could not find config file at path ") << relPath;
474 auto includedNode = RapidXmlReader::FromFile(filepath.string());
475 ARMARX_CHECK(includedNode->getRoot("HardwareConfig").is_valid())
476 << "Invalid root node for XML file `" << filepath.string() << "`";
477 ARMARX_CHECK(includedNode->getRoot("HardwareConfig").attribute_value("robotName") ==
479 multiNode.addNode(includedNode->getRoot("HardwareConfig"));
485 RTUnit::initBusRTThread()
488 Bus& bus = Bus::getBus();
489 bus.setSocketFileDescriptor(properties.socketFileDescriptor);
490 bus.setIfName(properties.ethercatInterfaceName);
491 if (!bus.switchBusToSafeOp())
493 ARMARX_ERROR << "Bringing bus to EtherCAT state 'safe-operational
' failed.";
497 auto addDevices = [&](auto device)
499 auto cd = std::dynamic_pointer_cast<ControlDevice>(device);
502 addControlDevice(cd);
504 auto sd = std::dynamic_pointer_cast<SensorDevice>(device);
511 for (auto& device : bus.getDevices())
514 for (const auto& subDevice : device->getSubDevices())
516 addDevices(subDevice);
520 if (properties.resetErrorRegistersAtStartup)
522 for (const auto& slave : bus.getSlaves())
524 bus.resetErrorRegisters(
525 static_cast<std::uint16_t>(slave->getSlaveIdentifier().slaveIndex));
529 if (properties.enableErrorCountersReading)
531 bus.configureErrorCountersReading(properties.enableErrorCountersReading,
532 properties.errorCountersReadingPeriodInMS);
533 // Add special slave error counters sensor devices
534 for (const auto& slave : bus.getSlaves())
536 auto sd = std::dynamic_pointer_cast<SensorDevice>(slave->getErrorRegistersDevice());
544 ARMARX_INFO << "EtherCAT bus is started";
549 RTUnit::controlLoopRTThread()
551 Bus& bus = Bus::getBus();
554 finishControlThreadInitialization();
556 RTUtility::pinThreadToCPU(0);
557 RTUtility::elevateThreadPriority(RTUtility::RT_THREAD_PRIORITY);
558 RTUtility::startLowLatencyMode();
560 //to not get any strange start values
561 currentPDOUpdateTimestamp = armarx::rtNow();
562 CycleUtil cycleKeeper(IceUtil::Time::microSeconds(properties.rtLoopTimeInUS));
563 cycleKeeper.setBusyWaitShare(1.0f);
564 ARMARX_CHECK_EXPRESSION(rtGetRobot());
565 ARMARX_CHECK_EXPRESSION(rtRobotJointSet);
566 ARMARX_CHECK_EXPRESSION(rtRobotBodySet);
567 ARMARX_CHECK_EXPRESSION(rtRobotJointSet->getSize() > 0);
568 VirtualRobot::Gravity gravity(rtGetRobot(), rtRobotJointSet, rtRobotBodySet);
570 const std::uint64_t loopDurationThreshold =
571 properties.rtLoopTimeInUS / 1000 * properties.rTLoopTimingCheckToleranceFactor;
574 std::vector<float> gravityValues(rtRobotJointSet->getSize());
575 ARMARX_CHECK_EQUAL(rtRobotJointSet->getSize(), nodeJointDataVec.size());
579 if (!bus.switchBusToOp())
581 BUS_FATAL_AND_THROW(bus.getIterationCount(), "Switching bus to EtherCAT state 'operational
' failed");
585 ARMARX_INFO << "RT control loop started";
586 EmergencyStopState lastSoftwareEmergencyStopState = rtGetEmergencyStopState();
588 auto lastMonoticTimestamp = armarx::rtNow();
589 auto currentMonotonicTimestamp = lastMonoticTimestamp;
590 currentPDOUpdateTimestamp = armarx::rtNow();
592 for (; taskRunning; ++_iterationCount)
594 const auto loopStartTime = armarx::rtNow();
595 rtGetRTThreadTimingsSensorDevice().rtMarkRtLoopStart();
597 auto realDeltaT = currentMonotonicTimestamp - lastMonoticTimestamp;
598 auto cappedDeltaT = IceUtil::Time::microSeconds(
599 std::clamp<long>(realDeltaT.toMicroSeconds(), 1, 2000)); // TODO: Make property
600 if (rtIsCalibrating())
602 BUS_TIMING_START(calibrateActivateControlers)
603 rtCalibrateActivateControlers();
604 BUS_TIMING_CEND(calibrateActivateControlers,
605 bus.getIterationCount(),
606 0.3 * loopDurationThreshold)
608 BUS_TIMING_START(switchControllerSetup)
609 rtSwitchControllerSetup(SwitchControllerMode::RTThread);
611 switchControllerSetup, bus.getIterationCount(), 0.3 * loopDurationThreshold)
613 BUS_TIMING_START(resetAllTargets)
616 resetAllTargets, bus.getIterationCount(), 0.3 * loopDurationThreshold)
618 BUS_TIMING_START(calibrateInRt)
619 _calibrationStatus = rtCalibrate();
621 calibrateInRt, bus.getIterationCount(), 0.3 * loopDurationThreshold)
623 BUS_TIMING_START(handleInvalidTargets)
624 rtHandleInvalidTargets();
626 handleInvalidTargets, bus.getIterationCount(), 0.3 * loopDurationThreshold)
628 BUS_TIMING_START(runJointControllers)
629 rtRunJointControllers(currentPDOUpdateTimestamp, cappedDeltaT);
631 runJointControllers, bus.getIterationCount(), 0.3 * loopDurationThreshold)
635 BUS_TIMING_START(RunControllers)
636 BUS_TIMING_START(SwitchControllers)
637 rtSwitchControllerSetup();
639 SwitchControllers, bus.getIterationCount(), 0.3 * loopDurationThreshold)
640 BUS_TIMING_START(ResettingTargets)
643 ResettingTargets, bus.getIterationCount(), 0.3 * loopDurationThreshold)
644 BUS_TIMING_START(RunNJointControllers)
645 rtRunNJointControllers(currentPDOUpdateTimestamp, cappedDeltaT);
647 RunNJointControllers, bus.getIterationCount(), 0.3 * loopDurationThreshold)
648 BUS_TIMING_START(CheckInvalidTargets)
649 rtHandleInvalidTargets();
651 CheckInvalidTargets, bus.getIterationCount(), 0.3 * loopDurationThreshold)
653 BUS_TIMING_START(RunJointControllers)
654 rtRunJointControllers(currentPDOUpdateTimestamp, cappedDeltaT);
656 RunJointControllers, bus.getIterationCount(), 0.3 * loopDurationThreshold)
658 RunControllers, bus.getIterationCount(), 0.3 * loopDurationThreshold)
662 BUS_TIMING_START(rtBusSendReceive)
663 rtGetRTThreadTimingsSensorDevice().rtMarkRtBusSendReceiveStart();
664 if (bus.rtGetEtherCATState() == EtherCATState::op)
667 BUS_TIMING_START(getEmergencyStopState)
668 auto currentSoftwareEmergencyStopState = rtGetEmergencyStopState();
670 getEmergencyStopState, bus.getIterationCount(), 0.5 * loopDurationThreshold)
671 if (lastSoftwareEmergencyStopState ==
672 EmergencyStopState::eEmergencyStopActive &&
673 currentSoftwareEmergencyStopState ==
674 EmergencyStopState::eEmergencyStopInactive)
678 lastSoftwareEmergencyStopState = currentSoftwareEmergencyStopState;
680 BUS_TIMING_START(rtUpdateBus)
681 bus.rtUpdateBus(true, _iterationCount);
683 rtUpdateBus, bus.getIterationCount(), 0.6 * loopDurationThreshold)
684 if (bus.rtIsEmergencyStopActive() || bus.rtHasError())
686 rtSetEmergencyStopState(EmergencyStopState::eEmergencyStopActive);
689 currentPDOUpdateTimestamp = armarx::rtNow();
691 rtGetRTThreadTimingsSensorDevice().rtMarkRtBusSendReceiveEnd();
692 BUS_TIMING_CEND(rtBusSendReceive, bus.getIterationCount(), 0.55)
693 BUS_TIMING_START(ReadSensorValues)
694 rtReadSensorDeviceValues(currentPDOUpdateTimestamp, cappedDeltaT);
696 ReadSensorValues, bus.getIterationCount(), 0.7 * loopDurationThreshold)
697 lastMonoticTimestamp = currentMonotonicTimestamp;
698 currentMonotonicTimestamp = armarx::rtNow();
700 BUS_TIMING_START(Publish)
701 rtUpdateSensorAndControlBuffer(currentPDOUpdateTimestamp, cappedDeltaT);
702 BUS_TIMING_CEND(Publish, bus.getIterationCount(), 0.15 * loopDurationThreshold)
704 BUS_TIMING_START(ComputeGravityTorques)
705 gravity.computeGravityTorque(gravityValues);
707 for (auto& node : nodeJointDataVec)
709 auto torque = gravityValues.at(i);
712 node.second->gravityTorque = -torque;
718 ComputeGravityTorques, bus.getIterationCount(), 0.2 * loopDurationThreshold)
721 rtGetRTThreadTimingsSensorDevice().rtMarkRtLoopPreSleep();
723 const auto loopPreSleepTime = armarx::rtNow();
724 BUS_TIMING_START(RTLoopWaiting)
725 cycleKeeper.waitForCycleDuration();
726 BUS_TIMING_CEND(RTLoopWaiting, bus.getIterationCount(), loopDurationThreshold)
727 const auto loopPostSleepTime = armarx::rtNow();
729 const auto loopDuration = armarx::rtNow() - loopStartTime;
731 if (bus.rtGetFunctionalState() == BusFunctionalState::Running &&
732 loopDuration.toMicroSecondsDouble() >
733 (properties.rtLoopTimeInUS + 500) *
734 properties.rTLoopTimingCheckToleranceFactor)
736 const SensorValueRTThreadTimings* sval =
737 rtGetRTThreadTimingsSensorDevice()
739 ->asA<SensorValueRTThreadTimings>();
740 ARMARX_CHECK_NOT_NULL(sval);
742 bus.getIterationCount(),
743 "RT loop is under 1kHz control frequency: %.1f \n"
744 "-- cycle time PDO timestamp %.1f µs\n"
746 "-- thread timing sensor value: \n"
747 "---- rtSwitchControllerSetup Duration %.1f µs\t"
748 " RoundTripTime %.1f µs\n"
749 "---- rtRunNJointControllers Duration %.1f µs\t"
750 " RoundTripTime %.1f µs\n"
751 "---- rtHandleInvalidTargets Duration %.1f µs\t"
752 " RoundTripTime %.1f µs\n"
753 "---- rtRunJointControllers Duration %.1f µs\t"
754 " RoundTripTime %.1f µs\n"
755 "---- rtBusSendReceive Duration %.1f µs\t"
756 " RoundTripTime %.1f µs\n"
757 "---- rtReadSensorDeviceValues Duration %.1f µs\t"
758 " RoundTripTime %.1f µs\n"
759 "---- rtUpdateSensorAndControlBuffer Duration %.1f µs\t"
760 " RoundTripTime %.1f µs\n"
761 "---- rtResetAllTargets Duration %.1f µs\t"
762 " RoundTripTime %.1f µs\n"
763 "---- rtLoop Duration %.1f µs\t"
764 " RoundTripTime %.1f µs\n",
765 realDeltaT.toMicroSecondsDouble(),
766 loopDuration.toMicroSecondsDouble(),
767 (loopPostSleepTime - loopPreSleepTime).toMicroSecondsDouble(),
768 sval->rtSwitchControllerSetupDuration.toMicroSecondsDouble(),
769 sval->rtSwitchControllerSetupRoundTripTime.toMicroSecondsDouble(),
770 sval->rtRunNJointControllersDuration.toMicroSecondsDouble(),
771 sval->rtRunNJointControllersRoundTripTime.toMicroSecondsDouble(),
772 sval->rtHandleInvalidTargetsDuration.toMicroSecondsDouble(),
773 sval->rtHandleInvalidTargetsRoundTripTime.toMicroSecondsDouble(),
774 sval->rtRunJointControllersDuration.toMicroSecondsDouble(),
775 sval->rtRunJointControllersRoundTripTime.toMicroSecondsDouble(),
776 sval->rtBusSendReceiveDuration.toMicroSecondsDouble(),
777 sval->rtBusSendReceiveRoundTripTime.toMicroSecondsDouble(),
778 sval->rtReadSensorDeviceValuesDuration.toMicroSecondsDouble(),
779 sval->rtReadSensorDeviceValuesRoundTripTime.toMicroSecondsDouble(),
780 sval->rtUpdateSensorAndControlBufferDuration.toMicroSecondsDouble(),
781 sval->rtUpdateSensorAndControlBufferRoundTripTime.toMicroSecondsDouble(),
782 sval->rtResetAllTargetsDuration.toMicroSecondsDouble(),
783 sval->rtResetAllTargetsRoundTripTime.toMicroSecondsDouble(),
784 sval->rtLoopDuration.toMicroSecondsDouble(),
785 sval->rtLoopRoundTripTime.toMicroSecondsDouble());
788 ARMARX_IMPORTANT << "RT loop stopped";
789 ARMARX_INFO << "Execution stats: Average: "
790 << cycleKeeper.getAverageDuration().toMilliSecondsDouble()
791 << " max: " << cycleKeeper.getMaximumDuration().toMilliSecondsDouble()
792 << " min: " << cycleKeeper.getMinimumDuration().toMilliSecondsDouble();
793 //switching off the bus and be sure that the bus will receive
796 catch (UserException& e)
798 ARMARX_FATAL << "exception in control thread!"
800 << e.what() << "\nreason:\n"
801 << e.reason << "\n\tname: " << e.ice_id() << "\n\tfile: " << e.ice_file()
802 << "\n\tline: " << e.ice_line() << "\n\tstack: " << e.ice_stackTrace()
806 catch (Ice::Exception& e)
808 ARMARX_FATAL << "exception in control thread!\nwhat:\n"
809 << e.what() << "\n\tname: " << e.ice_id() << "\n\tfile: " << e.ice_file()
810 << "\n\tline: " << e.ice_line() << "\n\tstack: " << e.ice_stackTrace()
814 catch (std::exception& e)
816 ARMARX_FATAL << "exception in control thread!\nwhat:\n" << e.what() << std::flush;
821 ARMARX_FATAL << "exception in control thread!" << std::flush;
824 ARMARX_INFO << "Leaving control loop function";
828 RTUnit::startBackupLogging()
830 ARMARX_IMPORTANT << "startBackupLogging()";
832 if (properties.periodicLoggingMode == PeriodicLoggingMode::None)
834 ARMARX_IMPORTANT << "Periodic backup logging deactivated.";
838 backupLoggingTask = std::thread{[this]
842 backupLoggingRunning.store(true);
843 periodicBackupLoggingRun();
853 RTUnit::periodicBackupLoggingRun()
855 // This loop is responsible for keeping the last x minutes of Sensor- and ControlData and BusErrors in temporary files
856 // Each x minutes a new instance of rtLogging is started and therefore a new file generated.
857 // Similary each x minutes the BusErrors from that timeframe are dumped into a file.
861 std::filesystem::path rtLogPath;
862 std::filesystem::path busErrorLogPath;
865 std::queue<FilePaths> fileHistory;
866 IceUtil::Time lastIterationStart = armarx::rtNow();
868 std::string robotUnitLogPathFormat =
869 std::filesystem::path(properties.periodicLoggingDirectory)
870 .append("rt_log_{DateTime}.csv")
872 std::filesystem::path busErrorLogPath =
873 std::filesystem::path(properties.periodicLoggingDirectory).append("bus_errors.txt");
876 SimpleRemoteReferenceCounterBasePtr token;
877 while (backupLoggingRunning.load())
880 if (fileHistory.size() > properties.periodicLoggingHistorySize)
882 FilePaths p = fileHistory.front();
883 std::filesystem::remove(p.rtLogPath);
884 std::filesystem::remove(p.busErrorLogPath);
888 lastIterationStart = armarx::rtNow();
889 while (backupLoggingRunning.load() &&
892 IceUtil::Time::seconds(properties.periodicLoggingIntervalInSeconds))
894 if (!token && this->getRobotUnitState() == RobotUnitState::Running &&
895 (properties.periodicLoggingMode == PeriodicLoggingMode::OnlyRtLogging ||
896 properties.periodicLoggingMode == PeriodicLoggingMode::Both))
898 ARMARX_DEBUG << "Start new periodic log iteration";
899 token = this->startRtLogging(robotUnitLogPathFormat, {"*"});
901 std::this_thread::sleep_for(std::chrono::milliseconds(100));
906 std::string rtLogActualPath;
907 if (backupLoggingRunning.load() && token)
909 rtLogActualPath = token->getId();
910 this->stopRtLogging(token);
916 std::string busErrorLogActualPath;
917 busErrorLogActualPath = Bus::getBus().logErrorsToFile(
918 busErrorLogPath, properties.periodicLoggingIntervalInSeconds);
920 if (properties.periodicLoggingMode == PeriodicLoggingMode::OnlyRtLogging ||
921 properties.periodicLoggingMode == PeriodicLoggingMode::None)
923 std::filesystem::remove(busErrorLogActualPath);
926 if (busErrorLogActualPath.empty())
928 // No errors happend in the last interval therefore we can delete the
929 // rtLog as well to save disk space
930 std::filesystem::remove(rtLogActualPath);
934 fileHistory.push({rtLogActualPath, busErrorLogActualPath});
938 // Special case for last rtLog in case of shutdown
939 // This rt log gets always saved no matter if the corresponding bus error log is empty or not
940 // We have to check it by hand and delete if no bus errors happend
941 if (token && (fileHistory.empty() || fileHistory.back().rtLogPath != token->getId()))
943 std::filesystem::remove(token->getId());
946 } // 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