SkillManagerWrapper.cpp
Go to the documentation of this file.
2
3#include <mutex>
4#include <optional>
5
6#include <SimoxUtility/algorithm/string/string_tools.h>
7
9
11
12namespace armarx::skills::gui
13{
14 using StatusMap = std::map<skills::SkillExecutionID, skills::SkillStatusUpdate>;
15 using SkillMap =
16 std::map<skills::ProviderID, std::map<skills::SkillID, skills::SkillDescription>>;
17
19 SkillManagerWrapper::fetchExecutionsFromMemory()
20 {
21 if (!memory)
22 {
23 // check if null
24 return {};
25 }
26
27 try
28 {
29 std::scoped_lock l(mutex_memory);
30
31 // we return this map
32 StatusMap statusMap;
33
34 auto currentManagerStatuses =
35 memory->ice_invocationTimeout(10000)->getSkillExecutionStatuses();
36
37 // iterate over raw data and convert to common types
38 for (const auto& [k, v] : currentManagerStatuses)
39 {
40 auto executionId = skills::SkillExecutionID::FromIce(k);
41 auto statusUpdate = skills::SkillStatusUpdate::FromIce(v);
42
43 // update maps
44 statusMap[executionId] = statusUpdate;
45 }
46 return statusMap;
47 }
48 catch (Ice::Exception const& e)
49 {
51 << "Unhandled Ice exception encountered while updating executions. Exception was: "
52 << e;
53 emit connectionUpdate("Could not fetch executions", e.what());
54 emit disableAutoUpdate();
55 return {};
56 }
57 catch (...)
58 {
59 ARMARX_WARNING << "Unknown exception encountered while updating executions.";
60 return {};
61 }
62 }
63
64 // check if search strings occur in the skill name in order
65 bool
66 matches(std::string skillName, std::vector<std::string>& searches)
67 {
68 size_t index = 0;
69 for (std::string& substring : searches)
70 {
71 size_t occurance = skillName.find(substring, index);
72 if (occurance == std::string::npos)
73 {
74 return false;
75 }
76
77
78 // we found an occurance
79 index = occurance;
80 }
81 return true;
82 }
83
84 SkillManagerWrapper::Snapshot
85 SkillManagerWrapper::filterUpdate(Snapshot update)
86 {
87 // empty search => do not filter
88 if (this->currentSkillSearch.empty())
89 {
90 return update;
91 }
92
93 Snapshot filtered;
94
95 // for now we don't change the executions
96 filtered.statuses = update.statuses;
97
98 std::vector<std::string> substrings;
99
100 {
101 std::vector<std::string> rawSubstrings = simox::alg::split(currentSkillSearch);
102 for (auto& string : rawSubstrings)
103 {
104 substrings.push_back(simox::alg::to_lower(string));
105 }
106 }
107
108
109 for (auto& provider_and_descrMap : update.skills)
110 {
111 using DescriptionMap = std::map<skills::SkillID, skills::SkillDescription>;
112
113 DescriptionMap& descriptionMap = provider_and_descrMap.second;
114 skills::ProviderID provider = provider_and_descrMap.first;
115
116 for (auto& skill_and_description : descriptionMap)
117 {
118 skills::SkillID sid = skill_and_description.first;
119 skills::SkillDescription descr = skill_and_description.second;
120
121 if (matches(simox::alg::to_lower(sid.skillName), substrings))
122 {
123 // add to map
124 filtered.skills[provider][sid] = descr;
125 }
126 }
127 }
128
129 return filtered;
130 }
131
133 SkillManagerWrapper::fetchSkillsFromMemory()
134 {
135 if (!memory)
136 {
137 return {};
138 }
139
140 try
141 {
142 std::scoped_lock l(mutex_memory);
143
144 SkillMap skills;
145
146 auto managerSkills = memory->ice_invocationTimeout(5000)->getSkillDescriptions();
147
148 for (const auto& [sid, desc] : managerSkills)
149 {
150 auto description = skills::SkillDescription::FromIce(desc);
151 auto skillId = skills::SkillID::FromIce(sid);
152 auto providerId = skillId.providerId.value_or(
153 skills::ProviderID{.providerName = "UNKNOWN PROVIDER NAME"});
154
155 ARMARX_CHECK(skillId.isFullySpecified());
156
157 auto& providedSkillsMap = skills[providerId]; // create new if not existing
158 providedSkillsMap.insert({skillId, description});
159 }
160
161 return skills;
162 }
163 catch (Ice::Exception const& e)
164 {
166 << "Unhandled Ice exception encountered while updating skills. Exception was: "
167 << e;
168 emit connectionUpdate("Could not fetch skills", e.what());
169 emit disableAutoUpdate();
170 return {};
171 }
172 catch (...)
173 {
174 ARMARX_WARNING << "Unknown exception encountered while updating skills.";
175 return {};
176 }
177 }
178
179 void
181 skills::manager::dti::SkillManagerInterfacePrx const& updatedMemory)
182 {
183 std::scoped_lock l(mutex_memory);
184 this->memory = updatedMemory;
185 }
186
187 void
189 {
190 // clear memory
191
192 std::scoped_lock l(mutex_memory);
193 this->memory = nullptr;
194 }
195
196 void
198 {
199 this->currentSkillSearch = search;
200 emit searchAccepted();
201 }
202
203 void
205 {
206 ARMARX_IMPORTANT << "Stopping all running executions.";
207
208 StatusMap executions;
209
210 // we ALWAYS want the newest information when stopping all!
211 // e.g. there is some new skill not known to the GUI which we explicitely want to stop too.
212 // the stop-all function is often used in an emergency, so we'll live with the extra call...
213 try
214 {
215 executions = this->fetchExecutionsFromMemory();
216 }
217 catch (...) // if any error occurs, we use the snapshot as backup. better to miss a skill
218 // than to not do anything.
219 {
220 executions = this->getExecutions();
221 }
222
223 for (auto& [executionId, status] : executions)
224 {
225 // select all running executions...
226 if (!status.hasBeenTerminated())
227 {
228 // ... and kill them.
229 this->stopExecution(executionId, 3);
230 }
231 }
232 }
233
234 void
236 {
237 std::scoped_lock l(mutex_snapshot);
238
239 snapshot.skills = fetchSkillsFromMemory();
240 snapshot.statuses = fetchExecutionsFromMemory();
241
242 // notify registered widgets of update
243 emit updateAvailable(filterUpdate(snapshot));
244 }
245
246 std::vector<Parameters>
248 {
249 if (this->skillParameterExecutionHistory.find(sid) ==
250 this->skillParameterExecutionHistory.end())
251 {
252 return {};
253 }
254 return this->skillParameterExecutionHistory[sid];
255 }
256
257 void
259 const Parameters& params)
260 {
261 if (this->skillParameterExecutionHistory.find(sid) ==
262 this->skillParameterExecutionHistory.end())
263 {
264 // no history exists for this skill. Assign new
265 this->skillParameterExecutionHistory.insert({sid, {params}});
266 }
267 else
268 {
269 // history already exists. Append to list
270 this->skillParameterExecutionHistory[sid].emplace_back(params);
271 }
272 }
273
274 std::optional<Parameters>
276 {
277 auto parameterList = getParameterHistoryForSkill(sid);
278 if (parameterList.empty())
279 {
280 return std::nullopt;
281 }
282 return parameterList.back();
283 }
284
285 const std::optional<ProviderID>
287 {
288 // check if id already contains a provider. If so, this function should not have been called!
289 if (skillId.isProviderSpecified())
290 {
291 ARMARX_WARNING << "The memory snapshot was searched for any provider, when a provider "
292 "was specified.";
293 // we continue, but this might result in unexpected behaviour...
294 }
295
296 for (auto& [prov, skillMap] : map)
297 {
298 for (auto& [skill, desc] : skillMap)
299 {
300 if (skill == skillId)
301 {
302 return prov;
303 }
304 }
305 }
306 return std::nullopt;
307 }
308
311 {
312 std::scoped_lock l(mutex_snapshot);
313
314 Snapshot filtered = filterUpdate(snapshot);
315
316 return filtered.skills;
317 }
318
321 {
322 std::scoped_lock l(mutex_snapshot);
323
324 return snapshot.skills;
325 }
326
329 {
330 std::scoped_lock l(mutex_snapshot);
331 return snapshot.statuses;
332 }
333
334 void
336 const unsigned int max_retries)
337 {
338 // memory???
339 if (!memory)
340 {
341 return;
342 }
343
344 unsigned int retries = max_retries;
345 bool retry = false;
346
347 do
348 {
349 try
350 {
351 ARMARX_INFO << "Aborting skill '" << executionId.skillId.skillName << "'...";
352 std::scoped_lock l(mutex_memory);
353 this->memory->ice_invocationTimeout(5000)->abortSkillAsync(
354 executionId.toManagerIce());
355 }
356 catch (Ice::Exception const& e)
357 {
358 retry = true;
359 ARMARX_ERROR << "Unhandeled Ice exception while aborting skill '"
360 << executionId.skillId.skillName << "'.";
361 emit connectionUpdate("Could not abort skill " + executionId.skillId.skillName,
362 e.what());
363 }
364 catch (...)
365 {
366 retry = true;
367 ARMARX_ERROR << "Unhandled error while aborting skill '"
368 << executionId.skillId.skillName << "'.";
369 }
370
371 if (retry)
372 {
373 retries -= 1;
374
375 if (retries > 0)
376 {
377 ARMARX_WARNING << "There where errors aborting skills. Retrying...";
378 }
379 else
380 {
381 ARMARX_ERROR << "Couldn't abort all skills after " << max_retries
382 << " tries. Giving up.";
383 retry = false;
384 }
385 }
386 } while (retry);
387 }
388
389 void
391 aron::data::DictPtr const params)
392 {
393 // Memory???
394 if (!memory)
395 {
396 return;
397 }
398
399 auto providerId = skillId.providerId;
400 if (!providerId.has_value())
401 {
402 ARMARX_IMPORTANT << "The skill: '" << skillId.skillName
403 << "' has been requested to be executed, but no provider was "
404 "given. Aborting...";
405 return;
406 }
407
408 std::map<skills::SkillID, skills::SkillDescription> skillDescriptions;
409 if (this->UPDATE_ON_EXECUTION_REQUEST)
410 {
411 skillDescriptions = this->fetchSkillsFromMemory().at(providerId.value());
412 }
413 else
414 {
415 skillDescriptions = this->getSkills().at(providerId.value());
416 }
417
418 if (skillDescriptions.find(skillId) == skillDescriptions.end())
419 {
420 ARMARX_IMPORTANT << "The Skill: '" << skillId.skillName
421 << "' has been requested to be executed, but no skill description was "
422 "found. Aborting...";
423 return;
424 }
425
426 char hostname[HOST_NAME_MAX];
427 gethostname(hostname, HOST_NAME_MAX);
428
430 .skillId = skillId,
431 .executorName = "Skills.Manager GUI (hostname: " + std::string(hostname) + ")",
432 .parameters = params};
433
434 ARMARX_CHECK(skillId.isFullySpecified()); // sanity check
435
436 ARMARX_IMPORTANT << "Executing skill from GUI: " << skillId << ".";
437
438 try
439 {
440 std::scoped_lock l(mutex_memory);
441 memory->ice_invocationTimeout(5000)->executeSkillAsync(req.toManagerIce());
442 }
443 catch (Ice::Exception const& e)
444 {
445 ARMARX_ERROR << "Unhandeled Ice exception while executing skill '" << skillId.skillName
446 << "'. Aborting...";
447 emit connectionUpdate("Execution failed of skill " + skillId.skillName, e.what());
448 }
449 catch (...)
450 {
451 ARMARX_ERROR << "Unhandled error while executing skill '" << skillId.skillName
452 << "'. Aborting...";
453 }
454 }
455
456
457} // namespace armarx::skills::gui
uint8_t index
Brief description of class memory.
Definition memory.h:39
bool isProviderSpecified() const
Definition SkillID.cpp:90
std::optional< ProviderID > providerId
Definition SkillID.h:40
bool isFullySpecified() const
Definition SkillID.cpp:78
static SkillID FromIce(const manager::dto::SkillID &)
Definition SkillID.cpp:36
std::string skillName
Definition SkillID.h:41
void stopAllExecutions()
Stops all available (and running) executions.
void disconnectMemory()
Disconnects the interface from memory.
static const std::optional< skills::ProviderID > findFirstProvider(SkillMap const &map, SkillID const &skillId)
StatusMap getExecutions()
Returns the latest status snapshot.
void connectMemory(skills::manager::dti::SkillManagerInterfacePrx const &updatedMemory)
Updates the memory pointer.
void acceptSearchRequest(std::string const &search)
Applies the search word to the update filter.
SkillMap getSkillsFiltered()
Returns the latest skills snapshot.
SkillMap getSkills()
Returns the latest skills snapshot.
void stopExecution(skills::SkillExecutionID const &executionId, const unsigned int max_retries=0)
Attempts to stop an execution.
void startExecutionWithParams(skills::SkillID &skillId, aron::data::DictPtr const params)
Attempts to start an execution with given parameters.
void connectionUpdate(std::string const &message, std::string const &error)
void addParametersToHistory(const skills::SkillID &sid, const Parameters &params)
std::optional< Parameters > getLatestParametersForSkill(const skills::SkillID &sid)
void updateFromMemory()
Requests this wrapper to overwrite its own state from memory.
std::vector< Parameters > getParameterHistoryForSkill(const skills::SkillID &sid)
#define ARMARX_CHECK(expression)
Shortcut for ARMARX_CHECK_EXPRESSION.
#define ARMARX_INFO
The normal logging level.
Definition Logging.h:181
#define ARMARX_IMPORTANT
The logging level for always important information, but expected behaviour (in contrast to ARMARX_WAR...
Definition Logging.h:190
#define ARMARX_ERROR
The logging level for unexpected behaviour, that must be fixed.
Definition Logging.h:196
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
Definition Logging.h:193
bool update(mongocxx::collection &coll, const nlohmann::json &query, const nlohmann::json &update)
Definition mongodb.cpp:68
std::shared_ptr< Dict > DictPtr
Definition Dict.h:42
bool matches(std::string skillName, std::vector< std::string > &searches)
std::map< skills::SkillExecutionID, skills::SkillStatusUpdate > StatusMap
aron::data::DictPtr Parameters
std::map< skills::ProviderID, std::map< skills::SkillID, skills::SkillDescription > > SkillMap
static SkillDescription FromIce(const provider::dto::SkillDescription &i, const std::optional< ProviderID > &=std::nullopt)
static SkillExecutionID FromIce(const skills::manager::dto::SkillExecutionID &)
skills::manager::dto::SkillExecutionID toManagerIce() const
static SkillStatusUpdate FromIce(const provider::dto::SkillStatusUpdate &update, const std::optional< skills::ProviderID > &providerId=std::nullopt)