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());
146 using IfaceT = KinematicUnitInterface;
151 if (
getUnit(IfaceT::ice_staticId()))
157 ControlModes::Position1DoF,
159 ? ControlModes::VelocityTorque
160 : ControlModes::Velocity1DoF);
178 rtTask = std::thread{[
this]
187 ARMARX_FATAL <<
"RT Thread caused an uncaught exception:\n"
214 properties.busConfigFilePath,
"BusConfigFilePath",
"Location of the BusConfigFile");
215 def->optional(
properties.socketFileDescriptor,
216 "SocketFileDescriptor",
217 "Socketfiledescriptor on which the ethercat connection is running");
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");
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.");
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");
248 "PeriodicLoggingMode",
249 "Select in which mode the periodic logging should be executed.")
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.");
271 "nodeSet.robotJoints",
272 "The node set which contains the actuated joints.");
275 "The node set for e.g. gravity computation.");
278 "ethercat.timeouts.register_read",
279 "EtherCAT timeout for a register frame to read certain registers in µs.");
281 "ethercat.timeouts.state_check",
282 "EtherCAT timeout to check for the current EtherCAT state of slaves in µs.");
284 "ethercat.timeouts.sdo_read",
285 "EtherCAT timeout to read an SDO in µs.");
287 "ethercat.timeouts.sdo_write",
288 "EtherCAT timeout to write an SDO in µs.");
327 bool busStartSucceeded =
false;
337 ARMARX_ERROR <<
"Initializing EtherCAT bus RT thread failed with exception: "
342 ARMARX_INFO <<
"initBus finished with: " << busStartSucceeded;
343 if (!busStartSucceeded)
348 bool initializationDone =
false;
349 bool initializationFailed =
false;
351 std::thread unitInitTask = std::thread{
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"
371 initializationFailed =
true;
374 unitInitTask.detach();
375 if (initializationFailed)
379 if (busStartSucceeded)
385 while (!initializationDone)
411 auto sensorValue =
const_cast<SensorValue1DoFGravityTorque*
>(
412 selectedJoint->getSensorValue()->asA<SensorValue1DoFGravityTorque>());
413 ARMARX_DEBUG <<
"will calculate gravity for robot node "
448 auto busConfigFilePathDir = std::filesystem::path(
busConfigFilePath).parent_path();
456 auto rootNode = rapidXmlReaderPtr->getRoot(
"HardwareConfig");
458 ARMARX_CHECK(rootNode.attribute_value(
"robotName") == robotName);
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))
474 throw LocalException(
"Could not find config file at path ") << relPath;
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"));
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);
509 auto sd = std::dynamic_pointer_cast<SensorDevice>(device);
519 for (
const auto& subDevice : device->getSubDevices())
521 addDevices(subDevice);
527 for (
const auto& slave : bus.
getSlaves())
530 static_cast<std::uint16_t
>(slave->getSlaveIdentifier().slaveIndex));
539 for (
const auto& slave : bus.
getSlaves())
541 auto sd = std::dynamic_pointer_cast<SensorDevice>(slave->getErrorRegistersDevice());
575 const std::uint64_t loopDurationThreshold =
587 "Switching bus to EtherCAT state 'operational' failed");
595 auto currentMonotonicTimestamp = lastMonoticTimestamp;
603 auto realDeltaT = currentMonotonicTimestamp - lastMonoticTimestamp;
604 auto cappedDeltaT = IceUtil::Time::microSeconds(
605 std::clamp<long>(realDeltaT.toMicroSeconds(), 1, 2000));
612 0.3 * loopDurationThreshold)
677 if (lastSoftwareEmergencyStopState ==
678 EmergencyStopState::eEmergencyStopActive &&
679 currentSoftwareEmergencyStopState ==
680 EmergencyStopState::eEmergencyStopInactive)
684 lastSoftwareEmergencyStopState = currentSoftwareEmergencyStopState;
703 lastMonoticTimestamp = currentMonotonicTimestamp;
711 gravity.computeGravityTorque(gravityValues);
715 auto torque = gravityValues.at(i);
718 node.second->gravityTorque = -torque;
738 loopDuration.toMicroSecondsDouble() >
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(),
802 catch (UserException& e)
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)
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;
867 std::filesystem::path rtLogPath;
868 std::filesystem::path busErrorLogPath;
871 std::queue<FilePaths> fileHistory;
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;
886 if (fileHistory.size() >
properties.periodicLoggingHistorySize)
888 FilePaths p = fileHistory.front();
889 std::filesystem::remove(p.rtLogPath);
890 std::filesystem::remove(p.busErrorLogPath);
898 IceUtil::Time::seconds(
properties.periodicLoggingIntervalInSeconds))
907 std::this_thread::sleep_for(std::chrono::milliseconds(100));
912 std::string rtLogActualPath;
915 rtLogActualPath = token->getId();
922 std::string busErrorLogActualPath;
924 busErrorLogPath,
properties.periodicLoggingIntervalInSeconds);
929 std::filesystem::remove(busErrorLogActualPath);
932 if (busErrorLogActualPath.empty())
936 std::filesystem::remove(rtLogActualPath);
940 fileHistory.push({rtLogActualPath, busErrorLogActualPath});
947 if (token && (fileHistory.empty() || fileHistory.back().rtLogPath != token->getId()))
949 std::filesystem::remove(token->getId());
#define BUS_WARNING(bin,...)
#define BUS_FATAL_AND_THROW(bin,...)
#define BUS_TIMING_CEND(name, bin, thresholdMs)
#define BUS_TIMING_START(name)
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.
IceUtil::Time getMaximumDuration() const
IceUtil::Time waitForCycleDuration()
This function will wait (virtual or system time) until the cycle time is reached.
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.
void setLocalMinimumLoggingLevel(MessageTypeT level)
With setLocalMinimumLoggingLevel the minimum verbosity-level of log-messages can be set.
MessageTypeT getEffectiveLoggingLevel() const
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
virtual void rtMarkRtLoopPreSleep()=0
virtual void rtMarkRtLoopStart()=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
IceUtil::Time rtLoopDuration
IceUtil::Time rtReadSensorDeviceValuesRoundTripTime
IceUtil::Time rtRunNJointControllersRoundTripTime
IceUtil::Time rtHandleInvalidTargetsDuration
IceUtil::Time rtResetAllTargetsDuration
IceUtil::Time rtHandleInvalidTargetsRoundTripTime
IceUtil::Time rtRunJointControllersRoundTripTime
DETAIL_SensorValueBase_DEFAULT_METHOD_IMPLEMENTATION IceUtil::Time rtSwitchControllerSetupDuration
IceUtil::Time rtBusSendReceiveDuration
IceUtil::Time rtRunJointControllersDuration
IceUtil::Time rtUpdateSensorAndControlBufferRoundTripTime
IceUtil::Time rtSwitchControllerSetupRoundTripTime
IceUtil::Time rtRunNJointControllersDuration
IceUtil::Time rtLoopRoundTripTime
IceUtil::Time rtUpdateSensorAndControlBufferDuration
IceUtil::Time rtReadSensorDeviceValuesDuration
IceUtil::Time rtBusSendReceiveRoundTripTime
IceUtil::Time rtResetAllTargetsRoundTripTime
void setTimeouts(Timeouts const &timeouts)
bool resetErrorRegisters(std::uint16_t slaveIndex)
Brief description of class Bus.
EtherCATState rtGetEtherCATState() const
void setIfName(const std::string &ifname)
void configureErrorCountersReading(bool enable, unsigned int periodInMS)
configureErrorCountersReading configure stuff
std::uint64_t getIterationCount() const
bool rtIsEmergencyStopActive() const
std::vector< std::experimental::observer_ptr< SlaveInterface > > getSlaves() const
Returns all identifiied slaves on the bus.
BusFunctionalState rtGetFunctionalState() const
const std::vector< std::shared_ptr< DeviceInterface > > & getDevices() const
Returns all initialized devices.
std::string logErrorsToFile(std::filesystem::path path, unsigned int lastSeconds)
void shutdown()
Shuts the bus down by executing the shutdown-hook for each slave and afterwards switch all slaves to ...
static Bus & getBus()
This returns the one and only Bus object.
void setRobot(const VirtualRobot::RobotPtr &robot)
void setSocketFileDescriptor(int socketFileDescriptor)
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 ...
@ init
Initial state after switch a EtherCAT slave on.
std::atomic_bool taskRunning
void joinControlThread() override
Implementations have to join their ControlThread in this hook. (used by RobotUnit::finishRunning())
void publish(armarx::StringVariantBaseMap debugObserverMap={}, armarx::StringVariantBaseMap timingMap={}) override
Publishes data.
VirtualRobot::RobotPtr robot
VirtualRobot::RobotNodeSetPtr rtRobotJointSet
void startBackupLogging()
std::thread backupLoggingTask
VirtualRobot::RobotNodeSetPtr rtRobotBodySet
struct armarx::control::ethercat::RTUnit::@220041145354046113147030301166073356361324314346 properties
std::atomic_bool backupLoggingRunning
std::vector< std::pair< VirtualRobot::RobotNodePtr, SensorValue1DoFGravityTorque * > > nodeJointDataVec
armarx::PropertyDefinitionsPtr createPropertyDefinitions() override
void onExitRobotUnit() override
called in onExitComponent before calling finishRunning
armarx::control::ethercat::Timeouts ethercatTimeouts
static MultiNodeRapidXMLReader ReadHardwareConfigFile(std::string hardwareConfigFilePath, std::string rootNodeName)
void controlLoopRTThread()
void onConnectRobotUnit() override
called in onConnectComponent
virtual CalibrationStatus rtCalibrate()
Hook to add calibration code.
bool rtIsCalibrating() const
std::string busConfigFilePath
virtual void rtCalibrateActivateControlers()
Allows to switch controllers while calibrating.
IceUtil::Time currentPDOUpdateTimestamp
virtual void rtRun()
the run method of the rt thread
void initializeKinematicUnit() override
Initializes the KinematicUnit.
void onInitRobotUnit() override
called in onInitComponent
void onDisconnectRobotUnit() override
called in onDisconnecComponent
void periodicBackupLoggingRun()
static bool pinThreadToCPU(unsigned int cpu)
Pins the calling thread to the CPU with the given id.
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.
static constexpr int RT_THREAD_PRIORITY
The thread priority for rt-threads.
static bool startLowLatencyMode()
Activate low latency mode of the system.
#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.
#define ARMARX_IMPORTANT
The logging level for always important information, but expected behaviour (in contrast to ARMARX_WAR...
#define ARMARX_FATAL
The logging level for unexpected behaviour, that will lead to a seriously malfunctioning program and ...
#define ARMARX_ERROR
The logging level for unexpected behaviour, that must be fixed.
#define ARMARX_DEBUG
The logging level for output that is only interesting while debugging.
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
#define ARMARX_ON_SCOPE_EXIT
Executes given code when the enclosing scope is left.
std::map< std::string, VariantBasePtr > StringVariantBaseMap
std::string GetHandledExceptionString()
IceUtil::Handle< class PropertyDefinitionContainer > PropertyDefinitionsPtr
PropertyDefinitions smart pointer type.