TaskOutcomeMemory.cpp
Go to the documentation of this file.
1#include "TaskOutcomeMemory.h"
2
3#include <algorithm>
4#include <numeric>
5#include <vector>
6
8
10#include <RobotAPI/libraries/armem_task_outcome/aron/TaskOutcome.aron.generated.h>
13
15
17
18namespace armarx
19{
20
21 namespace
22 {
23
24 std::string
25 outcomeTypeToString(armem::task_outcome::TaskOutcomeType type)
26 {
27 switch (type)
28 {
30 return "SUCCESS";
32 return "FAILURE";
34 return "ABORTED";
36 return "SUSPENDED";
37 default:
38 return "UNKNOWN";
39 }
40 }
41
42 std::string
43 taskTypeToString(armem::task_outcome::TaskType type)
44 {
45 switch (type)
46 {
48 return "GRASPING_FAMILIAR";
50 return "GRASPING_KNOWN";
52 return "MANIPULATION";
54 return "VERBALIZATION";
56 return "DETECTION";
58 return "NAVIGATION";
60 return "SPEAKING";
62 return "SPECIAL";
63 default:
64 return "UNKNOWN";
65 }
66 }
67
68 std::string
69 failureTypeSummary(const armem::task_outcome::FailureType& type)
70 {
71 std::vector<std::string> active;
72
74 active.push_back("Planning: NoTrajectoryFound");
75 if (type.planning.noGraspFound)
76 active.push_back("Planning: NoGraspFound");
77 if (type.planning.special)
78 active.push_back("Planning: Special");
79
81 active.push_back("Knowledge/Perception: ObjectNotDetected");
83 active.push_back("Knowledge/Perception: CameraNotWorking");
85 active.push_back("Knowledge/Retrieval: TechnicalFailure");
87 active.push_back("Knowledge/Retrieval: KnowledgeNotInMemory");
88
90 active.push_back("Execution: RobotNotMoving");
92 active.push_back("Execution: HandNotClosing");
94 active.push_back("Execution: GraspingFailureDetected");
95
97 active.push_back("Interaction: NoHumanForHandover");
99 active.push_back("Interaction: DesiredObjectNotAtLocation");
101 active.push_back("Interaction: CommunicationFailure");
102
103 if (type.interruption.userStopped)
104 active.push_back("Interruption: UserStopped");
105
106 if (type.subskillFailure.failed)
107 active.push_back("SubskillFailure: " + type.subskillFailure.subskillName);
108
109 if (active.empty())
110 {
111 return "(none)";
112 }
113
114 std::string result;
115 for (size_t i = 0; i < active.size(); ++i)
116 {
117 if (i > 0)
118 result += ", ";
119 result += active[i];
120 }
121 return result;
122 }
123
125 stringToOutcomeType(const std::string& str)
126 {
127 if (str == "SUCCESS")
129 if (str == "FAILURE")
131 if (str == "ABORTED")
133 if (str == "SUSPENDED")
136 }
137
138 std::string
139 truncateStr(const std::string& str, size_t maxLen)
140 {
141 if (str.size() <= maxLen)
142 return str;
143 if (maxLen <= 3)
144 return str.substr(0, maxLen);
145 return str.substr(0, maxLen - 3) + "...";
146 }
147
148 } // namespace
149
152 taskOutcomeSegment(iceAdapter())
153 {
154 }
155
158 {
161
162 setMemoryName(MemoryName);
163
164 taskOutcomeSegment.defineProperties(defs, "outcome.");
165
166 return defs;
167 }
168
169 std::string
171 {
172 return "TaskOutcomeMemory";
173 }
174
175 void
177 {
178 taskOutcomeSegment.init();
179 }
180
181 void
187
188 void
192
193 void
197
198 armem::data::CommitResult
199 TaskOutcomeMemory::commit(const armem::data::Commit& commit, const Ice::Current& current)
200 {
201 armem::data::CommitResult result = ReadWritePluginUser::commit(commit, current);
202 newDataAvailable = true;
203 return result;
204 }
205
206 void
208 {
209 using namespace armarx::RemoteGui::Client;
210
211 // --- Settings row ---
212 tab.maxEntries.setRange(1, 100);
213 tab.maxEntries.setValue(guiMaxEntries);
214
215 GridLayout settingsGrid;
216 {
217 int col = 0;
218 settingsGrid.add(Label("Max Entries"), Pos{0, col++});
219 settingsGrid.add(tab.maxEntries, Pos{0, col++});
220 }
221
222 GroupBox settingsGroup;
223 settingsGroup.setLabel("Settings");
224 settingsGroup.addChild(settingsGrid);
225
226 // --- Collect outcomes with MemoryIDs ---
227 std::vector<armem::task_outcome::TaskOutcome> outcomes;
228 std::vector<armem::MemoryID> outcomeIds;
229
230 if (workingMemory().hasCoreSegment("TaskOutcome"))
231 {
232 workingMemory().getCoreSegment("TaskOutcome").forEachProviderSegment(
233 [&outcomes, &outcomeIds](const auto& providerSegment)
234 {
235 providerSegment.forEachEntity(
236 [&outcomes, &outcomeIds](const auto& entity)
237 {
238 entity.forEachSnapshot(
239 [&outcomes, &outcomeIds](const auto& snapshot)
240 {
241 snapshot.forEachInstance(
242 [&outcomes, &outcomeIds](const auto& instance)
243 {
244 try
245 {
246 const auto dto =
247 armarx::task_outcome::arondto::
248 TaskOutcome::FromAron(
249 instance.data());
252 outcomes.push_back(bo);
253 outcomeIds.push_back(instance.id());
254 }
255 catch (...)
256 {
257 }
258 });
259 });
260 });
261 });
262 }
263
264 // Take the last N entries.
265 if (static_cast<int>(outcomes.size()) > guiMaxEntries)
266 {
267 const int excess = static_cast<int>(outcomes.size()) - guiMaxEntries;
268 outcomes.erase(outcomes.begin(), outcomes.begin() + excess);
269 outcomeIds.erase(outcomeIds.begin(), outcomeIds.begin() + excess);
270 }
271
272 // Sort newest first.
273 {
274 std::vector<size_t> idx(outcomes.size());
275 std::iota(idx.begin(), idx.end(), 0);
276 std::sort(idx.begin(),
277 idx.end(),
278 [&outcomes](size_t a, size_t b)
279 { return outcomes[a].endTime > outcomes[b].endTime; });
280
281 std::vector<armem::task_outcome::TaskOutcome> sortedOutcomes(outcomes.size());
282 std::vector<armem::MemoryID> sortedIds(outcomeIds.size());
283 for (size_t i = 0; i < idx.size(); ++i)
284 {
285 sortedOutcomes[i] = std::move(outcomes[idx[i]]);
286 sortedIds[i] = std::move(outcomeIds[idx[i]]);
287 }
288 outcomes = std::move(sortedOutcomes);
289 outcomeIds = std::move(sortedIds);
290 }
291
292 // Clamp selected index to valid range.
293 if (selectedOutcomeIndex >= static_cast<int>(outcomes.size()))
294 {
295 selectedOutcomeIndex = -1;
296 }
297
298 displayedOutcomeIds = outcomeIds;
299
300 // --- Results table with selection column ---
301 tab.selectButtons.clear();
302
303 GridLayout resultsGrid;
304 {
305 int row = 0;
306
307 // Header row
308 resultsGrid.add(Label(""), Pos{row, 0});
309 resultsGrid.add(Label("Name"), Pos{row, 1});
310 resultsGrid.add(Label("Status"), Pos{row, 2});
311 resultsGrid.add(Label("Failure Types"), Pos{row, 3});
312 resultsGrid.add(Label("Skill Type"), Pos{row, 4});
313 resultsGrid.add(Label("Time"), Pos{row, 5});
314 resultsGrid.add(Label("Recovered"), Pos{row, 6});
315 resultsGrid.add(Label("Error"), Pos{row, 7});
316 row++;
317
318 for (int i = 0; i < static_cast<int>(outcomes.size()); ++i)
319 {
320 const auto& outcome = outcomes[i];
321
322 auto& btn = tab.selectButtons[i];
323 btn.setLabel("select");
324 btn.setValue(i == selectedOutcomeIndex);
325
326 const std::string failureStr =
327 outcome.failureInfo.has_value()
328 ? failureTypeSummary(outcome.failureInfo->failureType)
329 : "-";
330
331 const std::string recoveredStr =
332 outcome.couldRecover.has_value()
333 ? (outcome.couldRecover.value() ? "yes" : "no")
334 : "-";
335
336 resultsGrid.add(btn, Pos{row, 0});
337 resultsGrid.add(Label(truncateStr(outcome.taskName, 20)), Pos{row, 1});
338 resultsGrid.add(
339 Label(outcomeTypeToString(outcome.outcomeType)), Pos{row, 2});
340 resultsGrid.add(Label(truncateStr(failureStr, 50)), Pos{row, 3});
341 resultsGrid.add(Label(taskTypeToString(outcome.taskType)), Pos{row, 4});
342 resultsGrid.add(Label(outcome.endTime.toTimeString()), Pos{row, 5});
343 resultsGrid.add(Label(recoveredStr), Pos{row, 6});
344 resultsGrid.add(
345 Label(truncateStr(outcome.errorMessage.value_or("-"), 35)), Pos{row, 7});
346 row++;
347 }
348 }
349
350 const std::string resultsLabel =
351 "Task Outcomes (" + std::to_string(outcomes.size()) + " entries)";
352 GroupBox resultsGroup;
353 resultsGroup.setLabel(resultsLabel);
354 resultsGroup.addChild(resultsGrid);
355
356 // --- Detail panel for selected outcome ---
357 // Max length for detail value labels to prevent the left column from
358 // becoming too wide and squeezing the edit panel on the right.
359 constexpr size_t detailMaxLen = 80;
360
361 GroupBox detailGroup;
362 if (selectedOutcomeIndex >= 0 &&
363 selectedOutcomeIndex < static_cast<int>(outcomes.size()))
364 {
365 const auto& sel = outcomes[selectedOutcomeIndex];
366 detailGroup.setLabel("Selected: " + sel.taskName);
367
368 GridLayout detailGrid;
369 int row = 0;
370
371 detailGrid.add(Label("Task Name"), Pos{row, 0});
372 detailGrid.add(Label(truncateStr(sel.taskName, detailMaxLen)), Pos{row, 1});
373 row++;
374
375 detailGrid.add(Label("Outcome"), Pos{row, 0});
376 detailGrid.add(Label(outcomeTypeToString(sel.outcomeType)), Pos{row, 1});
377 row++;
378
379 detailGrid.add(Label("Task Type"), Pos{row, 0});
380 detailGrid.add(Label(taskTypeToString(sel.taskType)), Pos{row, 1});
381 row++;
382
383 if (sel.specialTaskTypeDescription.has_value())
384 {
385 detailGrid.add(Label("Special Type"), Pos{row, 0});
386 detailGrid.add(Label(truncateStr(sel.specialTaskTypeDescription.value(), detailMaxLen)), Pos{row, 1});
387 row++;
388 }
389
390 detailGrid.add(Label("Agent"), Pos{row, 0});
391 detailGrid.add(Label(truncateStr(sel.agent, detailMaxLen)), Pos{row, 1});
392 row++;
393
394 detailGrid.add(Label("Start Time"), Pos{row, 0});
395 detailGrid.add(Label(sel.startTime.toTimeString()), Pos{row, 1});
396 row++;
397
398 detailGrid.add(Label("End Time"), Pos{row, 0});
399 detailGrid.add(Label(sel.endTime.toTimeString()), Pos{row, 1});
400 row++;
401
402 detailGrid.add(Label("Error"), Pos{row, 0});
403 detailGrid.add(Label(truncateStr(sel.errorMessage.value_or("-"), detailMaxLen)), Pos{row, 1});
404 row++;
405
406 {
407 const std::string failureStr = sel.failureInfo.has_value()
408 ? failureTypeSummary(sel.failureInfo->failureType)
409 : "(none)";
410
411 detailGrid.add(Label("Failure Types"), Pos{row, 0});
412 detailGrid.add(Label(truncateStr(failureStr, detailMaxLen)), Pos{row, 1});
413 row++;
414 }
415
416 if (sel.failureInfo.has_value() &&
417 sel.failureInfo->specialFailureDescription.has_value())
418 {
419 detailGrid.add(Label("Failure Desc"), Pos{row, 0});
420 detailGrid.add(
421 Label(truncateStr(sel.failureInfo->specialFailureDescription.value(), detailMaxLen)),
422 Pos{row, 1});
423 row++;
424 }
425
426 detailGrid.add(Label("Could Recover"), Pos{row, 0});
427 detailGrid.add(
428 Label(sel.couldRecover.has_value()
429 ? (sel.couldRecover.value() ? "true" : "false")
430 : "-"),
431 Pos{row, 1});
432 row++;
433
434 if (sel.recoveryInfo.has_value())
435 {
436 detailGrid.add(Label("Recovery Time"), Pos{row, 0});
437 detailGrid.add(
438 Label(sel.recoveryInfo->recoveryTime.toTimeString()), Pos{row, 1});
439 row++;
440
441 detailGrid.add(Label("Recovery Measure"), Pos{row, 0});
442 detailGrid.add(
443 Label(truncateStr(sel.recoveryInfo->recoveryMeasure, detailMaxLen)),
444 Pos{row, 1});
445 row++;
446 }
447
448 detailGrid.add(Label("Skill"), Pos{row, 0});
449 detailGrid.add(Label(truncateStr(sel.context.skillName, detailMaxLen)), Pos{row, 1});
450 row++;
451
452 if (sel.context.skillExecutionStartedTimestamp.has_value())
453 {
454 detailGrid.add(Label("Skill Exec Started"), Pos{row, 0});
455 detailGrid.add(Label(sel.context.skillExecutionStartedTimestamp->toTimeString()), Pos{row, 1});
456 row++;
457 }
458
459 if (sel.context.naturalLanguageDescription.has_value())
460 {
461 detailGrid.add(Label("Description"), Pos{row, 0});
462 detailGrid.add(Label(truncateStr(sel.context.naturalLanguageDescription.value(), detailMaxLen)), Pos{row, 1});
463 row++;
464 }
465
466 for (const auto& [key, value] : sel.context.parameters)
467 {
468 detailGrid.add(Label("Param: " + key), Pos{row, 0});
469 detailGrid.add(Label(truncateStr(value, detailMaxLen)), Pos{row, 1});
470 row++;
471 }
472
473 for (const auto& [key, value] : sel.context.additional)
474 {
475 detailGrid.add(Label("Extra: " + key), Pos{row, 0});
476 detailGrid.add(Label(truncateStr(value, detailMaxLen)), Pos{row, 1});
477 row++;
478 }
479
480 for (const auto& [key, value] : sel.context.taskPreconditions.items())
481 {
482 detailGrid.add(Label("Precond: " + key), Pos{row, 0});
483 detailGrid.add(Label(truncateStr(value.dump(), detailMaxLen)), Pos{row, 1});
484 row++;
485 }
486
487 for (const auto& [key, value] : sel.context.taskPostconditions.items())
488 {
489 detailGrid.add(Label("Postcond: " + key), Pos{row, 0});
490 detailGrid.add(Label(truncateStr(value.dump(), detailMaxLen)), Pos{row, 1});
491 row++;
492 }
493
494 detailGroup.addChild(detailGrid);
495 }
496 else
497 {
498 detailGroup.setLabel("Selected: (none)");
499 detailGroup.addChild(Label("Click \"select\" on a row to select an outcome."));
500 }
501
502 // --- Right panel: Edit outcome ---
503 // Always add the edit widgets to the layout so they are registered
504 // with the tab. Otherwise wasClicked()/getValue() crash on
505 // unregistered widgets in RemoteGui_update().
506 GroupBox editGroup;
507 editGroup.setLabel("Edit Outcome");
508
509 const bool hasSelection = selectedOutcomeIndex >= 0 &&
510 selectedOutcomeIndex < static_cast<int>(outcomes.size());
511
512 if (selectionJustChanged)
513 {
514 editStatusMessage = "";
515 if (hasSelection)
516 {
517 const auto& sel = outcomes[selectedOutcomeIndex];
518 editErrorText = sel.errorMessage.value_or("");
519 editStatus = outcomeTypeToString(sel.outcomeType);
520 editFailureType = sel.failureInfo.has_value()
521 ? sel.failureInfo->failureType
523 }
524 else
525 {
526 editErrorText = "";
527 editStatus = "UNKNOWN";
528 editFailureType = armem::task_outcome::FailureType{};
529 }
530 selectionJustChanged = false;
531 }
532
533 tab.statusComboBox.setOptions({"UNKNOWN", "SUCCESS", "FAILURE", "ABORTED", "SUSPENDED"});
534 tab.statusComboBox.setValue(editStatus);
535
536 // Set checkbox values from editFailureType
537 tab.failureCheckBoxes.planningNoTrajectoryFound.setValue(editFailureType.planning.noTrajectoryFound);
538 tab.failureCheckBoxes.planningNoGraspFound.setValue(editFailureType.planning.noGraspFound);
539 tab.failureCheckBoxes.planningSpecial.setValue(editFailureType.planning.special);
540 tab.failureCheckBoxes.perceptionObjectNotDetected.setValue(editFailureType.knowledge.perception.objectNotDetected);
541 tab.failureCheckBoxes.perceptionCameraNotWorking.setValue(editFailureType.knowledge.perception.cameraNotWorking);
542 tab.failureCheckBoxes.knowledgeRetrievalTechnicalFailure.setValue(editFailureType.knowledge.knowledgeRetrieval.technicalFailure);
543 tab.failureCheckBoxes.knowledgeRetrievalKnowledgeNotInMemory.setValue(editFailureType.knowledge.knowledgeRetrieval.knowledgeNotInMemory);
544 tab.failureCheckBoxes.executionRobotNotMoving.setValue(editFailureType.execution.robotNotMoving);
545 tab.failureCheckBoxes.executionHandNotClosing.setValue(editFailureType.execution.handNotClosing);
546 tab.failureCheckBoxes.executionGraspingFailureDetected.setValue(editFailureType.execution.graspingFailureDetected);
547 tab.failureCheckBoxes.interactionNoHumanForHandover.setValue(editFailureType.interaction.noHumanForHandover);
548 tab.failureCheckBoxes.interactionDesiredObjectNotAtLocation.setValue(editFailureType.interaction.desiredObjectNotAtLocation);
549 tab.failureCheckBoxes.interactionCommunicationFailure.setValue(editFailureType.interaction.communicationFailure);
550 tab.failureCheckBoxes.interruptionUserStopped.setValue(editFailureType.interruption.userStopped);
551 tab.failureCheckBoxes.subskillFailureFailed.setValue(editFailureType.subskillFailure.failed);
552 tab.failureCheckBoxes.subskillFailureName.setValue(editFailureType.subskillFailure.subskillName);
553
554 tab.errorMessageInput.setValue(editErrorText);
555 tab.saveButton.setLabel("Save");
556
557 GridLayout editGrid;
558 int editRow = 0;
559 if (!hasSelection)
560 {
561 editGrid.add(Label("Select an outcome to edit."), Pos{editRow, 0});
562 editRow++;
563 }
564 editGrid.add(Label("Status:"), Pos{editRow, 0});
565 editRow++;
566 editGrid.add(tab.statusComboBox, Pos{editRow, 0});
567 editRow++;
568
569 // Planning Failures group
570 {
571 GroupBox planningGroup;
572 planningGroup.setLabel("Planning Failures");
573 GridLayout planningGrid;
574 planningGrid.add(tab.failureCheckBoxes.planningNoTrajectoryFound, Pos{0, 0});
575 planningGrid.add(Label("No Trajectory Found"), Pos{0, 1});
576 planningGrid.add(tab.failureCheckBoxes.planningNoGraspFound, Pos{1, 0});
577 planningGrid.add(Label("No Grasp Found"), Pos{1, 1});
578 planningGrid.add(tab.failureCheckBoxes.planningSpecial, Pos{2, 0});
579 planningGrid.add(Label("Special"), Pos{2, 1});
580 planningGroup.addChild(planningGrid);
581 editGrid.add(planningGroup, Pos{editRow, 0});
582 editRow++;
583 }
584
585 // Knowledge Failures group
586 {
587 GroupBox knowledgeGroup;
588 knowledgeGroup.setLabel("Knowledge Failures");
589 GridLayout knowledgeGrid;
590 int kRow = 0;
591 knowledgeGrid.add(Label("-- Perception --"), Pos{kRow, 0}, Span{1, 2});
592 kRow++;
593 knowledgeGrid.add(tab.failureCheckBoxes.perceptionObjectNotDetected, Pos{kRow, 0});
594 knowledgeGrid.add(Label("Object Not Detected"), Pos{kRow, 1});
595 kRow++;
596 knowledgeGrid.add(tab.failureCheckBoxes.perceptionCameraNotWorking, Pos{kRow, 0});
597 knowledgeGrid.add(Label("Camera Not Working"), Pos{kRow, 1});
598 kRow++;
599 knowledgeGrid.add(Label("-- Knowledge Retrieval --"), Pos{kRow, 0}, Span{1, 2});
600 kRow++;
601 knowledgeGrid.add(tab.failureCheckBoxes.knowledgeRetrievalTechnicalFailure, Pos{kRow, 0});
602 knowledgeGrid.add(Label("Technical Failure"), Pos{kRow, 1});
603 kRow++;
604 knowledgeGrid.add(tab.failureCheckBoxes.knowledgeRetrievalKnowledgeNotInMemory, Pos{kRow, 0});
605 knowledgeGrid.add(Label("Knowledge Not In Memory"), Pos{kRow, 1});
606 knowledgeGroup.addChild(knowledgeGrid);
607 editGrid.add(knowledgeGroup, Pos{editRow, 0});
608 editRow++;
609 }
610
611 // Execution Failures group
612 {
613 GroupBox executionGroup;
614 executionGroup.setLabel("Execution Failures");
615 GridLayout executionGrid;
616 executionGrid.add(tab.failureCheckBoxes.executionRobotNotMoving, Pos{0, 0});
617 executionGrid.add(Label("Robot Not Moving"), Pos{0, 1});
618 executionGrid.add(tab.failureCheckBoxes.executionHandNotClosing, Pos{1, 0});
619 executionGrid.add(Label("Hand Not Closing"), Pos{1, 1});
620 executionGrid.add(tab.failureCheckBoxes.executionGraspingFailureDetected, Pos{2, 0});
621 executionGrid.add(Label("Grasping Failure Detected"), Pos{2, 1});
622 executionGroup.addChild(executionGrid);
623 editGrid.add(executionGroup, Pos{editRow, 0});
624 editRow++;
625 }
626
627 // Interaction Failures group
628 {
629 GroupBox interactionGroup;
630 interactionGroup.setLabel("Interaction Failures");
631 GridLayout interactionGrid;
632 interactionGrid.add(tab.failureCheckBoxes.interactionNoHumanForHandover, Pos{0, 0});
633 interactionGrid.add(Label("No Human For Handover"), Pos{0, 1});
634 interactionGrid.add(tab.failureCheckBoxes.interactionDesiredObjectNotAtLocation, Pos{1, 0});
635 interactionGrid.add(Label("Desired Object Not At Location"), Pos{1, 1});
636 interactionGrid.add(tab.failureCheckBoxes.interactionCommunicationFailure, Pos{2, 0});
637 interactionGrid.add(Label("Communication Failure"), Pos{2, 1});
638 interactionGroup.addChild(interactionGrid);
639 editGrid.add(interactionGroup, Pos{editRow, 0});
640 editRow++;
641 }
642
643 // Interruption Failures group
644 {
645 GroupBox interruptionGroup;
646 interruptionGroup.setLabel("Interruption Failures");
647 GridLayout interruptionGrid;
648 interruptionGrid.add(tab.failureCheckBoxes.interruptionUserStopped, Pos{0, 0});
649 interruptionGrid.add(Label("User Stopped"), Pos{0, 1});
650 interruptionGroup.addChild(interruptionGrid);
651 editGrid.add(interruptionGroup, Pos{editRow, 0});
652 editRow++;
653 }
654
655 // Subskill Failure group
656 {
657 GroupBox subskillGroup;
658 subskillGroup.setLabel("Subskill Failure");
659 GridLayout subskillGrid;
660 subskillGrid.add(tab.failureCheckBoxes.subskillFailureFailed, Pos{0, 0});
661 subskillGrid.add(Label("Failed"), Pos{0, 1});
662 subskillGrid.add(Label("Subskill Name:"), Pos{1, 0});
663 subskillGrid.add(tab.failureCheckBoxes.subskillFailureName, Pos{1, 1});
664 subskillGroup.addChild(subskillGrid);
665 editGrid.add(subskillGroup, Pos{editRow, 0});
666 editRow++;
667 }
668
669 editGrid.add(Label("Error Message:"), Pos{editRow, 0});
670 editRow++;
671 editGrid.add(tab.errorMessageInput, Pos{editRow, 0});
672 editRow++;
673 editGrid.add(tab.saveButton, Pos{editRow, 0});
674 editRow++;
675 const std::string messageHtml = editStatusMessage.empty()
676 ? " "
677 : "<font color='red'>" + editStatusMessage + "</font>";
678 editGrid.add(Label(messageHtml), Pos{editRow, 0});
679 editGroup.addChild(editGrid);
680
681 VBoxLayout leftColumn = {settingsGroup, resultsGroup, detailGroup, VSpacer()};
682 VBoxLayout rightColumn = {editGroup, VSpacer()};
683 HBoxLayout root = {leftColumn, rightColumn};
684 RemoteGui_createTab(getName(), root, &tab);
685 }
686
687 void
689 {
690 bool needsRebuild = false;
691
692 // Preserve user's input before any rebuild overwrites it.
693 editErrorText = tab.errorMessageInput.getValue();
694 editStatus = tab.statusComboBox.getValue();
695 editFailureType.planning.noTrajectoryFound = tab.failureCheckBoxes.planningNoTrajectoryFound.getValue();
696 editFailureType.planning.noGraspFound = tab.failureCheckBoxes.planningNoGraspFound.getValue();
697 editFailureType.planning.special = tab.failureCheckBoxes.planningSpecial.getValue();
698 editFailureType.knowledge.perception.objectNotDetected = tab.failureCheckBoxes.perceptionObjectNotDetected.getValue();
699 editFailureType.knowledge.perception.cameraNotWorking = tab.failureCheckBoxes.perceptionCameraNotWorking.getValue();
700 editFailureType.knowledge.knowledgeRetrieval.technicalFailure = tab.failureCheckBoxes.knowledgeRetrievalTechnicalFailure.getValue();
701 editFailureType.knowledge.knowledgeRetrieval.knowledgeNotInMemory = tab.failureCheckBoxes.knowledgeRetrievalKnowledgeNotInMemory.getValue();
702 editFailureType.execution.robotNotMoving = tab.failureCheckBoxes.executionRobotNotMoving.getValue();
703 editFailureType.execution.handNotClosing = tab.failureCheckBoxes.executionHandNotClosing.getValue();
704 editFailureType.execution.graspingFailureDetected = tab.failureCheckBoxes.executionGraspingFailureDetected.getValue();
705 editFailureType.interaction.noHumanForHandover = tab.failureCheckBoxes.interactionNoHumanForHandover.getValue();
706 editFailureType.interaction.desiredObjectNotAtLocation = tab.failureCheckBoxes.interactionDesiredObjectNotAtLocation.getValue();
707 editFailureType.interaction.communicationFailure = tab.failureCheckBoxes.interactionCommunicationFailure.getValue();
708 editFailureType.interruption.userStopped = tab.failureCheckBoxes.interruptionUserStopped.getValue();
709 editFailureType.subskillFailure.failed = tab.failureCheckBoxes.subskillFailureFailed.getValue();
710 editFailureType.subskillFailure.subskillName = tab.failureCheckBoxes.subskillFailureName.getValue();
711
712 if (tab.maxEntries.hasValueChanged())
713 {
714 guiMaxEntries = tab.maxEntries.getValue();
715 needsRebuild = true;
716 }
717
718 // Handle selection: if a toggle button was clicked, select that row.
719 for (auto& [index, btn] : tab.selectButtons)
720 {
721 if (btn.hasValueChanged() && btn.getValue())
722 {
723 selectedOutcomeIndex = index;
724 selectionJustChanged = true;
725 needsRebuild = true;
726 }
727 else if (btn.hasValueChanged() && !btn.getValue() &&
728 index == selectedOutcomeIndex)
729 {
730 selectedOutcomeIndex = -1;
731 selectionJustChanged = true;
732 needsRebuild = true;
733 }
734 }
735
736 // Handle save button.
737 if (tab.saveButton.wasClicked() && selectedOutcomeIndex >= 0 &&
738 selectedOutcomeIndex < static_cast<int>(displayedOutcomeIds.size()))
739 {
740 const std::string newError = tab.errorMessageInput.getValue();
741 const std::string newStatus = tab.statusComboBox.getValue();
742 const auto& id = displayedOutcomeIds[selectedOutcomeIndex];
743
744 const auto newOutcomeType = stringToOutcomeType(newStatus);
745
746 editStatusMessage = "";
747
748 try
749 {
750 auto& instance = workingMemory()
751 .getCoreSegment(id.coreSegmentName)
752 .getProviderSegment(id.providerSegmentName)
753 .getEntity(id.entityName)
754 .getSnapshot(id.timestamp)
755 .getInstance(id.instanceIndex);
756
757 auto dto = armarx::task_outcome::arondto::TaskOutcome::FromAron(
758 instance.data());
759
760 // Update error message.
761 dto.errorMessage = newError.empty()
762 ? std::optional<std::string>(std::nullopt)
763 : std::optional<std::string>(newError);
764
765 // Update outcome type (status).
766 armem::task_outcome::toAron(dto.outcomeType, newOutcomeType);
767
768 // Update failure info.
769 if (!dto.failureInfo.has_value())
770 {
771 armarx::task_outcome::arondto::FailureInfo fi;
773 armem::task_outcome::toAron(fi.failureType, defaultFt);
774 dto.failureInfo = fi;
775 }
776 armem::task_outcome::toAron(dto.failureInfo->failureType, editFailureType);
777
778 instance.data() = dto.toAron();
779
780 ARMARX_INFO << "Updated outcome for '" << id.entityName
781 << "': status=" << newStatus
782 << ", failures=" << failureTypeSummary(editFailureType)
783 << ", error='" << newError << "'";
784 }
785 catch (const std::exception& e)
786 {
787 ARMARX_WARNING << "Failed to update outcome: " << e.what();
788 }
789
790 needsRebuild = true;
791 }
792
793 // Rebuild when new data was committed (only when not editing).
794 if (selectedOutcomeIndex < 0 && newDataAvailable.exchange(false))
795 {
796 needsRebuild = true;
797 }
798
799 if (needsRebuild)
800 {
802 }
803 }
804
805} // namespace armarx
int Label(int n[], int size, int *curLabel, MiscLib::Vector< std::pair< int, size_t > > *labels)
Definition Bitmap.cpp:801
std::string timestamp()
#define ARMARX_REGISTER_COMPONENT_EXECUTABLE(ComponentT, applicationName)
Definition Decoupled.h:29
uint8_t index
std::string str(const T &t)
Default component property definition container.
Definition Component.h:70
std::string getConfigIdentifier()
Retrieve config identifier for this component as set in constructor.
Definition Component.cpp:90
std::string getName() const
Retrieve name of object.
void onInitComponent() override
Pure virtual hook for the subclass.
armem::data::CommitResult commit(const armem::data::Commit &commit, const Ice::Current &) override
void onDisconnectComponent() override
Hook for subclass.
armarx::PropertyDefinitionsPtr createPropertyDefinitions() override
void onConnectComponent() override
Pure virtual hook for the subclass.
void onExitComponent() override
Hook for subclass.
std::string getDefaultName() const override
Retrieve default name of component.
CoreSegmentT & getCoreSegment(const std::string &name)
Definition MemoryBase.h:134
#define ARMARX_INFO
The normal logging level.
Definition Logging.h:181
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
Definition Logging.h:193
void fromAron(const armarx::task_outcome::arondto::TaskOutcomeType &dto, TaskOutcomeType &bo)
void toAron(armarx::task_outcome::arondto::TaskOutcomeType &dto, const TaskOutcomeType &bo)
This file offers overloads of toIce() and fromIce() functions for STL container types.
IceUtil::Handle< class PropertyDefinitionContainer > PropertyDefinitionsPtr
PropertyDefinitions smart pointer type.
void RemoteGui_createTab(std::string const &name, RemoteGui::Client::Widget const &rootWidget, RemoteGui::Client::Tab *tab)
void addChild(Widget const &child)
Definition Widgets.cpp:95
GridLayout & add(Widget const &child, Pos pos, Span span=Span{1, 1})
Definition Widgets.cpp:438
void setLabel(std::string const &text)
Definition Widgets.cpp:420
InterruptionFailures interruption
Definition types.h:92
InteractionFailures interaction
Definition types.h:91
KnowledgeRetrievalFailures knowledgeRetrieval
Definition types.h:58