SkillDashboard.cpp
Go to the documentation of this file.
1
2#include "SkillDashboard.h"
3
4#include <cstddef>
5#include <cstdint>
6#include <fstream>
7#include <iostream>
8#include <mutex>
9#include <optional>
10#include <shared_mutex>
11#include <sstream>
12#include <string>
13#include <thread>
14#include <unordered_map>
15
16#include <boost/locale.hpp>
17#include <boost/uuid/name_generator.hpp>
18#include <boost/uuid/nil_generator.hpp>
19#include <boost/uuid/string_generator.hpp>
20#include <boost/uuid/uuid.hpp>
21#include <boost/uuid/uuid_generators.hpp>
22#include <boost/uuid/uuid_io.hpp>
23
24#include <IceUtil/Optional.h>
25
26#include <nlohmann/json.hpp>
27#include <nlohmann/json_fwd.hpp>
28
34
36#include <RobotAPI/interface/skills/SkillManagerInterface.h>
37#include <RobotAPI/interface/skills/SkillMemoryInterface.h>
38
39namespace armarx
40{
41
44 {
46 new ::armarx::ComponentPropertyDefinitions(getConfigIdentifier());
47 def->optional(properties.shortcutPath,
48 "ShortcutPath",
49 "Add path of predefined dashboards. Syntax: Package/folderName/fileName");
50 return def;
51 }
52
54 SkillDashboard::getTimeout(const std::string& skillId)
55 {
56 constexpr bool addToDependencies = false;
57 const auto& managerPrx =
58 getProxy<skills::dti::SkillMemoryInterfacePrx>("SkillMemory", addToDependencies);
59
60 if (!managerPrx)
61 {
63 }
64
65 const auto& splitIt = skillId.find('/');
66 std::string provider = "";
67 std::string nameSkill = "";
68
69 if (splitIt == std::string::npos)
70 {
71 // TODO: error handling
73 }
74 provider = skillId.substr(0, splitIt);
75 nameSkill = skillId.substr(splitIt + 1);
76 skills::manager::dto::ProviderID providerId{.providerName = provider};
77
78 skills::manager::dto::SkillID skillIdDTO{.providerId = providerId, .skillName = nameSkill};
79
80 IceUtil::Optional<armarx::skills::manager::dto::SkillDescription> skillDescDTO =
81 managerPrx->getSkillDescription(skillIdDTO);
82
83 if (!skillDescDTO)
84 {
86 }
87 const auto& dto = *skillDescDTO;
88 return armarx::core::time::Duration::MicroSeconds(dto.timeout.microSeconds);
89 }
90
91 void
92 SkillDashboard::updateTimeoutForShortcut(const SkillShortcut& shortcut)
93 {
94 armarx::core::time::Duration timeout = getTimeout(shortcut.skillId);
95 std::unique_lock timeoutsLock(shortcutNameToTimeoutsMutex);
96 this->shortcutNameToTimeouts[shortcut.shortcutName] = timeout;
97 timeoutsLock.unlock();
98 }
99
100 bool
101 SkillDashboard::loadShortcuts(const std::string& packagepath)
102 {
103 std::scoped_lock l(registeredShortcutsMutex);
104 this->registeredShortcuts = std::vector<SkillShortcut>();
105 std::stringstream ss(packagepath);
106 std::string item;
107 std::vector<std::string> result;
108 while (std::getline(ss, item, '/'))
109 {
110 result.push_back(item);
111 }
112 if (result.size() < 3)
113 {
114 ARMARX_ERROR << "Invalid property structure!";
115 return false;
116 }
117 this->packageName = result[0];
118
119 std::string absoluteFilepath;
120 std::string filename;
121 armarx::CMakePackageFinder finder(result[0]);
122 if (finder.packageFound())
123 {
124 ARMARX_INFO << "Found package.";
125
126 std::string packageDataDir = finder.getDataDir();
127
128 // add the data directory to the search list of ArmarXDataPath
129 ArmarXDataPath::addDataPaths(packageDataDir);
130 //std::filesystem::create_directory(packageDataDir + "/" + folder);
131
132 std::string relativeFilename;
133 for (size_t i = 1; i < result.size() - 1; i++)
134 {
135 if (!relativeFilename.empty())
136 {
137 relativeFilename += "/";
138 }
139 relativeFilename += result[i];
140 }
141
142 this->pathToDashboard = relativeFilename;
143 this->dahsboardName = result[result.size() - 1];
144
145 if (ArmarXDataPath::getAbsolutePath(relativeFilename, absoluteFilepath))
146 {
147 ARMARX_INFO << "Located file at:" << absoluteFilepath;
148 filename = result[result.size() - 1] + ".json";
149 }
150
151
152 std::ifstream file(absoluteFilepath + "/" + filename);
153 nlohmann::json jsonData;
154
155 if (!file.is_open())
156 {
157 ARMARX_WARNING << "Could not open file";
158 }
159
160 file >> jsonData;
161 boost::uuids::name_generator generator(boost::uuids::nil_uuid());
162
163 for (const auto& item : jsonData["shortcuts"])
164 {
165 SkillShortcut shortcut;
166 shortcut.skillArgs = item["skill_args"].dump(2);
167 shortcut.skillId = item["skill_id"];
168 if (item.contains("icon_name"))
169 {
170 shortcut.iconName = item["icon_name"];
171 }
172 else
173 {
174 shortcut.iconName = "";
175 }
176 shortcut.shortcutName = item["skill_shortcut_name"];
177 if (item.contains("id"))
178 {
179 shortcut.id = item["id"];
180 }
181 else
182 {
183 shortcut.id = boost::uuids::to_string(generator(shortcut.shortcutName));
184 }
185
186 this->registeredShortcuts.push_back(shortcut);
187 }
188 return true;
189 }
190 else
191 {
192 ARMARX_WARNING << "Configured ArmarX package not found for '" << result[0] << "'.";
193 return false;
194 }
195 }
196
197 void
199 {
200 offeringTopic("SkillDashboardCoverTopic");
201 this->properties.shortcutPath = getProperty<std::string>("ShortcutPath").getValue();
202
203 if (not properties.shortcutPath.empty())
204 {
205 loadShortcuts(properties.shortcutPath);
206 }
207 }
208
209 void
211 {
212 ARMARX_DEBUG << "Skills dashboard component connected.";
213 coverTopicPrx =
214 getTopic<armarx::SkillDashboardCoverListenerPrx>("SkillDashboardCoverTopic");
215 }
216
217 SkillShortcutList
218 SkillDashboard::getShortcuts(const ::Ice::Current&)
219 {
220 ARMARX_DEBUG << "Getting shortcuts … (registered are " << this->registeredShortcuts.size()
221 << ")";
222 return this->registeredShortcuts;
223 }
224
225 SkillShortcutWithTimeoutList
227 {
228 ARMARX_DEBUG << "Getting shortcuts with timeout …";
229 std::vector<SkillShortcutWithTimeout> shortcutsWithTimeout;
230 std::scoped_lock l(registeredShortcutsMutex);
231 for (const auto& shortcut : this->registeredShortcuts)
232 {
233 this->updateTimeoutForShortcut(shortcut);
234 SkillShortcutWithTimeout shortcutWithTimeout;
235 shortcutWithTimeout.shortcutName = shortcut.shortcutName;
236 shortcutWithTimeout.skillId = shortcut.skillId;
237 shortcutWithTimeout.skillArgs = shortcut.skillArgs;
238 shortcutWithTimeout.iconName = shortcut.iconName;
239
240 std::shared_lock timeoutsLock(shortcutNameToTimeoutsMutex);
241 shortcutWithTimeout.timeout.microSeconds =
242 this->shortcutNameToTimeouts[shortcut.shortcutName].toMicroSeconds();
243 timeoutsLock.unlock();
244
245 shortcutsWithTimeout.push_back(shortcutWithTimeout);
246 }
247 return shortcutsWithTimeout;
248 }
249
250 SkillShortcut
251 SkillDashboard::getShortcut(const std::string& name, const ::Ice::Current&)
252 {
253 std::scoped_lock l(registeredShortcutsMutex);
254 for (const SkillShortcut& shortcut : this->registeredShortcuts)
255 {
256 if (shortcut.shortcutName == name)
257 {
258 return shortcut;
259 }
260 }
261
262 return SkillShortcut();
263 }
264
265 std::optional<SkillShortcut>
266 SkillDashboard::getShortcutWithId(const std::string& id)
267 {
268 std::scoped_lock l(registeredShortcutsMutex);
269 for (const SkillShortcut& shortcut : this->registeredShortcuts)
270 {
271 if (shortcut.id == id)
272 {
273 return shortcut;
274 }
275 }
276
277 return std::nullopt;
278 }
279
280 void
281 SkillDashboard::addNewShortcut(const SkillShortcut& newShortcut, const ::Ice::Current&)
282 {
283 if (newShortcut.shortcutName == "")
284 {
285 return;
286 }
287 if (newShortcut.id == "" || getShortcutWithId(newShortcut.id) == std::nullopt)
288 {
289 if (getShortcut(newShortcut.shortcutName).shortcutName == "")
290 {
291 boost::uuids::name_generator generator(boost::uuids::nil_uuid());
292
293 SkillShortcut toAddShortcut;
294 toAddShortcut.shortcutName = newShortcut.shortcutName;
295 toAddShortcut.skillArgs = newShortcut.skillArgs;
296 toAddShortcut.skillId = newShortcut.skillId;
297 toAddShortcut.iconName = newShortcut.iconName;
298 toAddShortcut.id = boost::uuids::to_string(generator(newShortcut.shortcutName));
299
300 std::unique_lock l(registeredShortcutsMutex);
301 this->registeredShortcuts.push_back(toAddShortcut);
302 l.unlock();
303 this->updateTimeoutForShortcut(toAddShortcut);
304 ARMARX_INFO << "Added shortcut " << newShortcut.shortcutName;
305 }
306 else
307 {
308 ARMARX_INFO << "Shortcut name already exists";
309 }
310 }
311 else
312 {
313 std::optional<SkillShortcut> updatedShortcut;
314 std::unique_lock l(registeredShortcutsMutex);
315 for (SkillShortcut& shortcut : this->registeredShortcuts)
316 {
317 if (shortcut.id == newShortcut.id)
318 {
319 shortcut.shortcutName = newShortcut.shortcutName;
320 shortcut.skillId = newShortcut.skillId;
321 shortcut.skillArgs = newShortcut.skillArgs;
322 shortcut.iconName = newShortcut.iconName;
323
324 updatedShortcut = shortcut;
325 }
326 }
327 l.unlock();
328 if (updatedShortcut)
329 {
330 this->updateTimeoutForShortcut(*updatedShortcut);
331 }
332 ARMARX_INFO << "changed shortcut";
333 }
334 }
335
336 void
337 SkillDashboard::deleteShortcut(const std::string& name, const ::Ice::Current&)
338 {
339 std::scoped_lock l(registeredShortcutsMutex);
340 for (auto it = this->registeredShortcuts.begin(); it != this->registeredShortcuts.end();
341 ++it)
342 {
343 if (it->shortcutName == name)
344 {
345 this->registeredShortcuts.erase(it);
346 std::unique_lock timeoutsLock(shortcutNameToTimeoutsMutex);
347 this->shortcutNameToTimeouts.erase(name);
348 timeoutsLock.unlock();
349 break;
350 }
351 }
352 }
353
354 void
355 SkillDashboard::saveShortcutOrder(const std::vector<std::string>& order, const ::Ice::Current&)
356 {
357 ARMARX_IMPORTANT << "Save shortcut order";
358 this->shortcutOrder = order;
359 }
360
361 void
363 {
364 std::unordered_map<std::string, std::size_t> index;
365 for (std::size_t i = 0; i < this->shortcutOrder.size(); ++i)
366 {
367 index[this->shortcutOrder[i]] = i;
368 }
369
370 std::sort(this->registeredShortcuts.begin(),
371 this->registeredShortcuts.end(),
372 [&](const auto& a, const auto& b)
373 { return index[a.shortcutName] < index[b.shortcutName]; });
374 }
375
376 std::vector<std::string>
377 SkillDashboard::getPathStructure(const ::Ice::Current&)
378 {
379 return std::vector<std::string>{
380 this->packageName, this->pathToDashboard, this->dahsboardName};
381 }
382
383 bool
384 SkillDashboard::importShortcuts(const std::string& package,
385 const std::string& folder,
386 const std::string& name,
387 const ::Ice::Current&)
388 {
389 std::string path = package + "/" + folder + "/" + name;
390 return loadShortcuts(path);
391 }
392
393 void
394 SkillDashboard::exportShortcuts(const std::string& package,
395 const std::string& folder,
396 const std::string& name,
397 const ::Ice::Current&)
398 {
399 nlohmann::json j;
400
401 std::unordered_map<std::string, SkillShortcut> lookup;
402 lookup.reserve(registeredShortcuts.size());
403 std::shared_lock shortcutsLock(registeredShortcutsMutex);
404 for (const auto& s : registeredShortcuts)
405 {
406 lookup[s.shortcutName] = s;
407 }
408 shortcutsLock.unlock();
409
410
411 for (const auto& name : this->shortcutOrder)
412 {
413
414 auto it = lookup.find(name);
415 if (it == lookup.end())
416 {
417 ARMARX_IMPORTANT << "Shortcut does not exist.";
418 continue;
419 }
420 const auto& shortcut = it->second;
421
422 nlohmann::json skillArgs_json = nlohmann::json::parse(shortcut.skillArgs);
423
424 j["shortcuts"].push_back({
425 {"skill_shortcut_name", shortcut.shortcutName},
426 {"skill_id", shortcut.skillId},
427 {"skill_args", skillArgs_json},
428 {"icon_name", shortcut.iconName},
429 });
430 }
431
432 std::string absoluteFilename;
433 armarx::CMakePackageFinder finder(package);
434 if (finder.packageFound())
435 {
436
437 std::string packageDataDir = finder.getDataDir();
438
439 // add the data directory to the search list of ArmarXDataPath
440 ArmarXDataPath::addDataPaths(packageDataDir);
441 std::filesystem::create_directories(packageDataDir + "/" + folder);
442
443 std::string relativeFilename(folder);
444
445 if (ArmarXDataPath::getAbsolutePath(relativeFilename, absoluteFilename))
446 {
447 ARMARX_INFO << "Located file at:" << absoluteFilename;
448 std::string filename = name + ".json";
449 std::ofstream outFile(absoluteFilename + "/" + filename);
450 outFile << std::setw(4) << j << std::endl;
451 outFile.close();
452 ARMARX_INFO << "Insert " << package << "/" << folder << "/" << name
453 << " into the component property.";
454 }
455 }
456 }
457
458 void
462
463 void
467
469} // namespace armarx
#define ARMARX_REGISTER_COMPONENT_EXECUTABLE(ComponentT, applicationName)
Definition Decoupled.h:29
uint8_t index
static bool getAbsolutePath(const std::string &relativeFilename, std::string &storeAbsoluteFilename, const std::vector< std::string > &additionalSearchPaths={}, bool verbose=true)
static void addDataPaths(const std::string &dataPathList)
The CMakePackageFinder class provides an interface to the CMake Package finder capabilities.
bool packageFound() const
Returns whether or not this package was found with cmake.
std::string getConfigIdentifier()
Retrieve config identifier for this component as set in constructor.
Definition Component.cpp:88
Property< PropertyType > getProperty(const std::string &name)
void offeringTopic(const std::string &name)
Registers a topic for retrival after initialization.
TopicProxyType getTopic(const std::string &name)
Returns a proxy of the specified topic.
Ice::ObjectPrx getProxy(long timeoutMs=0, bool waitForScheduler=true) const
Returns the proxy of this object (optionally it waits for the proxy)
SkillShortcutWithTimeoutList getShortcutsWithTimeout(const ::Ice::Current &=::Ice::Current()) override
void onInitComponent() override
bool importShortcuts(const std::string &package, const std::string &folder, const std::string &name, const ::Ice::Current &=::Ice::Current()) override
void deleteShortcut(const std::string &name, const ::Ice::Current &=::Ice::Current()) override
void syncOrderedWithDefault(const ::Ice::Current &) override
void addNewShortcut(const SkillShortcut &newShortcut, const ::Ice::Current &=::Ice::Current()) override
void onDisconnectComponent() override
SkillShortcut getShortcut(const std::string &name, const ::Ice::Current &=::Ice::Current()) override
SkillShortcutList getShortcuts(const ::Ice::Current &=::Ice::Current()) override
armarx::PropertyDefinitionsPtr createPropertyDefinitions() override
void saveShortcutOrder(const std::vector< std::string > &order, const ::Ice::Current &=::Ice::Current()) override
void exportShortcuts(const std::string &package, const std::string &folder, const std::string &name, const ::Ice::Current &=::Ice::Current()) override
void onConnectComponent() override
std::vector< std::string > getPathStructure(const ::Ice::Current &=::Ice::Current()) override
void onExitComponent() override
Represents a duration.
Definition Duration.h:17
static Duration MicroSeconds(std::int64_t microSeconds)
Constructs a duration in microseconds.
Definition Duration.cpp:24
static Duration Seconds(std::int64_t seconds)
Constructs a duration in seconds.
Definition Duration.cpp:72
#define ARMARX_INFO
The normal logging level.
Definition Logging.h:179
#define ARMARX_IMPORTANT
The logging level for always important information, but expected behaviour (in contrast to ARMARX_WAR...
Definition Logging.h:188
#define ARMARX_ERROR
The logging level for unexpected behaviour, that must be fixed.
Definition Logging.h:194
#define ARMARX_DEBUG
The logging level for output that is only interesting while debugging.
Definition Logging.h:182
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
Definition Logging.h:191
This file offers overloads of toIce() and fromIce() functions for STL container types.
IceUtil::Handle< class PropertyDefinitionContainer > PropertyDefinitionsPtr
PropertyDefinitions smart pointer type.