RTUnit.cpp
Go to the documentation of this file.
1/*
2 * This file is part of ArmarX.
3 *
4 * Copyright (C) 2011-2017, High Performance Humanoid Technologies (H2T), Karlsruhe Institute of Technology (KIT), all rights reserved.
5 *
6 * ArmarX is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 *
10 * ArmarX is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * @package ArmarX
19 * @author Mirko Waechter( mirko.waechter at kit dot edu)
20 * @date 2018
21 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
22 * GNU General Public License
23 */
24
25
26#include "RTUnit.h"
27
28#include <chrono>
29#include <filesystem>
30#include <sstream>
31#include <thread>
32
33#include <sys/mman.h>
34
35#include <VirtualRobot/Nodes/RobotNode.h>
36#include <VirtualRobot/RobotNodeSet.h>
37#include <VirtualRobot/Tools/Gravity.h>
38
46
51
58
60{
61 // The maximum stack size which is guaranteed safe to access without faulting
62 static constexpr unsigned int MAX_SAFE_STACK = (8 * 1024);
63
65 {
66 ;
67 }
68
69 void
71 {
73
74 ARMARX_INFO << "Locking memory";
75
76 if (mlockall(MCL_CURRENT | MCL_FUTURE) == -1)
77 {
78 ARMARX_WARNING << "Could not lock memory (mlockall failed)";
79 }
80
81 ARMARX_DEBUG << "Pre-fault our stack";
82 unsigned char dummy[MAX_SAFE_STACK];
83 memset(dummy, 0, MAX_SAFE_STACK);
84
85 ARMARX_INFO << "RTUnit initializing now";
86 robot = rtGetRobot()->clone();
87 ARMARX_CHECK_EXPRESSION(rtGetRobot()->hasRobotNodeSet(properties.robotJointsNodeSet));
88 ARMARX_CHECK_EXPRESSION(rtGetRobot()->hasRobotNodeSet(properties.robotColNodeSet));
89 rtRobotJointSet = rtGetRobot()->getRobotNodeSet(properties.robotJointsNodeSet);
90 rtRobotBodySet = rtGetRobot()->getRobotNodeSet(properties.robotColNodeSet);
93 for (auto& node : *rtRobotJointSet)
94 {
95 node->setEnforceJointLimits(false);
96 }
97 std::stringstream massesInfo;
98 for (int i = 0; i < static_cast<int>(rtRobotBodySet->getSize()); ++i)
99 {
100 auto node = rtRobotBodySet->getNode(i);
101 massesInfo << "\t" << node->getName() << ": " << node->getMass() << " kg\n";
102 }
103 ARMARX_DEBUG << "Masses info: " << massesInfo.str();
104
106 std::filesystem::path(properties.periodicLoggingDirectory).is_absolute());
107
108 //setting the bus
109 Bus& bus = Bus::getBus();
110 bus.setRobot(robot);
112 bus.setTimeouts(ethercatTimeouts); // Set configured timeouts.
113 }
114
115 void
117 {
118 ARMARX_INFO << "RTUnit connects now";
119
122 {
123 usleep(100'000);
124 }
125 }
126
127 void
129 {
130 ARMARX_INFO << "RTUnit disconnects now";
131 }
132
133 void
135 {
136 ARMARX_INFO << "RTUnit is exiting now ";
137
139
140 ARMARX_INFO << "RTUnit exit complete";
141 }
142
143 void
145 {
146 using IfaceT = KinematicUnitInterface;
147
148 auto guard = getGuard();
150 //check if unit is already added
151 if (getUnit(IfaceT::ice_staticId()))
152 {
153 return;
154 }
155
156 auto unit = createKinematicSubUnit(getIceProperties()->clone(),
157 ControlModes::Position1DoF,
158 properties.useTorqueVelocityModeAsDefault
159 ? ControlModes::VelocityTorque
160 : ControlModes::Velocity1DoF);
161 //add
162 if (unit)
163 {
164 addUnit(std::move(unit));
165 }
166 }
167
168 void
170 {
171 ARMARX_INFO << "starting control task";
172
173 if (rtTask.joinable())
174 {
175 rtTask.join();
176 }
177
178 rtTask = std::thread{[this]
179 {
180 taskRunning = true;
181 try
182 {
183 this->rtRun();
184 }
185 catch (...)
186 {
187 ARMARX_FATAL << "RT Thread caused an uncaught exception:\n"
189 }
190 }};
191 }
192
193 void
195 {
196 ARMARX_INFO << "stopping control task";
197 taskRunning = false;
198 rtTask.join();
199 ARMARX_INFO << "rt task stopped";
200 }
201
202 void
204 {
205 RobotUnit::publish(std::move(debugObserverMap), std::move(timingMap));
206 }
207
210 {
212
213 def->optional(
214 properties.busConfigFilePath, "BusConfigFilePath", "Location of the BusConfigFile");
215 def->optional(properties.socketFileDescriptor,
216 "SocketFileDescriptor",
217 "Socketfiledescriptor on which the ethercat connection is running");
218 def->optional(
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,
227 "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 "
232 "warning outputs");
233
234
235 // Periodic Logging
236 def->optional(properties.periodicLoggingDirectory,
237 "PeriodicLoggingDirectory",
238 "This absolute path to a directory is used for storing periodic log-files.");
239 def->optional(
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);
254
255
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 "
264 "all slaves.");
265 def->optional(properties.resetErrorRegistersAtStartup,
266 "ErrorCountersReading.ResetAtStartup",
267 "Whether to reset all error counters on all slaves after starting the bus.");
268
269 // node sets
270 def->optional(properties.robotJointsNodeSet,
271 "nodeSet.robotJoints",
272 "The node set which contains the actuated joints.");
273 def->optional(properties.robotColNodeSet,
274 "nodeSet.robotCol",
275 "The node set for e.g. gravity computation.");
276
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.");
289
290 return def;
291 }
292
294 {
295 ARMARX_INFO << "DTOR of RTUnit";
296
297 backupLoggingRunning.store(false);
298 if (backupLoggingTask.joinable())
299 {
300 backupLoggingTask.join();
301 }
302 }
303
304 void
306 {
307 ARMARX_INFO << "Control task running";
308 Bus& bus = Bus::getBus();
309
310 // Start periodic logging of rt-values and bus errors
312
314 {
315 ARMARX_INFO << "Leaving rtRun scope";
317 {
318 bus.shutdown();
319 }
320 backupLoggingRunning.store(false); // This will automatically dump the latest BusErrors
321 if (backupLoggingTask.joinable())
322 {
323 backupLoggingTask.join();
324 }
325 };
326
327 bool busStartSucceeded = false;
328
329
330 try
331 {
332 ARMARX_DEBUG << "initBus";
333 busStartSucceeded = initBusRTThread();
334 }
335 catch (...)
336 {
337 ARMARX_ERROR << "Initializing EtherCAT bus RT thread failed with exception: "
339 return;
340 }
341
342 ARMARX_INFO << "initBus finished with: " << busStartSucceeded;
343 if (!busStartSucceeded)
344 {
345 return;
346 }
347
348 bool initializationDone = false;
349 bool initializationFailed = false;
350 ARMARX_DEBUG << "Async init starting now";
351 std::thread unitInitTask = std::thread{
352 [&, this]
353 {
354 try
355 {
357 // rtReadSensorDeviceValues(TimeUtil::GetTime(), IceUtil::Time::microSeconds(1)); // initialize sensor values
359 ARMARX_IMPORTANT << "Setting up custom Units";
361 ARMARX_IMPORTANT << "Finishing setting up custom Units...DONE";
362 ARMARX_INFO << "Sleeping a moment to let everything settle in";
363 usleep(500000);
364 initializationDone = true;
365 }
366 catch (...)
367 {
369 << "Shutting down - RobotUnit Init Thread caused an uncaught exception:\n"
371 initializationFailed = true;
372 }
373 }};
374 unitInitTask.detach();
375 if (initializationFailed)
376 {
377 return;
378 }
379 if (busStartSucceeded)
380 {
381 //setting the timestamps for the pdo update, at the moment we only have on
383 CycleUtil cycleStats(IceUtil::Time::microSeconds(properties.rtLoopTimeInUS));
384 cycleStats.setBusyWaitShare(0.1f);
385 while (!initializationDone)
386 {
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!";
395 }
396 ARMARX_IMPORTANT << "RobotUnit is now ready";
397 }
398 else
399 {
400 ARMARX_WARNING << "Bus was not started!";
401 return;
402 }
403
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++)
407 {
408 auto selectedJoint = getSensorDevice(rtRobotJointSet->getNode(i)->getName());
409 if (selectedJoint)
410 {
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));
417 }
418 else
419 {
420 ARMARX_INFO << "Joint " << rtRobotJointSet->getNode(i)->getName() << " not found";
421 nodeJointDataVec.push_back(std::make_pair(rtRobotJointSet->getNode(i), nullptr));
422 }
423 }
424
426
427 while (getObjectScheduler() && !getObjectScheduler()->isTerminationRequested())
428 {
429 ARMARX_INFO << deactivateSpam() << "Waiting for shutdown";
430 usleep(10000);
431 }
432 }
433
435 RTUnit::ReadHardwareConfigFile(std::string busConfigFilePath, std::string robotName)
436 {
439 {
440 throw LocalException("could not find BusConfigFilePath: ") << busConfigFilePath;
441 }
442 ARMARX_CHECK(std::filesystem::is_regular_file(busConfigFilePath))
443 << "The bus config file `" << busConfigFilePath << "` does not exist!";
444
445 ARMARX_INFO_S << "Read the hw config from " << busConfigFilePath;
446
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();
449
451 ARMARX_INFO_S << "Reading file `" << busConfigFilePath << "`";
452 auto rapidXmlReaderPtr = RapidXmlReader::FromFile(busConfigFilePath);
453 ARMARX_CHECK_NOT_NULL(rapidXmlReaderPtr) << "Failed to read `" << busConfigFilePath << "`";
454
456 auto rootNode = rapidXmlReaderPtr->getRoot("HardwareConfig");
457 ARMARX_CHECK(rootNode.is_valid());
458 ARMARX_CHECK(rootNode.attribute_value("robotName") == robotName);
459
460 MultiNodeRapidXMLReader multiNode({rootNode});
461 for (RapidXmlReaderNode& includeNode : rootNode.nodes("include"))
462 {
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");
467
468 std::filesystem::path filepath = (busConfigFilePathDir / relPath);
469 if (!std::filesystem::exists(filepath))
470 {
471 std::string absPath;
472 if (!ArmarXDataPath::getAbsolutePath(relPath, absPath))
473 {
474 throw LocalException("Could not find config file at path ") << relPath;
475 }
476 }
477
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") ==
483 robotName);
484 multiNode.addNode(includedNode->getRoot("HardwareConfig"));
485 }
486 return multiNode;
487 }
488
489 bool
491 {
492 // init EtherCAT
493 Bus& bus = Bus::getBus();
494 bus.setSocketFileDescriptor(properties.socketFileDescriptor);
495 bus.setIfName(properties.ethercatInterfaceName);
496 if (!bus.switchBusToSafeOp())
497 {
498 ARMARX_ERROR << "Bringing bus to EtherCAT state 'safe-operational' failed.";
499 return false;
500 }
501
502 auto addDevices = [&](auto device)
503 {
504 auto cd = std::dynamic_pointer_cast<ControlDevice>(device);
505 if (cd)
506 {
508 }
509 auto sd = std::dynamic_pointer_cast<SensorDevice>(device);
510 if (sd)
511 {
512 addSensorDevice(sd);
513 }
514 };
515
516 for (auto& device : bus.getDevices())
517 {
518 addDevices(device);
519 for (const auto& subDevice : device->getSubDevices())
520 {
521 addDevices(subDevice);
522 }
523 }
524
525 if (properties.resetErrorRegistersAtStartup)
526 {
527 for (const auto& slave : bus.getSlaves())
528 {
530 static_cast<std::uint16_t>(slave->getSlaveIdentifier().slaveIndex));
531 }
532 }
533
534 if (properties.enableErrorCountersReading)
535 {
536 bus.configureErrorCountersReading(properties.enableErrorCountersReading,
537 properties.errorCountersReadingPeriodInMS);
538 // Add special slave error counters sensor devices
539 for (const auto& slave : bus.getSlaves())
540 {
541 auto sd = std::dynamic_pointer_cast<SensorDevice>(slave->getErrorRegistersDevice());
542 if (sd)
543 {
544 addSensorDevice(sd);
545 }
546 }
547 }
548
549 ARMARX_INFO << "EtherCAT bus is started";
550 return true;
551 }
552
553 void
555 {
556 Bus& bus = Bus::getBus();
557 try
558 {
560
564
565 //to not get any strange start values
567 CycleUtil cycleKeeper(IceUtil::Time::microSeconds(properties.rtLoopTimeInUS));
568 cycleKeeper.setBusyWaitShare(1.0f);
573 VirtualRobot::Gravity gravity(rtGetRobot(), rtRobotJointSet, rtRobotBodySet);
574
575 const std::uint64_t loopDurationThreshold =
576 properties.rtLoopTimeInUS / 1000 * properties.rTLoopTimingCheckToleranceFactor;
577
578 //#if 0
579 std::vector<float> gravityValues(rtRobotJointSet->getSize());
581 //#endif
582
583
584 if (!bus.switchBusToOp())
585 {
587 "Switching bus to EtherCAT state 'operational' failed");
588 }
589
590
591 ARMARX_INFO << "RT control loop started";
592 EmergencyStopState lastSoftwareEmergencyStopState = rtGetEmergencyStopState();
593
594 auto lastMonoticTimestamp = armarx::rtNow();
595 auto currentMonotonicTimestamp = lastMonoticTimestamp;
597
598 for (; taskRunning; ++_iterationCount)
599 {
600 const auto loopStartTime = armarx::rtNow();
602
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())
607 {
608 BUS_TIMING_START(calibrateActivateControlers)
610 BUS_TIMING_CEND(calibrateActivateControlers,
611 bus.getIterationCount(),
612 0.3 * loopDurationThreshold)
613
614 BUS_TIMING_START(switchControllerSetup)
617 switchControllerSetup, bus.getIterationCount(), 0.3 * loopDurationThreshold)
618
619 BUS_TIMING_START(resetAllTargets)
622 resetAllTargets, bus.getIterationCount(), 0.3 * loopDurationThreshold)
623
624 BUS_TIMING_START(calibrateInRt)
625 _calibrationStatus = rtCalibrate();
627 calibrateInRt, bus.getIterationCount(), 0.3 * loopDurationThreshold)
628
629 BUS_TIMING_START(handleInvalidTargets)
632 handleInvalidTargets, bus.getIterationCount(), 0.3 * loopDurationThreshold)
633
634 BUS_TIMING_START(runJointControllers)
637 runJointControllers, bus.getIterationCount(), 0.3 * loopDurationThreshold)
638 }
639 else
640 {
641 BUS_TIMING_START(RunControllers)
642 BUS_TIMING_START(SwitchControllers)
645 SwitchControllers, bus.getIterationCount(), 0.3 * loopDurationThreshold)
646 BUS_TIMING_START(ResettingTargets)
649 ResettingTargets, bus.getIterationCount(), 0.3 * loopDurationThreshold)
650 BUS_TIMING_START(RunNJointControllers)
653 RunNJointControllers, bus.getIterationCount(), 0.3 * loopDurationThreshold)
654 BUS_TIMING_START(CheckInvalidTargets)
657 CheckInvalidTargets, bus.getIterationCount(), 0.3 * loopDurationThreshold)
658
659 BUS_TIMING_START(RunJointControllers)
662 RunJointControllers, bus.getIterationCount(), 0.3 * loopDurationThreshold)
664 RunControllers, bus.getIterationCount(), 0.3 * loopDurationThreshold)
665 }
666
667 //bus update
668 BUS_TIMING_START(rtBusSendReceive)
671 {
672 // error correction
674 auto currentSoftwareEmergencyStopState = rtGetEmergencyStopState();
676 getEmergencyStopState, bus.getIterationCount(), 0.5 * loopDurationThreshold)
677 if (lastSoftwareEmergencyStopState ==
678 EmergencyStopState::eEmergencyStopActive &&
679 currentSoftwareEmergencyStopState ==
680 EmergencyStopState::eEmergencyStopInactive)
681 {
682 // bus.rebootBus();
683 }
684 lastSoftwareEmergencyStopState = currentSoftwareEmergencyStopState;
685
686 BUS_TIMING_START(rtUpdateBus)
687 bus.rtUpdateBus(true, _iterationCount);
689 rtUpdateBus, bus.getIterationCount(), 0.6 * loopDurationThreshold)
690 if (bus.rtIsEmergencyStopActive() || bus.rtHasError())
691 {
692 rtSetEmergencyStopState(EmergencyStopState::eEmergencyStopActive);
693 }
694 }
696
698 BUS_TIMING_CEND(rtBusSendReceive, bus.getIterationCount(), 0.55)
699 BUS_TIMING_START(ReadSensorValues)
702 ReadSensorValues, bus.getIterationCount(), 0.7 * loopDurationThreshold)
703 lastMonoticTimestamp = currentMonotonicTimestamp;
704 currentMonotonicTimestamp = armarx::rtNow();
705
706 BUS_TIMING_START(Publish)
708 BUS_TIMING_CEND(Publish, bus.getIterationCount(), 0.15 * loopDurationThreshold)
709
710 BUS_TIMING_START(ComputeGravityTorques)
711 gravity.computeGravityTorque(gravityValues);
712 size_t i = 0;
713 for (auto& node : nodeJointDataVec)
714 {
715 auto torque = gravityValues.at(i);
716 if (node.second)
717 {
718 node.second->gravityTorque = -torque;
719 }
720
721 i++;
722 }
724 ComputeGravityTorques, bus.getIterationCount(), 0.2 * loopDurationThreshold)
725
726
728
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();
734
735 const auto loopDuration = armarx::rtNow() - loopStartTime;
736
738 loopDuration.toMicroSecondsDouble() >
739 (properties.rtLoopTimeInUS + 500) *
740 properties.rTLoopTimingCheckToleranceFactor)
741 {
742 const SensorValueRTThreadTimings* sval =
748 bus.getIterationCount(),
749 "RT loop is under 1kHz control frequency: %.1f \n"
750 "-- cycle time PDO timestamp %.1f µs\n"
751 "-- sleep %.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());
792 }
793 }
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
800 bus.shutdown();
801 }
802 catch (UserException& e)
803 {
804 ARMARX_FATAL << "exception in control thread!"
805 << "\nwhat:\n"
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()
809 << std::flush;
810 bus.shutdown();
811 }
812 catch (Ice::Exception& e)
813 {
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()
817 << std::flush;
818 bus.shutdown();
819 }
820 catch (std::exception& e)
821 {
822 ARMARX_FATAL << "exception in control thread!\nwhat:\n" << e.what() << std::flush;
823 bus.shutdown();
824 }
825 catch (...)
826 {
827 ARMARX_FATAL << "exception in control thread!" << std::flush;
828 bus.shutdown();
829 }
830 ARMARX_INFO << "Leaving control loop function";
831 }
832
833 void
835 {
836 ARMARX_IMPORTANT << "startBackupLogging()";
837
838 if (properties.periodicLoggingMode == PeriodicLoggingMode::None)
839 {
840 ARMARX_IMPORTANT << "Periodic backup logging deactivated.";
841 return;
842 }
843
844 backupLoggingTask = std::thread{[this]
845 {
846 try
847 {
848 backupLoggingRunning.store(true);
850 }
851 catch (...)
852 {
854 }
855 }};
856 }
857
858 void
860 {
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.
864
865 struct FilePaths
866 {
867 std::filesystem::path rtLogPath;
868 std::filesystem::path busErrorLogPath;
869 };
870
871 std::queue<FilePaths> fileHistory;
872 IceUtil::Time lastIterationStart = armarx::rtNow();
873
874 std::string robotUnitLogPathFormat =
875 std::filesystem::path(properties.periodicLoggingDirectory)
876 .append("rt_log_{DateTime}.csv")
877 .string();
878 std::filesystem::path busErrorLogPath =
879 std::filesystem::path(properties.periodicLoggingDirectory).append("bus_errors.txt");
880
881
882 SimpleRemoteReferenceCounterBasePtr token;
883 while (backupLoggingRunning.load())
884 {
886 if (fileHistory.size() > properties.periodicLoggingHistorySize)
887 {
888 FilePaths p = fileHistory.front();
889 std::filesystem::remove(p.rtLogPath);
890 std::filesystem::remove(p.busErrorLogPath);
891 fileHistory.pop();
892 }
893
894 lastIterationStart = armarx::rtNow();
895 while (backupLoggingRunning.load() &&
896 armarx::rtNow() <
897 lastIterationStart +
898 IceUtil::Time::seconds(properties.periodicLoggingIntervalInSeconds))
899 {
900 if (!token && this->getRobotUnitState() == RobotUnitState::Running &&
901 (properties.periodicLoggingMode == PeriodicLoggingMode::OnlyRtLogging ||
902 properties.periodicLoggingMode == PeriodicLoggingMode::Both))
903 {
904 ARMARX_DEBUG << "Start new periodic log iteration";
905 token = this->startRtLogging(robotUnitLogPathFormat, {"*"});
906 }
907 std::this_thread::sleep_for(std::chrono::milliseconds(100));
908 }
909
911
912 std::string rtLogActualPath;
913 if (backupLoggingRunning.load() && token)
914 {
915 rtLogActualPath = token->getId();
916 this->stopRtLogging(token);
917 token = nullptr;
918 }
919
921
922 std::string busErrorLogActualPath;
923 busErrorLogActualPath = Bus::getBus().logErrorsToFile(
924 busErrorLogPath, properties.periodicLoggingIntervalInSeconds);
925
926 if (properties.periodicLoggingMode == PeriodicLoggingMode::OnlyRtLogging ||
927 properties.periodicLoggingMode == PeriodicLoggingMode::None)
928 {
929 std::filesystem::remove(busErrorLogActualPath);
930 }
931
932 if (busErrorLogActualPath.empty())
933 {
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);
937 }
938 else
939 {
940 fileHistory.push({rtLogActualPath, busErrorLogActualPath});
941 }
942 }
943
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()))
948 {
949 std::filesystem::remove(token->getId());
950 }
951 }
952} // namespace armarx::control::ethercat
#define BUS_WARNING(bin,...)
#define BUS_FATAL_AND_THROW(bin,...)
#define BUS_TIMING_CEND(name, bin, thresholdMs)
Definition Timing.h:181
#define BUS_TIMING_START(name)
Definition Timing.h:163
static bool getAbsolutePath(const std::string &relativeFilename, std::string &storeAbsoluteFilename, const std::vector< std::string > &additionalSearchPaths={}, bool verbose=true)
This util class helps with keeping a cycle time during a control cycle.
Definition CycleUtil.h:41
IceUtil::Time getMaximumDuration() const
IceUtil::Time waitForCycleDuration()
This function will wait (virtual or system time) until the cycle time is reached.
Definition CycleUtil.cpp:53
IceUtil::Time getAverageDuration() const
void setBusyWaitShare(float value)
IceUtil::Time getMinimumDuration() const
SpamFilterDataPtr deactivateSpam(float deactivationDurationSec=10.0f, const std::string &identifier="", bool deactivate=true) const
disables the logging for the current line for the given amount of seconds.
Definition Logging.cpp:99
void setLocalMinimumLoggingLevel(MessageTypeT level)
With setLocalMinimumLoggingLevel the minimum verbosity-level of log-messages can be set.
Definition Logging.cpp:66
MessageTypeT getEffectiveLoggingLevel() const
Definition Logging.cpp:130
ArmarXObjectSchedulerPtr getObjectScheduler() const
Ice::PropertiesPtr getIceProperties() const
Returns the set of Ice properties.
virtual void rtMarkRtBusSendReceiveStart()=0
virtual void rtMarkRtBusSendReceiveEnd()=0
const SensorValueBase * getSensorValue() const override=0
std::vector< RapidXmlReaderNode > nodes(const char *name=nullptr) const
static RapidXmlReaderPtr FromFile(const std::string &path)
@ RTThread
This modus is used if the RTThread is supported to control which controllers are running.
EmergencyStopState getEmergencyStopState() const
Returns the ControlThread's target EmergencyStopState.
void rtResetAllTargets()
Calls rtResetTarget for all active Joint controllers.
void rtUpdateSensorAndControlBuffer(const IceUtil::Time &sensorValuesTimestamp, const IceUtil::Time &timeSinceLastIteration)
Updates the current SensorValues and ControlTargets for code running outside of the ControlThread.
void rtHandleInvalidTargets()
Deactivates all NJointControllers generating invalid targets.
EmergencyStopState rtGetEmergencyStopState() const
Returns the ControlThread's emergency stop state.
bool rtSwitchControllerSetup(SwitchControllerMode mode=SwitchControllerMode::IceRequests)
Changes the set of active controllers.
const VirtualRobot::RobotPtr & rtGetRobot() const
Returns the simox robot used in the control thread.
void rtSetEmergencyStopState(EmergencyStopState state)
Set an EmergencyStopState request.
void rtRunNJointControllers(const IceUtil::Time &sensorValuesTimestamp, const IceUtil::Time &timeSinceLastIteration)
Runs NJoint controllers.
void rtRunJointControllers(const IceUtil::Time &sensorValuesTimestamp, const IceUtil::Time &timeSinceLastIteration)
Runs Joint controllers and writes target values to ControlDevices.
void rtReadSensorDeviceValues(const IceUtil::Time &sensorValuesTimestamp, const IceUtil::Time &timeSinceLastIteration)
Calls rtReadSensorValues for all SensorDevices.
void addSensorDevice(const SensorDevicePtr &sd)
Adds a SensorDevice to the robot.
void addControlDevice(const ControlDevicePtr &cd)
Adds a ControlDevice to the robot.
RTThreadTimingsSensorDevice & rtGetRTThreadTimingsSensorDevice()
Returns the SensorDevice used to log timings in the ControlThread.
ConstSensorDevicePtr getSensorDevice(const std::string &deviceName) const
TODO move to attorney for NJointControllerBase.
SimpleRemoteReferenceCounterBasePtr startRtLogging(const std::string &formatString, const Ice::StringSeq &loggingNames, const Ice::Current &=Ice::emptyCurrent) override
Starts logging to a CSV file.
void stopRtLogging(const armarx::SimpleRemoteReferenceCounterBasePtr &token, const Ice::Current &=Ice::emptyCurrent) override
Stops logging to the given log.
virtual void finishDeviceInitialization()
Transition InitializingDevices -> InitializingUnits.
RobotUnitState getRobotUnitState() const
Returns the RobotUnit's State.
virtual void finishUnitInitialization()
Transition InitializingUnits -> RobotUnitState::WaitingForRTThreadInitialization.
GuardType getGuard() const
Returns a guard to the RobotUnits mutex.
virtual void finishControlThreadInitialization()
Transition InitializingControlThread -> Running.
void throwIfStateIsNot(const std::set< RobotUnitState > &stateSet, const std::string &fnc, bool onlyWarn=false) const
Throws an exception if the current state is not in.
virtual void publish(StringVariantBaseMap additionalMap={}, StringVariantBaseMap timingMap={})
Publishes data.
T::PointerType getUnit() const
Returns a pointer to the Unit for the given type (or null if there is none)
void addUnit(ManagedIceObjectPtr unit)
Registers a unit for management (the Unit is added and removed to the ArmarXManager and updated with ...
virtual void initializeDefaultUnits()
Calls all init hooks for all managed Units.
ManagedIceObjectPtr createKinematicSubUnit(const Ice::PropertiesPtr &properties, const std::string &positionControlMode=ControlModes::Position1DoF, const std::string &velocityControlMode=ControlModes::Velocity1DoF, const std::string &torqueControlMode=ControlModes::Torque1DoF, const std::string &pwmControlMode=ControlModes::PWM1DoF)
Create the KinematicUnit (this function should be called in initializeKinematicUnit)
armarx::PropertyDefinitionsPtr createPropertyDefinitions() override
Definition RobotUnit.cpp:33
const T * asA() const
DETAIL_SensorValueBase_DEFAULT_METHOD_IMPLEMENTATION IceUtil::Time rtSwitchControllerSetupDuration
void setTimeouts(Timeouts const &timeouts)
Definition BusIO.cpp:470
bool resetErrorRegisters(std::uint16_t slaveIndex)
Definition BusIO.cpp:433
Brief description of class Bus.
Definition Bus.h:57
EtherCATState rtGetEtherCATState() const
Definition Bus.cpp:1360
void setIfName(const std::string &ifname)
Definition Bus.cpp:468
void configureErrorCountersReading(bool enable, unsigned int periodInMS)
configureErrorCountersReading configure stuff
Definition Bus.cpp:1462
std::uint64_t getIterationCount() const
Definition Bus.cpp:1486
bool rtIsEmergencyStopActive() const
Definition Bus.cpp:1428
std::vector< std::experimental::observer_ptr< SlaveInterface > > getSlaves() const
Returns all identifiied slaves on the bus.
Definition Bus.cpp:1392
BusFunctionalState rtGetFunctionalState() const
Definition Bus.cpp:1366
const std::vector< std::shared_ptr< DeviceInterface > > & getDevices() const
Returns all initialized devices.
Definition Bus.cpp:1404
std::string logErrorsToFile(std::filesystem::path path, unsigned int lastSeconds)
Definition Bus.cpp:36
void shutdown()
Shuts the bus down by executing the shutdown-hook for each slave and afterwards switch all slaves to ...
Definition Bus.cpp:593
static Bus & getBus()
This returns the one and only Bus object.
Definition Bus.cpp:29
void setRobot(const VirtualRobot::RobotPtr &robot)
Definition Bus.cpp:1456
void setSocketFileDescriptor(int socketFileDescriptor)
Definition Bus.cpp:462
bool rtUpdateBus(bool doErrorHandling=true, size_t iterationCount=0)
Updates all information on the bus, so all commands will be send to the Bus and all sensor and other ...
Definition Bus.cpp:474
@ init
Initial state after switch a EtherCAT slave on.
std::atomic_bool taskRunning
Definition RTUnit.h:163
void joinControlThread() override
Implementations have to join their ControlThread in this hook. (used by RobotUnit::finishRunning())
Definition RTUnit.cpp:194
void publish(armarx::StringVariantBaseMap debugObserverMap={}, armarx::StringVariantBaseMap timingMap={}) override
Publishes data.
Definition RTUnit.cpp:203
VirtualRobot::RobotPtr robot
Definition RTUnit.h:168
VirtualRobot::RobotNodeSetPtr rtRobotJointSet
Definition RTUnit.h:170
VirtualRobot::RobotNodeSetPtr rtRobotBodySet
Definition RTUnit.h:170
struct armarx::control::ethercat::RTUnit::@220041145354046113147030301166073356361324314346 properties
std::atomic_bool backupLoggingRunning
Definition RTUnit.h:183
std::vector< std::pair< VirtualRobot::RobotNodePtr, SensorValue1DoFGravityTorque * > > nodeJointDataVec
Definition RTUnit.h:172
armarx::PropertyDefinitionsPtr createPropertyDefinitions() override
Definition RTUnit.cpp:209
void onExitRobotUnit() override
called in onExitComponent before calling finishRunning
Definition RTUnit.cpp:134
armarx::control::ethercat::Timeouts ethercatTimeouts
Definition RTUnit.h:215
static MultiNodeRapidXMLReader ReadHardwareConfigFile(std::string hardwareConfigFilePath, std::string rootNodeName)
Definition RTUnit.cpp:435
void onConnectRobotUnit() override
called in onConnectComponent
Definition RTUnit.cpp:116
virtual CalibrationStatus rtCalibrate()
Hook to add calibration code.
Definition RTUnit.h:138
virtual void rtCalibrateActivateControlers()
Allows to switch controllers while calibrating.
Definition RTUnit.h:122
IceUtil::Time currentPDOUpdateTimestamp
Definition RTUnit.h:166
virtual void rtRun()
the run method of the rt thread
Definition RTUnit.cpp:305
void initializeKinematicUnit() override
Initializes the KinematicUnit.
Definition RTUnit.cpp:144
void onInitRobotUnit() override
called in onInitComponent
Definition RTUnit.cpp:70
void onDisconnectRobotUnit() override
called in onDisconnecComponent
Definition RTUnit.cpp:128
static bool pinThreadToCPU(unsigned int cpu)
Pins the calling thread to the CPU with the given id.
Definition RTUtility.cpp:52
static bool stopLowLatencyMode()
Deactivate low latency mode of the system.
static bool elevateThreadPriority(int priority)
Elevate the thread priority of the calling thread to the given priority.
Definition RTUtility.cpp:17
static constexpr int RT_THREAD_PRIORITY
The thread priority for rt-threads.
Definition RTUtility.h:24
static bool startLowLatencyMode()
Activate low latency mode of the system.
Definition RTUtility.cpp:94
#define ARMARX_CHECK_EXPRESSION(expression)
This macro evaluates the expression and if it turns out to be false it will throw an ExpressionExcept...
#define ARMARX_CHECK(expression)
Shortcut for ARMARX_CHECK_EXPRESSION.
#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...
#define ARMARX_CHECK_EQUAL(lhs, rhs)
This macro evaluates whether lhs is equal (==) rhs and if it turns out to be false it will throw an E...
#define ARMARX_INFO
The normal logging level.
Definition Logging.h:181
#define ARMARX_IMPORTANT
The logging level for always important information, but expected behaviour (in contrast to ARMARX_WAR...
Definition Logging.h:190
#define ARMARX_FATAL
The logging level for unexpected behaviour, that will lead to a seriously malfunctioning program and ...
Definition Logging.h:199
#define ARMARX_ERROR
The logging level for unexpected behaviour, that must be fixed.
Definition Logging.h:196
#define ARMARX_INFO_S
Definition Logging.h:202
#define ARMARX_DEBUG
The logging level for output that is only interesting while debugging.
Definition Logging.h:184
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
Definition Logging.h:193
#define ARMARX_ON_SCOPE_EXIT
Executes given code when the enclosing scope is left.
std::map< std::string, VariantBasePtr > StringVariantBaseMap
std::string GetHandledExceptionString()
void handleExceptions()
IceUtil::Time rtNow()
Definition RtTiming.h:40
IceUtil::Handle< class PropertyDefinitionContainer > PropertyDefinitionsPtr
PropertyDefinitions smart pointer type.
#define ARMARX_TRACE
Definition trace.h:77