MemoryViewer.cpp 34.31 KiB
#include "MemoryViewer.h"
#include <QApplication>
#include <QBoxLayout>
#include <QCheckBox>
#include <QClipboard>
#include <QDialog>
#include <QGroupBox>
#include <QLabel>
#include <QLayout>
#include <QMenu>
#include <QSettings>
#include <QTimer>
#include <Ice/Exception.h>
#include <SimoxUtility/algorithm/get_map_keys_values.h>
#include <ArmarXCore/core/ManagedIceObject.h>
#include <ArmarXCore/core/time/TimeUtil.h>
#include <ArmarXCore/observers/variant/Variant.h>
#include <ArmarXCore/core/ice_conversions/ice_conversions_templates.h>
#include <ArmarXGui/libraries/SimpleConfigDialog/SimpleConfigDialog.h>
#include <RobotAPI/libraries/aron/converter/json/NLohmannJSONConverter.h>
#include <RobotAPI/interface/armem/actions.h>
#include <RobotAPI/interface/armem/memory.h>
#include <RobotAPI/interface/armem/mns/MemoryNameSystemInterface.h>
#include <RobotAPI/libraries/armem/core/container_maps.h>
#include <RobotAPI/libraries/armem/core/wm/ice_conversions.h>
#include <RobotAPI/libraries/armem/server/query_proc/ltm/disk/ltm.h>
#include <RobotAPI/libraries/armem/server/query_proc/wm/wm.h>
#include <RobotAPI/libraries/armem_gui/ActionsMenuBuilder.h>
#include <RobotAPI/libraries/armem_gui/gui_utils.h>
#include <RobotAPI/libraries/armem_gui/instance/AronDataView.h>
#include <RobotAPI/libraries/armem_gui/instance/tree_visitors/TreeTypedJSONConverter.h>
namespace armarx::armem::gui
{
MemoryViewer::MemoryViewer(QBoxLayout* updateWidgetLayout,
QGroupBox* memoryGroupBox,
QLayout* memoryGroupBoxParentLayout,
QGroupBox* instanceGroupBox,
QLayout* instanceGroupBoxParentLayout,
QBoxLayout* diskControlWidgetLayout,
QLabel* statusLabel)
{
Logging::setTag("MemoryViewer");
this->statusLabel = statusLabel;
this->statusLabel->clear();
statusLabel->setContextMenuPolicy(Qt::CustomContextMenu);
connect(statusLabel,
&QLabel::customContextMenuRequested,
[statusLabel](const QPoint& pos)
{
QMenu menu(statusLabel);
menu.addAction("Copy to clipboard",
[statusLabel]()
{ QApplication::clipboard()->setText(statusLabel->text()); });
menu.addAction("Clear status", [statusLabel]() { statusLabel->clear(); });
menu.exec(statusLabel->mapToGlobal(pos));
});
// Update timer
this->updateWidgetLayout = updateWidgetLayout;
updateWidget = new armem::gui::PeriodicUpdateWidget(2.0, 60);
updateWidgetLayout->insertWidget(0, updateWidget);
periodicUpdateTimer = new QTimer(this);
periodicUpdateTimer->setInterval(1000 / 60); // Keep this stable.
// Memory View
auto retrieveEntityInfo = [this](const MemoryID& entityID) -> PredictionWidget::EntityInfo
{
client::Reader reader = memoryReaders.at(entityID.memoryName);
if (!reader.predictionPrx)
{
std::stringstream sstream;
sstream << "Predictions are not available for memory '" << entityID.memoryName
<< "'.";
this->statusLabel->setText(QString::fromStdString(sstream.str()));
return {};
}
std::map<MemoryID, std::vector<PredictionEngine>> predictionEngines;
client::QueryResult queryResult;
try
{
predictionEngines = reader.getAvailablePredictionEngines();
queryResult = reader.queryMemoryIDs({entityID}, armem::query::DataMode::NoData);
}
catch (const Ice::LocalException& e)
{
std::stringstream sstream;
sstream << "Could not get prediction engines and type from memory: "
<< e.what();
this->statusLabel->setText(QString::fromStdString(sstream.str()));
return {nullptr, {}};
}
aron::type::ObjectPtr entityType;
if (queryResult.success)
{
auto* providerSegment = queryResult.memory.findProviderSegment(entityID);
if (providerSegment != nullptr)
{
entityType = providerSegment->aronType();
}
}
return PredictionWidget::EntityInfo{
.type = entityType,
.engines = armem::accumulateEntriesContainingID(predictionEngines, entityID)
};
};
memoryGroup = new armem::gui::MemoryGroupBox(std::move(retrieveEntityInfo));
armarx::gui::replaceWidget(memoryGroupBox, memoryGroup, memoryGroupBoxParentLayout);
ARMARX_CHECK_NULL(memoryGroupBox);
// Instance View
this->instanceGroup = new armem::gui::InstanceGroupBox();
armarx::gui::replaceWidget(instanceGroupBox, instanceGroup, instanceGroupBoxParentLayout);
this->instanceGroup->setStatusLabel(statusLabel);
ARMARX_CHECK_NULL(instanceGroupBox);
// Disk Control
if (diskControlWidgetLayout)
{
this->diskControlLayout = diskControlWidgetLayout;
diskControl = new armem::gui::disk::ControlWidget();
diskControlWidgetLayout->addWidget(diskControl);
}
// Connections
connect(diskControl,
&armem::gui::disk::ControlWidget::requestedStoreOnDisk,
this,
&This::storeOnDisk);
connect(diskControl,
&armem::gui::disk::ControlWidget::requestedLoadFromDisk,
this,
&This::loadFromDisk);
connect(this, &This::connected, this, &This::startQueries);
connect(this, &This::connected, this, &This::startPeriodicUpdateTimer);
connect(updateWidget, &armem::gui::PeriodicUpdateWidget::update, this, &This::startQueries);
connect(periodicUpdateTimer, &QTimer::timeout, this, &This::updateListOfActiveMemories);
connect(periodicUpdateTimer, &QTimer::timeout, this, &This::processQueryResults);
connect(memoryGroup->queryWidget(), &armem::gui::QueryWidget::storeInLTM, this, &This::queryAndStoreInLTM);
connect(memoryGroup->queryWidget(), &armem::gui::QueryWidget::startRecording, this, &This::startLTMRecording);
connect(memoryGroup->queryWidget(), &armem::gui::QueryWidget::stopRecording, this, &This::stopLTMRecording);
connect(memoryGroup->predictionWidget(),
&armem::gui::PredictionWidget::makePrediction,
this,
&This::makePrediction);
connect(
memoryGroup->commitWidget(), &armem::gui::CommitWidget::commit, this, &This::commit);
connect(this, &This::memoryDataChanged, this, &This::updateMemoryTree);
connect(memoryGroup->tree(),
&armem::gui::MemoryTreeWidget::selectedItemChanged,
this,
&This::updateInstanceTree);
connect(memoryGroup->tree(),
&armem::gui::MemoryTreeWidget::updated,
this,
&This::memoryTreeUpdated);
connect(memoryGroup->tree(),
&armem::gui::MemoryTreeWidget::actionsMenuRequested,
this,
&This::showActionsMenu);
connect(instanceGroup,
&armem::gui::InstanceGroupBox::viewUpdated,
this,
&This::instanceTreeUpdated);
connect(instanceGroup->view,
&armem::gui::InstanceView::memoryIdResolutionRequested,
this,
&This::resolveMemoryID);
connect(instanceGroup->view,
&armem::gui::InstanceView::actionsMenuRequested,
this,
&This::showActionsMenu);
}
void
MemoryViewer::setLogTag(const std::string& _tag) // Leading _ silences a warning
{
Logging::setTag(_tag);
}
void
MemoryViewer::onInit(ManagedIceObject& component)
{
if (mnsName.size() > 0)
{
component.usingProxy(mnsName);
}
if (debugObserverName.size() > 0)
{
component.usingProxy(debugObserverName);
}
is_initialized = true;
emit initialized();
}
void
MemoryViewer::onConnect(ManagedIceObject& component)
{
if (not mnsName.empty())
{
armem::mns::MemoryNameSystemInterfacePrx mnsProxy;
component.getProxy(mnsProxy, mnsName);
mns = client::MemoryNameSystem(mnsProxy);
const bool update = true;
memoryReaders = mns.getAllReaders(update);
memoryWriters = mns.getAllWriters(update);
}
// DebugObserver is optional (check for null on every call)
if (not debugObserverName.empty())
{
component.getProxy(debugObserver, debugObserverName, false, "", false);
}
updateWidget->startTimerIfEnabled();
is_connected = true;
emit connected();
}
void
MemoryViewer::onDisconnect(ManagedIceObject&)
{
periodicUpdateTimer->stop();
updateWidget->stopTimer();
is_connected = false;
emit disconnected();
}
void MemoryViewer::startPeriodicUpdateTimer()
{
periodicUpdateTimer->start();
}
const armem::wm::Memory*
MemoryViewer::getSingleMemoryData(const std::string& memoryName)
{
auto it = memoryData.find(memoryName);
if (it == memoryData.end())
{
std::stringstream ss;
ss << "Memory name '" << memoryName
<< "' is unknown. Known are: " << simox::alg::to_string(simox::alg::get_keys(memoryData), ", ");
statusLabel->setText(QString::fromStdString(ss.str()));
return nullptr;
}
/*else if (not it->second.has_value())
{
return nullptr;
}*/
else
{
//return &it->second.value();
return &it->second;
}
}
void
MemoryViewer::queryAndStoreInLTM()
{
TIMING_START(MemoryStore);
auto enabledMemories = memoryGroup->queryWidget()->getEnabledMemories();
for (auto& [name, reader] : memoryReaders)
{
// skip if memory should not be queried
if (std::find(enabledMemories.begin(), enabledMemories.end(), name) == enabledMemories.end())
{
continue;
}
// Query memory
auto q_res = reader.query(memoryGroup->queryInput());
if (q_res.success)
{
server::dto::DirectlyStoreInput input;
input.memory = q_res.toIce().memory;
reader.directlyStore(input);
}
else
{
std::string s = "Query of memory " + name + " was unsuccessful.";
statusLabel->setText(QString::fromStdString(s));
}
}
TIMING_END_STREAM(MemoryStore, ARMARX_VERBOSE);
}
void
MemoryViewer::startLTMRecording()
{
TIMING_START(MemoryStartRecording);
auto enabledMemories = memoryGroup->queryWidget()->getEnabledMemories();
for (auto& [name, reader] : memoryReaders)
{
// skip if memory should not be queried
if (std::find(enabledMemories.begin(), enabledMemories.end(), name) == enabledMemories.end())
{
continue;
}
reader.startRecording();
}
TIMING_END_STREAM(MemoryStartRecording, ARMARX_VERBOSE);
}
void
MemoryViewer::stopLTMRecording()
{
TIMING_START(MemoryStopRecording);
auto enabledMemories = memoryGroup->queryWidget()->getEnabledMemories();
for (auto& [name, reader] : memoryReaders)
{
// skip if memory should not be queried
if (std::find(enabledMemories.begin(), enabledMemories.end(), name) == enabledMemories.end())
{
continue;
}
reader.stopRecording();
}
TIMING_END_STREAM(MemoryStopRecording, ARMARX_VERBOSE);
}
void MemoryViewer::commit()
{
TIMING_START(Commit);
auto now = armem::Time::Now();
const std::string memoryIDStr = memoryGroup->commitWidget()->getMemoryID();
const std::string aronJSONStr = memoryGroup->commitWidget()->getAronJSON();
ARMARX_INFO << "Committing to " << memoryIDStr << " the data: " << aronJSONStr;
MemoryID memId(memoryIDStr);
if (!memId.hasEntityName())
{
ARMARX_WARNING << "The entered MemoryID '" << memoryIDStr << "' does not contain an entity.";
}
else
{
nlohmann::json json = nlohmann::json::parse(aronJSONStr);
// ToDo: multiple objects
auto aron = aron::converter::AronNlohmannJSONConverter::ConvertFromNlohmannJSONObject(json);
if (const auto& it = memoryWriters.find(memId.memoryName); it == memoryWriters.end())
{
ARMARX_WARNING << "No memory with name '" << memId.memoryName << "' available for commit.";
}
else
{
armem::Commit comm;
auto& entityUpdate = comm.add();
entityUpdate.entityID = memId;
entityUpdate.confidence = 1.0;
entityUpdate.instancesData = {aron};
entityUpdate.timeCreated = now;
it->second.commit(comm);
}
}
TIMING_END_STREAM(Commit, ARMARX_VERBOSE);
}
void
MemoryViewer::storeOnDisk(QString directory)
{
TIMING_START(MemoryExport)
std::string status;
std::vector<wm::Memory> memoryDataVec;
std::transform(memoryData.begin(), memoryData.end(), std::back_inserter(memoryDataVec),
[](auto& el) { return el.second; });
diskControl->storeOnDisk(directory, memoryDataVec, &status);
statusLabel->setText(QString::fromStdString(status));
TIMING_END_STREAM(MemoryExport, ARMARX_VERBOSE)
}
void
MemoryViewer::loadFromDisk(QString directory)
{
std::string status;
std::map<std::filesystem::path, wm::Memory> data =
diskControl->loadFromDisk(directory, memoryGroup->queryInput(), &status);
for (auto& [path, memory] : data)
{
std::string name = memory.id().memoryName;
auto commit = armem::toCommit(memory);
if (memoryWriters.count(name) > 0)
{
memoryWriters.at(name).commit(commit);
}
else
{
ARMARX_INFO << "No memory with name '" << name << "' available for commit. Create new virtual memory.";
// Please note: Here we assume that a memory server with the same name does not exist.
// I think this assumption is ok, since nobody should use filepaths as memory name.
// Nonetheless, we did not restrict the user to do so...
std::string virtualMemoryName = name + " (at " + path.string() + ")";
wm::Memory virtualMemory(virtualMemoryName);
virtualMemory.update(commit, true, false);
memoryData[virtualMemoryName] = virtualMemory;
}
}
statusLabel->setText(QString::fromStdString(status));
emit memoryDataChanged();
}
void
MemoryViewer::startQueries()
{
startDueQueries();
}
void
MemoryViewer::processQueryResults()
{
const std::map<std::string, client::QueryResult> results = collectQueryResults();
int errorCount = 0;
applyQueryResults(results, &errorCount);
emit memoryDataChanged();
updateStatusLabel(errorCount);
}
void
MemoryViewer::updateStatusLabel(int errorCount)
{
// Code to output status label information
if (statusLabel and errorCount > 0)
{
auto now = std::chrono::system_clock::now();
auto in_time_t = std::chrono::system_clock::to_time_t(now);
std::stringstream ss;
ss << "Last update: " << std::put_time(std::localtime(&in_time_t), "%Y-%m-%d %X");
ss << "\nThe query produced " << errorCount << " errors! Please check log.";
statusLabel->setText(QString::fromStdString(ss.str()));
}
}
void
MemoryViewer::startDueQueries()
{
armem::client::QueryInput input = memoryGroup->queryInput();
int recursionDepth = memoryGroup->queryWidget()->queryLinkRecursionDepth();
// Can't use a structured binding here because you can't capture those in a lambda
// according to the C++ standard.
auto enabledMemories = memoryGroup->queryWidget()->getEnabledMemories();
for (const auto& pair : memoryReaders)
{
// skip if memory should not be queried
if (std::find(enabledMemories.begin(), enabledMemories.end(), pair.first) == enabledMemories.end())
{
continue;
}
const auto& name = pair.first;
const auto& reader = pair.second;
// skip if query already running
if (runningQueries.count(name) != 0)
{
continue;
}
// You could pass the query function itself to async here,
// but that caused severe template headaches when I tried it.
runningQueries[name] = std::async(std::launch::async,
[reader, input, recursionDepth, this]()
{
// Can't resolve MemoryLinks without data
return recursionDepth == 0 || input.dataMode == armem::query::DataMode::NoData
? reader.query(input.toIce())
: reader.query(input.toIce(), mns, recursionDepth);
});
}
}
std::map<std::string, client::QueryResult>
MemoryViewer::collectQueryResults()
{
TIMING_START(tCollectQueryResults)
std::map<std::string, client::QueryResult> results;
for (auto it = runningQueries.begin(); it != runningQueries.end();)
{
const std::string& name = it->first;
std::future<armem::query::data::Result>* queryPromise = &it->second;
if (queryPromise->wait_for(std::chrono::seconds(0)) == std::future_status::ready)
{
if (auto jt = memoryReaders.find(name); jt != memoryReaders.end())
{
try
{
results[name] = client::QueryResult::fromIce(queryPromise->get());
}
catch (const Ice::ConnectionRefusedException&)
{
// Server is gone (MNS did not know about it yet) => Skip result.
}
}
// else: Server is gone (MNS knew about it) => Skip result.
// Promise is completed => Clean up in any case.
it = runningQueries.erase(it);
}
else
{
++it; // Uncompleted => Keep.
}
}
TIMING_END_STREAM(tCollectQueryResults, ARMARX_VERBOSE)
if (debugObserver)
{
debugObserver->begin_setDebugChannel(
Logging::tag.tagName,
{
{"t Collect Query Results [ms]",
new Variant(tCollectQueryResults.toMilliSecondsDouble())},
{"# Collected Query Results", new Variant(static_cast<int>(results.size()))},
});
}
return results;
}
void
MemoryViewer::applyQueryResults(const std::map<std::string, client::QueryResult>& results, int* outErrorCount)
{
TIMING_START(tProcessQueryResults)
for (const auto& [name, result] : results)
{
if (result.success)
{
memoryData[name] = std::move(result.memory);
}
else
{
ARMARX_WARNING << "Querying memory server '" << name << "' produced an error: \n" << result.errorMessage;
if (outErrorCount)
{
outErrorCount++;
}
}
}
// Perhaps remove entries
auto enabledMemories = memoryGroup->queryWidget()->getEnabledMemories();
for (auto it = memoryData.begin(); it != memoryData.end();)
{
// Drop all entries in memoryData which are not in memoryReaders anymore.
if (memoryReaders.count(it->first) == 0)
{
if (memoryGroup->queryWidget()->dropRemovedMemories())
{
it = memoryData.erase(it);
}
else
{
++it;
}
continue;
}
// Drop all entries that are not enabled by user (which means that there is no query result)
if (std::find(enabledMemories.begin(), enabledMemories.end(), it->first) == enabledMemories.end())
{
if (memoryGroup->queryWidget()->dropDisabledMemories())
{
it = memoryData.erase(it);
}
else
{
++it;
}
continue;
}
// Memory found
++it;
}
TIMING_END_STREAM(tProcessQueryResults, ARMARX_VERBOSE)
if (debugObserver)
{
debugObserver->begin_setDebugChannel(
Logging::tag.tagName,
{
{"t Process Query Results [ms]",
new Variant(tProcessQueryResults.toMilliSecondsDouble())},
{"# Processed Query Results", new Variant(static_cast<int>(results.size()))},
});
}
}
void
MemoryViewer::updateInstanceTree(const armem::MemoryID& selectedID)
{
const armem::wm::Memory* data = getSingleMemoryData(selectedID.memoryName);
if (data)
{
if (not selectedID.hasEntityName())
{
return;
}
armem::MemoryID id = selectedID;
const armem::wm::EntitySnapshot* snapshot = nullptr;
if (not id.hasTimestamp())
{
const armem::wm::Entity& entity = data->getEntity(id);
if (entity.empty())
{
return;
}
snapshot = &entity.getLatestSnapshot();
id.timestamp = snapshot->time();
}
if (not id.hasInstanceIndex())
{
if (not snapshot)
{
try
{
snapshot = &data->getSnapshot(id);
}
catch (const armem::error::ArMemError& e)
{
if (statusLabel)
{
statusLabel->setText(e.what());
}
}
}
if (snapshot && snapshot->size() > 0)
{
id.instanceIndex = 0;
}
}
if (id.hasInstanceIndex())
{
instanceGroup->view->update(id, *data);
}
}
}
void
MemoryViewer::resolveMemoryID(const MemoryID& id)
{
// ARMARX_IMPORTANT << "Resolving memory ID: " << id;
auto handleError = [this](const std::string& msg)
{
statusLabel->setText(QString::fromStdString(msg));
ARMARX_WARNING << msg;
};
if (id.memoryName.empty())
{
handleError("Memory name is empty.");
}
aron::type::ObjectPtr segmentType;
std::optional<wm::EntityInstance> instance;
try
{
if (const wm::Memory* data = getSingleMemoryData(id.memoryName))
{
segmentType = data->getProviderSegment(id).aronType();
if (id.hasInstanceIndex())
{
instance = data->getInstance(id);
}
else if (id.hasTimestamp())
{
instance = data->getSnapshot(id).getInstance(0);
}
else
{
instance = data->getEntity(id).getLatestSnapshot().getInstance(0);
}
}
}
catch (const armem::error::ArMemError&)
{
// May be handled by remote lookup
}
if (not instance)
{
try
{
// Resolve remotely (may still fail, returns an optional).
instance = mns.resolveEntityInstance(id);
}
catch (const armem::error::ArMemError& e)
{
ARMARX_WARNING << e.what();
statusLabel->setText(e.what());
}
}
if (instance)
{
auto* view = new InstanceView();
instanceGroup->view->addDataView(view);
view->update(*instance, segmentType);
//instanceGroup->view->addInstanceView(*instance, segmentType);
}
else
{
// ToDo: Propagate error back to highlight selected entry in red
}
}
void
MemoryViewer::updateListOfActiveMemories()
{
if (is_connected and mns) // mns must be connected and mns must be available
{
try
{
memoryReaders = mns.getAllReaders(true);
memoryWriters = mns.getAllWriters(true);
std::vector<std::string> activeMemoryNames;
// add all active memories to update list
std::transform(memoryReaders.begin(), memoryReaders.end(), std::back_inserter(activeMemoryNames), [](const auto& p){return p.first;});
TIMING_START(GuiUpdateAvailableMemories);
memoryGroup->queryWidget()->update(activeMemoryNames);
TIMING_END_STREAM(GuiUpdateAvailableMemories, ARMARX_VERBOSE);
}
catch (...)
{
// MNS was killed/stopped
// ignore?!
}
}
else
{
ARMARX_VERBOSE << deactivateSpam() << "MNS not ready yet. Skip update of available memories in query widget.";
}
}
void
MemoryViewer::updateMemoryTree()
{
std::map<std::string, const armem::wm::Memory*> memoriesToUpdate;
//auto checkboxStates = memoryGroup->queryWidget()->getAvailableMemoryStates();
for (auto& [name, data] : memoryData)
{
memoriesToUpdate[name] = &data;
}
TIMING_START(GuiUpdateMemoryTree)
memoryGroup->tree()->update(memoriesToUpdate);
TIMING_END_STREAM(GuiUpdateMemoryTree, ARMARX_VERBOSE)
if (debugObserver)
{
try
{
debugObserver->setDebugDatafield(Logging::tag.tagName,
"GUI Update [ms]",
new Variant(GuiUpdateMemoryTree.toMilliSecondsDouble()));
}
catch (const Ice::Exception&)
{
// Ignore ...
}
}
}
void
MemoryViewer::showActionsMenu(const MemoryID& memoryID,
QWidget* parent,
const QPoint& pos,
QMenu* menu)
{
// Called if we have to stop because of an error.
auto showMenu = [menu, pos]()
{
if (menu)
menu->exec(pos);
};
if (memoryID == MemoryID())
{
// Empty MemoryID, don't try to generate actions.
showMenu();
return;
}
mns::dto::MemoryServerInterfaces prx;
try
{
prx = mns.resolveServer(memoryID);
}
catch (const error::CouldNotResolveMemoryServer& e)
{
statusLabel->setText(
QString::fromStdString(
e.makeMsg(memoryID, "Could not resolve memory server.")));
showMenu();
return;
}
if (!prx.actions)
{
std::stringstream ss;
ss << "Memory server " << memoryID
<< " does not support actions or is offline.";
statusLabel->setText(QString::fromStdString(ss.str()));
showMenu();
return;
}
actions::GetActionsOutputSeq result;
try
{
result = prx.actions->getActions({{armarx::toIce<data::MemoryID>(memoryID)}});
}
catch (const Ice::LocalException& e)
{
std::stringstream ss;
ss << "Could not get actions for " << memoryID << ".";
statusLabel->setText(QString::fromStdString(ss.str()));
showMenu();
return;
}
if (result.size() == 0)
{
showMenu();
return;
}
auto builder = ActionsMenuBuilder(
memoryID,
parent,
[this, prx](const MemoryID& memoryID, const actions::ActionPath& path)
{
actions::data::ExecuteActionOutputSeq result;
try
{
result = prx.actions->executeActions(
{{armarx::toIce<armem::data::MemoryID>(memoryID), path}});
}
catch (const Ice::LocalException& e)
{
std::stringstream ss;
ss << "Failed to execute action: " << e.what();
statusLabel->setText(QString::fromStdString(ss.str()));
}
for (const auto& [success, errorMessage] : result)
{
if (not success)
{
std::stringstream ss;
ss << "Failed to execute action: " << errorMessage;
statusLabel->setText(QString::fromStdString(ss.str()));
ARMARX_WARNING << ss.str();
}
}
});
QMenu* actionsMenu = builder.buildActionsMenu(result[0]);
if (menu == nullptr)
{
actionsMenu->exec(pos);
}
else
{
menu->addMenu(actionsMenu);
menu->exec(pos);
}
}
void
MemoryViewer::makePrediction(const MemoryID& entityID,
const aron::type::ObjectPtr& entityType,
const armarx::DateTime& timestamp,
const std::string& engineID)
{
std::stringstream errorStream;
auto showError = [this, &errorStream]()
{
statusLabel->setText(QString::fromStdString(errorStream.str()));
};
if (!entityID.hasEntityName() || entityID.hasGap())
{
errorStream << "Could not convert " << entityID << " to valid entity ID.";
showError();
return;
}
if (memoryReaders.find(entityID.memoryName) == memoryReaders.end())
{
errorStream << "Not connected to memory '" << entityID.memoryName
<< "', cannot make prediction.";
showError();
return;
}
client::Reader reader = memoryReaders.at(entityID.memoryName);
if (!reader.predictionPrx)
{
errorStream << "Predictions are not available for memory '" << entityID.memoryName
<< "'.";
showError();
return;
}
PredictionRequest request;
request.snapshotID = entityID.withTimestamp(timestamp);
request.predictionSettings.predictionEngineID = engineID;
PredictionResult result;
try
{
result = reader.predict({request}).at(0);
}
catch (const Ice::LocalException& e)
{
errorStream << "Could not make prediction request: " << e.what();
showError();
return;
}
if (!result.success)
{
errorStream << "Prediction failed: " << result.errorMessage;
showError();
return;
}
auto* view = new AronDataView();
instanceGroup->view->addDataView(view);
view->update(result.prediction, entityType);
}
const static std::string CONFIG_KEY_MEMORY = "MemoryViewer.MemoryNameSystem";
const static std::string CONFIG_KEY_DEBUG_OBSERVER = "MemoryViewer.DebugObserverName";
void
MemoryViewer::loadSettings(QSettings* settings)
{
mnsName = settings->value(QString::fromStdString(CONFIG_KEY_MEMORY), "MemoryNameSystem")
.toString()
.toStdString();
debugObserverName =
settings->value(QString::fromStdString(CONFIG_KEY_DEBUG_OBSERVER), "DebugObserver")
.toString()
.toStdString();
}
void
MemoryViewer::saveSettings(QSettings* settings)
{
settings->setValue(QString::fromStdString(CONFIG_KEY_MEMORY),
QString::fromStdString(mnsName));
settings->setValue(QString::fromStdString(CONFIG_KEY_DEBUG_OBSERVER),
QString::fromStdString(debugObserverName));
}
void
MemoryViewer::writeConfigDialog(SimpleConfigDialog* dialog)
{
dialog->addProxyFinder<armarx::armem::mns::MemoryNameSystemInterfacePrx>(
{CONFIG_KEY_MEMORY, "MemoryNameSystem", "MemoryNameSystem"});
dialog->addProxyFinder<armarx::DebugObserverInterfacePrx>(
{CONFIG_KEY_DEBUG_OBSERVER, "Debug Observer", "DebugObserver"});
}
void
MemoryViewer::readConfigDialog(SimpleConfigDialog* dialog)
{
mnsName = dialog->getProxyName(CONFIG_KEY_MEMORY);
if (mnsName.empty())
{
mnsName = "MemoryNameSystem";
}
debugObserverName = dialog->getProxyName(CONFIG_KEY_DEBUG_OBSERVER);
}
} // namespace armarx::armem::gui