36#include <RobotAPI/libraries/armem_skills/aron/Skill.aron.generated.h>
50 assistanceSessionTimeoutSeconds =
65 ensureMemoryConnection();
69 InterventionObserver::ensureMemoryConnection()
71 if (memoryConnected.load())
78 std::unique_lock lock(memoryConnectionMutex, std::try_to_lock);
79 if (!lock.owns_lock())
84 if (memoryConnected.load())
93 const auto memoryId = armem::MemoryID().withMemoryName(
"TaskOutcome");
103 const auto skillMemoryId = armem::MemoryID().withMemoryName(
"Skill");
106 memoryConnected.store(
true);
107 ARMARX_INFO <<
"InterventionObserver: Memory connection established.";
112 <<
"InterventionObserver: TaskOutcome memory not available yet. "
113 "Will retry on next event.";
116 return memoryConnected.load();
122 memoryConnected =
false;
147 const std::string& name,
148 const GamepadData&
data,
150 const Ice::Current&
c)
154 const bool backRisingEdge =
data.backButton && !prevBackButton;
155 prevBackButton =
data.backButton;
157 const bool startRisingEdge =
data.startButton && !prevStartButton;
158 prevStartButton =
data.startButton;
160 if (!ensureMemoryConnection())
168 ARMARX_INFO <<
"Back button pressed — committing SUSPENDED TaskOutcome";
170 const std::string suspendedSkillName = queryLatestSkillName();
171 ARMARX_INFO <<
"Skill suspended by gamepad back button: " << suspendedSkillName;
174 outcome.
taskName = suspendedSkillName;
177 outcome.
agent = agentName;
187 {
"source",
"gamepad"},
190 {
"leftStickX", std::to_string(
data.leftStickX)},
191 {
"leftStickY", std::to_string(
data.leftStickY)},
192 {
"rightStickX", std::to_string(
data.rightStickX)},
193 {
"rightStickY", std::to_string(
data.rightStickY)},
194 {
"dPadX", std::to_string(
data.dPadX)},
195 {
"dPadY", std::to_string(
data.dPadY)},
196 {
"leftTrigger", std::to_string(
data.leftTrigger)},
197 {
"rightTrigger", std::to_string(
data.rightTrigger)},
198 {
"leftButton", std::to_string(
data.leftButton)},
199 {
"rightButton", std::to_string(
data.rightButton)},
200 {
"backButton", std::to_string(
data.backButton)},
201 {
"startButton", std::to_string(
data.startButton)},
202 {
"xButton", std::to_string(
data.xButton)},
203 {
"yButton", std::to_string(
data.yButton)},
204 {
"aButton", std::to_string(
data.aButton)},
205 {
"bButton", std::to_string(
data.bButton)},
206 {
"theMiddleButton", std::to_string(
data.theMiddleButton)},
207 {
"leftStickButton", std::to_string(
data.leftStickButton)},
208 {
"rightStickButton", std::to_string(
data.rightStickButton)},
216 ARMARX_INFO <<
"Successfully committed SUSPENDED TaskOutcome for gamepad intervention.";
220 ARMARX_WARNING <<
"Failed to commit SUSPENDED TaskOutcome for gamepad intervention.";
227 ARMARX_INFO <<
"Start button pressed — checking for SUSPENDED entry to mark as recovered";
230 .providerName = providerName,
231 .taskTypeFilter = std::nullopt,
236 const auto result = reader.query(
q);
238 if (!result || result.outcomes.empty())
240 ARMARX_WARNING <<
"Start button pressed but no SUSPENDED TaskOutcome found in memory. Skipping recovery.";
246 for (
const auto& o : result.outcomes)
248 auto it = o.context.additional.find(
"source");
249 if (it == o.context.additional.end() || it->second !=
"gamepad")
261 ARMARX_WARNING <<
"Start button pressed but no gamepad-sourced SUSPENDED entry found. Skipping recovery.";
272 .recoveryMeasure =
"resume by gamepad triggered"};
279 ARMARX_INFO <<
"Successfully committed recovery for task '"
294 lastGamepadDevice = device;
295 lastGamepadName = name;
297 constexpr float deadzone = 0.1f;
299 std::set<std::string> activeActions;
301 if (std::abs(
data.leftTrigger) > deadzone || std::abs(
data.rightTrigger) > deadzone)
303 activeActions.insert(
"manual gripper control");
305 if (std::abs(
data.leftStickX) > deadzone || std::abs(
data.leftStickY) > deadzone)
307 activeActions.insert(
"manual platform navigation");
309 if (std::abs(
data.rightStickX) > deadzone || std::abs(
data.rightStickY) > deadzone)
311 activeActions.insert(
"rightStick");
313 if (std::abs(
data.dPadX) > deadzone || std::abs(
data.dPadY) > deadzone)
315 activeActions.insert(
"dPad");
319 activeActions.insert(
"manual action button");
323 activeActions.insert(
"manual action button");
327 activeActions.insert(
"manual action button");
331 activeActions.insert(
"manual action button");
335 activeActions.insert(
"manual bumper action");
337 if (
data.rightButton)
339 activeActions.insert(
"manual bumper action");
341 if (
data.theMiddleButton)
343 activeActions.insert(
"theMiddleButton");
345 if (
data.leftStickButton)
347 activeActions.insert(
"leftStickButton");
349 if (
data.rightStickButton)
351 activeActions.insert(
"rightStickButton");
355 const bool hasInput = !activeActions.empty();
359 if (!assistanceSessionActive)
362 assistanceSessionActive =
true;
363 assistanceSessionStart = now;
364 assistanceActionsInSession = activeActions;
370 assistanceActionsInSession.insert(activeActions.begin(), activeActions.end());
372 assistanceLastActivityTime = now;
374 else if (assistanceSessionActive)
376 const auto elapsed = now - assistanceLastActivityTime;
380 std::ostringstream actionStr;
382 for (
const auto& a : assistanceActionsInSession)
392 const auto durationMs =
393 (assistanceLastActivityTime - assistanceSessionStart).toMilliSeconds();
396 outcome.
taskName =
"GamepadIntervention";
399 outcome.
agent = agentName;
400 outcome.
startTime = assistanceSessionStart;
401 outcome.
endTime = assistanceLastActivityTime;
404 {
"source",
"gamepad_assistance"},
405 {
"actions", actionStr.str()},
406 {
"duration_ms", std::to_string(durationMs)},
407 {
"device", lastGamepadDevice},
408 {
"name", lastGamepadName},
416 ARMARX_INFO <<
"Committed assistance session: actions=[" << actionStr.str()
417 <<
"], duration=" << durationMs <<
"ms";
421 ARMARX_WARNING <<
"Failed to commit assistance session TaskOutcome.";
425 assistanceSessionActive =
false;
426 assistanceActionsInSession.clear();
432 InterventionObserver::queryLatestSkillName()
434 if (!skillMemoryReader)
454 const auto result = skillMemoryReader.
query(qb);
457 ARMARX_WARNING <<
"Failed to query Skill memory for latest skill event.";
470 std::string latestSkillName =
"unknown";
474 [&](
const armem::wm::Entity& entity)
479 skills::arondto::SkillStatusUpdate::FromAron(instance->data());
481 aronUpdate.executionStartedTimestamp > latestTime)
483 latestTime = aronUpdate.executionStartedTimestamp;
484 latestSkillName = aronUpdate.skillId.skillName;
489 return latestSkillName;
491 catch (
const std::exception& e)
493 ARMARX_WARNING <<
"Exception querying Skill memory: " << e.what();
504 if (!ensureMemoryConnection())
508 prevEmergencyStopState = state;
513 const bool becameActive =
514 (state == EmergencyStopState::eEmergencyStopActive &&
515 prevEmergencyStopState == EmergencyStopState::eEmergencyStopInactive);
516 const bool becameInactive =
517 (state == EmergencyStopState::eEmergencyStopInactive &&
518 prevEmergencyStopState == EmergencyStopState::eEmergencyStopActive);
519 prevEmergencyStopState = state;
523 ARMARX_INFO <<
"Emergency stop activated — committing SUSPENDED TaskOutcome";
525 const std::string suspendedSkillName = queryLatestSkillName();
526 ARMARX_INFO <<
"Skill suspended by emergency stop: " << suspendedSkillName;
529 outcome.
taskName = suspendedSkillName;
532 outcome.
agent = agentName;
542 {
"source",
"emergency_stop"},
543 {
"emergencyStopState",
"active"}};
550 ARMARX_INFO <<
"Successfully committed SUSPENDED TaskOutcome for emergency stop.";
554 ARMARX_WARNING <<
"Failed to commit SUSPENDED TaskOutcome for emergency stop.";
560 ARMARX_INFO <<
"Emergency stop released — checking for SUSPENDED entry to mark as recovered";
563 .providerName = providerName,
564 .taskTypeFilter = std::nullopt,
569 const auto result = reader.query(
q);
571 if (!result || result.outcomes.empty())
573 ARMARX_WARNING <<
"Emergency stop released but no SUSPENDED TaskOutcome found in memory. Skipping recovery.";
579 for (
const auto& o : result.outcomes)
581 auto it = o.context.additional.find(
"source");
582 if (it == o.context.additional.end() || it->second !=
"emergency_stop")
594 ARMARX_WARNING <<
"Emergency stop released but no emergency-stop-sourced SUSPENDED entry found. Skipping recovery.";
602 .recoveryMeasure =
"resume by emergency stop release"};
609 ARMARX_INFO <<
"Successfully committed recovery for emergency stop intervention.";
613 ARMARX_WARNING <<
"Failed to commit recovery TaskOutcome for emergency stop.";
std::string getConfigIdentifier()
Retrieve config identifier for this component as set in constructor.
Property< PropertyType > getProperty(const std::string &name)
static Duration Hours(std::int64_t hours)
Constructs a duration in hours.
static Duration SecondsDouble(double seconds)
Constructs a duration in seconds.
void onInitComponent() override
void onDisconnectComponent() override
armarx::PropertyDefinitionsPtr createPropertyDefinitions() override
void reportEmergencyStopState(EmergencyStopState state, const Ice::Current &) override
void onConnectComponent() override
void reportGamepadState(const std::string &device, const std::string &name, const GamepadData &data, const TimestampBasePtr ×tamp, const Ice::Current &c) override
void onExitComponent() override
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 usingTopic(const std::string &name, bool orderedPublishing=false)
Registers a proxy for subscription after initialization.
auto * findLatestInstance(int instanceIndex=0)
CoreSegmentT * findCoreSegment(const std::string &name)
Reader getReader(const MemoryID &memoryID)
Get a reader to the given memory name.
Reader useReader(const MemoryID &memoryID)
Use a memory server and get a reader for it.
Writer getWriter(const MemoryID &memoryID)
Get a writer to the given memory name.
QueryResult query(const QueryInput &input) const
Perform a query on the WM.
MemoryNameSystem & memoryNameSystem()
The query::Builder class provides a fluent-style specification of hierarchical queries.
CoreSegmentSelector & coreSegments()
Start specifying core segments.
CoreSegmentSelector & withName(const std::string &name) override
ProviderSegmentSelector & providerSegments()
Start specifying provider segments.
EntitySelector & all() override
SnapshotSelector & snapshots()
Start specifying entity snapshots.
EntitySelector & entities()
Start specifying entities.
ProviderSegmentSelector & all() override
SnapshotSelector & latest()
static DateTime Invalid()
#define ARMARX_INFO
The normal logging level.
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
This file offers overloads of toIce() and fromIce() functions for STL container types.
IceUtil::Handle< class PropertyDefinitionContainer > PropertyDefinitionsPtr
PropertyDefinitions smart pointer type.
wm::Memory memory
The slice of the memory that matched the query.
InterruptionFailures interruption
std::map< std::string, std::string > additional
armarx::core::time::DateTime startTime
std::optional< RecoveryInfo > recoveryInfo
armarx::core::time::DateTime endTime
std::optional< bool > couldRecover
TaskOutcomeContext context
TaskOutcomeType outcomeType
std::optional< FailureInfo > failureInfo