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;
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());