29#include <boost/regex.hpp>
31#include <SimoxUtility/algorithm/get_map_keys_values.h>
32#include <SimoxUtility/algorithm/string/string_tools.h>
50 monitorUpdateHealthTask =
53 defaultMaximumCycleTimeWarn =
55 defaultMaximumCycleTimeErr =
58 const std::vector<std::string> requiredTags = simox::alg::split(p.requiredTags,
",");
61 std::set<std::string>(requiredTags.begin(), requiredTags.end());
71 monitorUpdateHealthTask->start();
75 RobotHealth::monitorHealthUpdateTaskClb()
77 const std::scoped_lock lock(updateMutex);
82 for (
auto& e : updateEntries)
85 const std::scoped_lock elock(e.mutex);
92 if (e.history.empty())
97 auto deltaToArrival = now - e.history.back().arrivalTime;
101 if (deltaToArrival > e.maximumCycleTimeErr)
105 if (e.state != HealthError)
110 e.state = HealthError;
113 else if (deltaToArrival > e.maximumCycleTimeWarn)
117 <<
" is running too slow.";
119 e.state = HealthWarning;
129 RobotHealthState overallHealthState = RobotHealthState::HealthOK;
133 for (
auto& e : updateEntries)
136 std::scoped_lock elock(e.mutex);
139 while (e.history.size() > 20)
141 e.history.pop_front();
146 if (e.state == HealthWarning && overallHealthState != HealthError)
148 overallHealthState = RobotHealthState::HealthWarning;
150 else if (e.state == HealthError)
152 overallHealthState = RobotHealthState::HealthError;
157 if (e.state != HealthOK && overallHealthState != HealthError)
159 overallHealthState = RobotHealthState::HealthWarning;
164 if (overallHealthState == HealthError)
169 p.emergencyStopTopicPrx->reportEmergencyStopState(
170 EmergencyStopState::eEmergencyStopActive);
174 p.aggregatedRobotHealthTopicPrx->aggregatedHeartbeat(overallHealthState);
176 reportDebugObserver();
183 monitorUpdateHealthTask->stop();
198 defs->topic(p.emergencyStopTopicPrx,
200 "The name of the topic over which changes of the emergencyStopState are sent.");
201 defs->topic<RobotHealthInterfacePrx>(
202 p.robotHealthTopicName,
"RobotHealthTopic",
"Name of the RobotHealth topic");
204 defs->topic(p.aggregatedRobotHealthTopicPrx,
205 "AggregatedRobotHealthTopic",
206 "Name of the AggregatedRobotHealthTopic");
208 defs->optional(p.maximumCycleTimeWarnMS,
209 "MaximumCycleTimeWarnMS",
210 "Default value of the maximum cycle time for warnings");
211 defs->optional(p.maximumCycleTimeErrMS,
212 "MaximumCycleTimeErrMS",
213 "Default value of the maximum cycle time for error");
215 defs->optional(p.requiredTags,
"RequiredTags",
"Tags that should be requested.");
220 RobotHealth::UpdateEntry*
221 RobotHealth::findUpdateEntry(
const std::string& name)
225 for (
auto& updateEntrie : updateEntries)
227 if (updateEntrie.name == name)
229 return &updateEntrie;
236 std::pair<bool, RobotHealth::UpdateEntry&>
237 RobotHealth::findOrCreateUpdateEntry(
const std::string& name)
241 for (
auto& updateEntrie : updateEntries)
243 if (updateEntrie.name == name)
245 return {
false, updateEntrie};
250 ARMARX_INFO <<
"registering for heartbeat: '" << name <<
"'";
252 updateEntries.emplace_back(name, defaultMaximumCycleTimeWarn, defaultMaximumCycleTimeErr);
254 return {
true, updateEntries.back()};
261 std::scoped_lock lockU(updateMutex);
263 auto e = findOrCreateUpdateEntry(args.identifier);
266 std::scoped_lock lock(e.second.mutex);
270 ARMARX_INFO <<
"Newly registering component " << args.identifier <<
" for updates.";
275 e.second.maximumCycleTimeWarn);
278 e.second.tags = args.tags;
280 e.second.description = args.description;
281 e.second.enabled =
true;
282 e.second.state = HealthOK;
283 e.second.history.clear();
286 updateRequiredElements();
291 const core::time::dto::DateTime& referenceTime,
292 const Ice::Current& current)
302 std::scoped_lock lockU(updateMutex);
303 auto*
const entry = findUpdateEntry(identifier);
305 if (entry ==
nullptr)
308 <<
"` was not signed up for heartbeat. Ignoring heartbeat for now...";
314 std::scoped_lock lock(entry->mutex);
316 if (not entry->enabled)
319 <<
"` was not enabled. The heartbeat is ignored... ";
328 fromIce(referenceTime, refTime);
329 entry->history.push_back(
337 std::scoped_lock lock(updateMutex);
341 for (
auto& e : updateEntries)
343 if (e.name == identifier)
345 std::scoped_lock elock(e.mutex);
357 ARMARX_INFO <<
"Component " << identifier <<
" disabled from heartbeat";
361 ARMARX_INFO <<
"Could not unregister component " << identifier <<
". Not found.";
364 updateRequiredElements();
369 const std::vector<std::string>& tags,
370 const Ice::Current& current)
372 std::scoped_lock lock(updateMutex);
375 for (
const auto&
tag : tags)
377 tagsPerRequester[requesterIdentifier].insert(
tag);
380 updateRequiredElements();
383 std::set<std::string>
384 armarx::RobotHealth::requestedTags()
const
387 std::set<std::string> allRequestedTags;
388 for (
const auto& tgs : simox::alg::get_values(tagsPerRequester))
390 for (
const auto& tag : tgs)
392 allRequestedTags.insert(tag);
395 return allRequestedTags;
400 containsAnyOf(
const std::set<std::string>& values,
const std::vector<std::string>& searchKeys)
402 return std::any_of(searchKeys.begin(),
404 [&values](
const auto& key) { return values.count(key) > 0; });
408 armarx::RobotHealth::updateRequiredElements()
411 const std::set<std::string> allRequestedTags = requestedTags();
413 for (
auto& e : updateEntries)
419 std::unique_lock lock(e.mutex);
427 ARMARX_WARNING <<
"You are requiring the disabled component " << e.name
428 <<
". Enabling it...";
437 const std::vector<std::string>& tags,
438 const Ice::Current& current)
440 std::scoped_lock lock(updateMutex);
443 for (
const auto&
tag : tags)
445 tagsPerRequester[requesterIdentifier].erase(
tag);
448 updateRequiredElements();
454 std::scoped_lock lock(updateMutex);
461 tagsPerRequester.clear();
468 std::scoped_lock lock(updateMutex);
478 for (
const auto& e : updateEntries)
480 RobotHealthInfoEntry healthEntry;
482 for (
size_t i = 1; i < e.history.size(); i++)
484 auto later = e.history[i];
485 auto pre = e.history[i - 1];
487 auto delta = later.arrivalTime - pre.arrivalTime;
489 if (minDelta > delta)
494 if (maxDelta < delta)
500 const Duration timeSinceLastUpdateArrival =
502 const Duration timeSinceLastUpdateReference =
505 const DateTime lastReferenceTime = e.history.empty()
507 : e.history.back().referenceTime;
509 const Duration timeSyncDelayAndIce =
511 : e.history.back().arrivalTime - e.history.back().referenceTime;
513 healthEntry.identifier = e.name;
514 healthEntry.state = e.state;
515 healthEntry.enabled = e.enabled;
516 healthEntry.required = e.required;
517 toIce(healthEntry.minDelta, minDelta);
518 toIce(healthEntry.maxDelta, maxDelta);
519 toIce(healthEntry.lastReferenceTimestamp, lastReferenceTime);
520 toIce(healthEntry.timeSinceLastArrival, timeSinceLastUpdateArrival);
521 toIce(healthEntry.timeSyncDelayAndIce, timeSyncDelayAndIce);
522 toIce(healthEntry.timeSinceLastUpdateReference, timeSinceLastUpdateReference);
523 toIce(healthEntry.maximumCycleTimeWarning, e.maximumCycleTimeWarn);
524 toIce(healthEntry.maximumCycleTimeError, e.maximumCycleTimeErr);
525 healthEntry.tags = e.tags;
527 ret.entries[e.name] = healthEntry;
530 const auto tags = requestedTags();
531 ret.activeTags = std::vector<std::string>(tags.begin(), tags.end());
539 return p.robotHealthTopicName;
543 RobotHealth::reportDebugObserver()
545 for (
const auto& entry : updateEntries)
547 if (entry.history.size() > 1)
549 auto later = entry.history[entry.history.size() - 1];
550 auto pre = entry.history[entry.history.size() - 2];
551 const Duration delta = later.arrivalTime - pre.arrivalTime;
553 const Duration timeSinceLastArrival =
554 Clock::Now() - entry.history.back().arrivalTime;
555 const Duration timeToLastReference =
556 Clock::Now() - entry.history.back().referenceTime;
558 entry.history.back().arrivalTime - entry.history.back().referenceTime;
569 entry.maximumCycleTimeWarn.toMilliSecondsDouble());
571 entry.maximumCycleTimeErr.toMilliSecondsDouble());
static DateTime Now()
Current time on the virtual clock.
Default component property definition container.
std::string getConfigIdentifier()
Retrieve config identifier for this component as set in constructor.
void setDebugObserverDatafield(Ts &&... ts) const
void sendDebugObserverBatch()
void setDebugObserverBatchModeEnabled(bool enable)
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.
The periodic task executes one thread method repeatedly using the time period specified in the constr...
void onInitComponent() override
void unregister(const std::string &identifier, const Ice::Current &) override
void removeRequiredTags(const std::string &requesterIdentifier, const std::vector< std::string > &tags, const Ice::Current ¤t) override
void onDisconnectComponent() override
RobotHealthInfo getSummary(const Ice::Current ¤t) override
armarx::PropertyDefinitionsPtr createPropertyDefinitions() override
void resetRequiredTags(const Ice::Current ¤t) override
void signUp(const RobotHealthHeartbeatArgs &args, const Ice::Current ¤t) override
void onConnectComponent() override
void heartbeat(const std::string &identifier, const core::time::dto::DateTime &referenceTime, const Ice::Current ¤t) override
void addRequiredTags(const std::string &requesterIdentifier, const std::vector< std::string > &tags, const Ice::Current ¤t) override
std::string getTopicName(const Ice::Current ¤t) override
void onExitComponent() override
Represents a point in time.
static DateTime Invalid()
double toMilliSecondsDouble() const
Returns the amount of milliseconds.
static Duration MilliSeconds(std::int64_t milliSeconds)
Constructs a duration in milliseconds.
#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_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_ERROR
The logging level for unexpected behaviour, that must be fixed.
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
#define ARMARX_VERBOSE
The logging level for verbose information.
void fromIce(const dto::ClockType::ClockTypeEnum &dto, ClockType &bo)
This file offers overloads of toIce() and fromIce() functions for STL container types.
bool containsAnyOf(const std::set< std::string > &values, const std::vector< std::string > &searchKeys)
const std::string PROPERTY_REQUESTER_ID
IceUtil::Handle< class PropertyDefinitionContainer > PropertyDefinitionsPtr
PropertyDefinitions smart pointer type.
void fromIce(const std::map< IceKeyT, IceValueT > &iceMap, boost::container::flat_map< CppKeyT, CppValueT > &cppMap)
void toIce(std::map< IceKeyT, IceValueT > &iceMap, const boost::container::flat_map< CppKeyT, CppValueT > &cppMap)