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