Fawkes API  Fawkes Development Version
clips_robot_memory_thread.cpp
1 
2 /***************************************************************************
3  * clips_robot_memory_thread.cpp - CLIPS feature to access the robot memory
4  *
5  * Created: Mon Aug 29 15:41:47 2016
6  * Copyright 2016 Frederik Zwilling
7  * 2013-2018 Tim Niemueller [www.niemueller.de]
8  ****************************************************************************/
9 
10 /* This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU Library General Public License for more details.
19  *
20  * Read the full text in the LICENSE.GPL file in the doc directory.
21  */
22 
23 #include "clips_robot_memory_thread.h"
24 
25 #include <core/threading/mutex_locker.h>
26 #include <plugins/mongodb/utils.h>
27 
28 #include <bsoncxx/document/element.hpp>
29 #include <bsoncxx/exception/exception.hpp>
30 #include <bsoncxx/types.hpp>
31 #include <mongocxx/exception/exception.hpp>
32 
33 using namespace fawkes;
34 
35 /** @class ClipsRobotMemoryThread 'clips_robot_memory_thread.h'
36  * CLIPS feature to access the robot memory.
37  * MongoDB access through CLIPS first appeared in the RCLL referee box.
38  * @author Tim Niemueller
39  * @author Frederik Zwilling
40  */
41 
42 ClipsRobotMemoryThread::ClipsRobotMemoryThread()
43 : Thread("ClipsRobotMemoryThread", Thread::OPMODE_WAITFORWAKEUP),
44  CLIPSFeature("robot_memory"),
45  CLIPSFeatureAspect(this)
46 {
47 }
48 
49 void
51 {
52 }
53 
54 void
56 {
57 }
58 
59 void
61 {
62  envs_.clear();
63  for (ClipsRmTrigger *trigger : clips_triggers_) {
64  delete trigger;
65  }
66 }
67 
68 void
69 ClipsRobotMemoryThread::clips_context_init(const std::string & env_name,
71 {
72  envs_[env_name] = clips;
73  logger->log_debug(name(), "Called to initialize environment %s", env_name.c_str());
74 
75  clips.lock();
76 
77  clips->add_function("bson-create",
78  sigc::slot<CLIPS::Value>(
79  sigc::mem_fun(*this, &ClipsRobotMemoryThread::clips_bson_create)));
80  clips->add_function("bson-parse",
81  sigc::slot<CLIPS::Value, std::string>(
82  sigc::mem_fun(*this, &ClipsRobotMemoryThread::clips_bson_parse)));
83  clips->add_function("bson-destroy",
84  sigc::slot<void, void *>(
85  sigc::mem_fun(*this, &ClipsRobotMemoryThread::clips_bson_destroy)));
86  clips->add_function("bson-append",
87  sigc::slot<void, void *, std::string, CLIPS::Value>(
88  sigc::mem_fun(*this, &ClipsRobotMemoryThread::clips_bson_append)));
89  clips->add_function("bson-append-regex",
90  sigc::slot<void, void *, std::string, CLIPS::Value>(
91  sigc::mem_fun(*this, &ClipsRobotMemoryThread::clips_bson_append_regex)));
92  clips->add_function("bson-append-array",
93  sigc::slot<void, void *, std::string, CLIPS::Values>(
94  sigc::mem_fun(*this, &ClipsRobotMemoryThread::clips_bson_append_array)));
95  clips->add_function("bson-array-start",
96  sigc::slot<CLIPS::Value>(
97  sigc::mem_fun(*this, &ClipsRobotMemoryThread::clips_bson_array_start)));
98  clips->add_function("bson-array-finish",
99  sigc::slot<void, void *, std::string, void *>(
100  sigc::mem_fun(*this, &ClipsRobotMemoryThread::clips_bson_array_finish)));
101  clips->add_function("bson-array-append",
102  sigc::slot<void, void *, CLIPS::Value>(
103  sigc::mem_fun(*this, &ClipsRobotMemoryThread::clips_bson_array_append)));
104 
105  clips->add_function("bson-append-time",
106  sigc::slot<void, void *, std::string, CLIPS::Values>(
107  sigc::mem_fun(*this, &ClipsRobotMemoryThread::clips_bson_append_time)));
108  clips->add_function("bson-tostring",
109  sigc::slot<std::string, void *>(
110  sigc::mem_fun(*this, &ClipsRobotMemoryThread::clips_bson_tostring)));
111  clips->add_function("robmem-insert",
112  sigc::slot<void, std::string, void *>(
113  sigc::mem_fun(*this, &ClipsRobotMemoryThread::clips_robotmemory_insert)));
114  clips->add_function("robmem-upsert",
115  sigc::slot<void, std::string, void *, CLIPS::Value>(
116  sigc::mem_fun(*this, &ClipsRobotMemoryThread::clips_robotmemory_upsert)));
117  clips->add_function("robmem-update",
118  sigc::slot<void, std::string, void *, CLIPS::Value>(
119  sigc::mem_fun(*this, &ClipsRobotMemoryThread::clips_robotmemory_update)));
120  clips->add_function("robmem-replace",
121  sigc::slot<void, std::string, void *, CLIPS::Value>(
122  sigc::mem_fun(*this, &ClipsRobotMemoryThread::clips_robotmemory_replace)));
123  clips->add_function("robmem-query",
124  sigc::slot<CLIPS::Value, std::string, void *>(
125  sigc::mem_fun(*this, &ClipsRobotMemoryThread::clips_robotmemory_query)));
126  clips->add_function("robmem-remove",
127  sigc::slot<void, std::string, void *>(
128  sigc::mem_fun(*this, &ClipsRobotMemoryThread::clips_robotmemory_remove)));
129  clips->add_function("robmem-query-sort",
130  sigc::slot<CLIPS::Value, std::string, void *, void *>(
131  sigc::mem_fun(*this,
132  &ClipsRobotMemoryThread::clips_robotmemory_query_sort)));
133  clips->add_function("robmem-dump-collection",
134  sigc::slot<CLIPS::Value, std::string, std::string>(
135  sigc::mem_fun(*this,
136  &ClipsRobotMemoryThread::clips_robotmemory_dump_collection)));
137  clips->add_function("robmem-restore-collection",
138  sigc::slot<CLIPS::Value, std::string, std::string, std::string>(sigc::mem_fun(
139  *this, &ClipsRobotMemoryThread::clips_robotmemory_restore_collection)));
140  clips->add_function("robmem-cursor-destroy",
141  sigc::slot<void, void *>(
142  sigc::mem_fun(*this,
143  &ClipsRobotMemoryThread::clips_robotmemory_cursor_destroy)));
144  clips->add_function("robmem-cursor-more",
145  sigc::slot<CLIPS::Value, void *>(
146  sigc::mem_fun(*this,
147  &ClipsRobotMemoryThread::clips_robotmemory_cursor_more)));
148  clips->add_function("robmem-cursor-next",
149  sigc::slot<CLIPS::Value, void *>(
150  sigc::mem_fun(*this,
151  &ClipsRobotMemoryThread::clips_robotmemory_cursor_next)));
152  clips->add_function("robmem-trigger-register",
153  sigc::slot<CLIPS::Value, std::string, void *, std::string>(sigc::bind<0>(
154  sigc::mem_fun(*this,
155  &ClipsRobotMemoryThread::clips_robotmemory_register_trigger),
156  env_name)));
157  clips->add_function("robmem-trigger-destroy",
158  sigc::slot<void, void *>(
159  sigc::mem_fun(*this,
160  &ClipsRobotMemoryThread::clips_robotmemory_destroy_trigger)));
161  clips->add_function("bson-field-names",
162  sigc::slot<CLIPS::Values, void *>(
163  sigc::mem_fun(*this, &ClipsRobotMemoryThread::clips_bson_field_names)));
164  clips->add_function("bson-has-field",
165  sigc::slot<CLIPS::Value, void *, std::string>(
166  sigc::mem_fun(*this, &ClipsRobotMemoryThread::clips_bson_has_field)));
167  clips->add_function("bson-get",
168  sigc::slot<CLIPS::Value, void *, std::string>(
169  sigc::mem_fun(*this, &ClipsRobotMemoryThread::clips_bson_get)));
170  clips->add_function("bson-get-array",
171  sigc::slot<CLIPS::Values, void *, std::string>(
172  sigc::mem_fun(*this, &ClipsRobotMemoryThread::clips_bson_get_array)));
173  clips->add_function("bson-get-time",
174  sigc::slot<CLIPS::Values, void *, std::string>(
175  sigc::mem_fun(*this, &ClipsRobotMemoryThread::clips_bson_get_time)));
176  clips->add_function("robmem-create-index",
177  sigc::slot<void, std::string, void *>(
178  sigc::mem_fun(*this,
179  &ClipsRobotMemoryThread::clips_robotmemory_create_index)));
180  clips->add_function("robmem-create-unique-index",
181  sigc::slot<void, std::string, void *>(sigc::mem_fun(
182  *this, &ClipsRobotMemoryThread::clips_robotmemory_create_unique_index)));
183 
184  clips->add_function("robmem-mutex-create",
185  sigc::slot<CLIPS::Value, std::string>(
186  sigc::mem_fun(*this,
187  &ClipsRobotMemoryThread::clips_robotmemory_mutex_create)));
188  clips->add_function("robmem-mutex-destroy",
189  sigc::slot<CLIPS::Value, std::string>(
190  sigc::mem_fun(*this,
191  &ClipsRobotMemoryThread::clips_robotmemory_mutex_destroy)));
192  clips->add_function("robmem-mutex-try-lock",
193  sigc::slot<CLIPS::Value, std::string, std::string>(
194  sigc::mem_fun(*this,
195  &ClipsRobotMemoryThread::clips_robotmemory_mutex_try_lock)));
196  clips->add_function("robmem-mutex-renew-lock",
197  sigc::slot<CLIPS::Value, std::string, std::string>(sigc::mem_fun(
198  *this, &ClipsRobotMemoryThread::clips_robotmemory_mutex_renew_lock)));
199  clips->add_function("robmem-mutex-force-lock",
200  sigc::slot<CLIPS::Value, std::string, std::string>(sigc::mem_fun(
201  *this, &ClipsRobotMemoryThread::clips_robotmemory_mutex_force_lock)));
202  clips->add_function("robmem-mutex-unlock",
203  sigc::slot<CLIPS::Value, std::string, std::string>(
204  sigc::mem_fun(*this,
205  &ClipsRobotMemoryThread::clips_robotmemory_mutex_unlock)));
206  clips->add_function("robmem-mutex-setup-ttl",
207  sigc::slot<CLIPS::Value, float>(
208  sigc::mem_fun(*this,
209  &ClipsRobotMemoryThread::clips_robotmemory_mutex_setup_ttl)));
210  clips->add_function("robmem-mutex-expire-locks",
211  sigc::slot<CLIPS::Value, float>(sigc::mem_fun(
212  *this, &ClipsRobotMemoryThread::clips_robotmemory_mutex_expire_locks)));
213 
214  clips->add_function("robmem-mutex-create-async",
215  sigc::slot<CLIPS::Values, std::string>(sigc::mem_fun(
216  *this, &ClipsRobotMemoryThread::clips_robotmemory_mutex_create_async)));
217  clips->add_function("robmem-mutex-destroy-async",
218  sigc::slot<CLIPS::Values, std::string>(sigc::mem_fun(
219  *this, &ClipsRobotMemoryThread::clips_robotmemory_mutex_destroy_async)));
220  clips->add_function(
221  "robmem-mutex-try-lock-async",
222  sigc::slot<CLIPS::Values, std::string, std::string>(sigc::bind<0>(
223  sigc::mem_fun(*this, &ClipsRobotMemoryThread::clips_robotmemory_mutex_try_lock_async),
224  env_name)));
225  clips->add_function(
226  "robmem-mutex-renew-lock-async",
227  sigc::slot<CLIPS::Values, std::string, std::string>(sigc::bind<0>(
228  sigc::mem_fun(*this, &ClipsRobotMemoryThread::clips_robotmemory_mutex_renew_lock_async),
229  env_name)));
230  clips->add_function("robmem-mutex-force-lock-async",
231  sigc::slot<CLIPS::Values, std::string, std::string>(sigc::mem_fun(
232  *this, &ClipsRobotMemoryThread::clips_robotmemory_mutex_force_lock_async)));
233  clips->add_function("robmem-mutex-unlock-async",
234  sigc::slot<CLIPS::Values, std::string, std::string>(sigc::mem_fun(
235  *this, &ClipsRobotMemoryThread::clips_robotmemory_mutex_unlock_async)));
236  clips->add_function(
237  "robmem-mutex-expire-locks-async",
238  sigc::slot<CLIPS::Value, float>(sigc::bind<0>(
239  sigc::mem_fun(*this, &ClipsRobotMemoryThread::clips_robotmemory_mutex_expire_locks_async),
240  env_name)));
241 
242  clips->build("(deffacts have-feature-mongodb (have-feature MongoDB))");
243 
244  //load helper functions written in CLIPS
245  clips->batch_evaluate(SRCDIR "/robot-memory.clp");
246 
247  clips.unlock();
248 }
249 
250 void
252 {
253  envs_.erase(env_name);
254  logger->log_debug(name(), "Removing environment %s", env_name.c_str());
255 }
256 
257 CLIPS::Value
258 ClipsRobotMemoryThread::clips_bson_create()
259 {
260  return CLIPS::Value(new bsoncxx::builder::basic::document());
261 }
262 
263 CLIPS::Value
264 ClipsRobotMemoryThread::clips_bson_parse(std::string document)
265 {
266  auto b = new bsoncxx::builder::basic::document();
267  try {
268  b->append(bsoncxx::builder::concatenate(bsoncxx::from_json(document)));
269  } catch (bsoncxx::exception &e) {
270  logger->log_error("MongoDB", "Parsing JSON doc failed: %s\n%s", e.what(), document.c_str());
271  }
272  return CLIPS::Value(b);
273 }
274 
275 void
276 ClipsRobotMemoryThread::clips_bson_destroy(void *bson)
277 {
278  auto b = static_cast<bsoncxx::builder::basic::document *>(bson);
279  delete b;
280 }
281 
282 std::string
283 ClipsRobotMemoryThread::clips_bson_tostring(void *bson)
284 {
285  auto b = static_cast<bsoncxx::builder::basic::document *>(bson);
286  return bsoncxx::to_json(b->view());
287 }
288 
289 void
290 ClipsRobotMemoryThread::clips_bson_append(void *bson, std::string field_name, CLIPS::Value value)
291 {
292  using namespace bsoncxx::builder;
293  try {
294  auto b = static_cast<basic::document *>(bson);
295  switch (value.type()) {
296  case CLIPS::TYPE_FLOAT: b->append(basic::kvp(field_name, value.as_float())); break;
297 
298  case CLIPS::TYPE_INTEGER:
299  b->append(basic::kvp(field_name, static_cast<int64_t>(value.as_integer())));
300  break;
301 
302  case CLIPS::TYPE_SYMBOL:
303  case CLIPS::TYPE_INSTANCE_NAME:
304  case CLIPS::TYPE_STRING: b->append(basic::kvp(field_name, value.as_string())); break;
305  case CLIPS::TYPE_EXTERNAL_ADDRESS: {
306  auto subb = static_cast<basic::document *>(value.as_address());
307  b->append(basic::kvp(field_name, subb->view()));
308  } break;
309 
310  default:
311  logger->log_warn("RefBox", "Tried to add unknown type to BSON field %s", field_name.c_str());
312  break;
313  }
314  } catch (bsoncxx::exception &e) {
315  logger->log_error("MongoDB",
316  "Failed to append array value to field %s: %s",
317  field_name.c_str(),
318  e.what());
319  }
320 }
321 
322 void
323 ClipsRobotMemoryThread::clips_bson_append_regex(void * bson,
324  std::string field_name,
325  CLIPS::Value regex_string)
326 {
327  using namespace bsoncxx::builder;
328  if (regex_string.type() != CLIPS::TYPE_STRING) {
329  logger->log_error("MongoDB", "Regex string has to be of type string");
330  return;
331  }
332  try {
333  auto b = static_cast<basic::document *>(bson);
334  b->append(basic::kvp(field_name, bsoncxx::types::b_regex{regex_string.as_string()}));
335  } catch (bsoncxx::exception &e) {
336  logger->log_error("MongoDB",
337  "Failed to append regex to field %s: %s",
338  field_name.c_str(),
339  e.what());
340  }
341 }
342 
343 void
344 ClipsRobotMemoryThread::clips_bson_append_array(void * bson,
345  std::string field_name,
346  CLIPS::Values values)
347 {
348  using namespace bsoncxx::builder;
349  try {
350  auto b = static_cast<basic::document *>(bson);
351 
352  b->append(basic::kvp(field_name, [&](basic::sub_array array) {
353  for (auto value : values) {
354  switch (value.type()) {
355  case CLIPS::TYPE_FLOAT: array.append(value.as_float()); break;
356 
357  case CLIPS::TYPE_INTEGER: array.append(static_cast<int64_t>(value.as_integer())); break;
358 
359  case CLIPS::TYPE_SYMBOL:
360  case CLIPS::TYPE_STRING:
361  case CLIPS::TYPE_INSTANCE_NAME: array.append(value.as_string()); break;
362 
363  case CLIPS::TYPE_EXTERNAL_ADDRESS: {
364  auto subb = static_cast<bsoncxx::builder::basic::document *>(value.as_address());
365  array.append(subb->view());
366  } break;
367 
368  default:
369  logger->log_warn("MongoDB",
370  "Tried to add unknown type to BSON array field %s",
371  field_name.c_str());
372  break;
373  }
374  }
375  }));
376  } catch (bsoncxx::exception &e) {
377  logger->log_error("MongoDB",
378  "Failed to append array value to field %s: %s",
379  field_name.c_str(),
380  e.what());
381  }
382 }
383 
384 CLIPS::Value
385 ClipsRobotMemoryThread::clips_bson_array_start()
386 {
387  return CLIPS::Value(new bsoncxx::builder::basic::array{});
388 }
389 
390 void
391 ClipsRobotMemoryThread::clips_bson_array_finish(void *bson, std::string field_name, void *array)
392 {
393  using namespace bsoncxx::builder;
394  auto doc = static_cast<basic::document *>(bson);
395  auto array_doc = static_cast<bsoncxx::builder::basic::array *>(array);
396  doc->append(basic::kvp(field_name, array_doc->view()));
397  delete array_doc;
398 }
399 
400 void
401 ClipsRobotMemoryThread::clips_bson_array_append(void *array, CLIPS::Value value)
402 {
403  using namespace bsoncxx::builder;
404  auto array_doc = static_cast<bsoncxx::builder::basic::array *>(array);
405  switch (value.type()) {
406  case CLIPS::TYPE_FLOAT: array_doc->append(value.as_float()); break;
407 
408  case CLIPS::TYPE_INTEGER: array_doc->append(static_cast<int64_t>(value.as_integer())); break;
409 
410  case CLIPS::TYPE_SYMBOL:
411  case CLIPS::TYPE_STRING:
412  case CLIPS::TYPE_INSTANCE_NAME: array_doc->append(value.as_string()); break;
413 
414  case CLIPS::TYPE_EXTERNAL_ADDRESS: {
415  auto subb = static_cast<basic::document *>(value.as_address());
416  array_doc->append(subb->view());
417  } break;
418  default:
419  logger->log_warn("MongoDB", "bson-array-append: tried to add unknown type to BSON array field");
420  break;
421  }
422 }
423 
424 void
425 ClipsRobotMemoryThread::clips_bson_append_time(void * bson,
426  std::string field_name,
427  CLIPS::Values time)
428 {
429  if (time.size() != 2) {
430  logger->log_warn("MongoDB", "Invalid time, %zu instead of 2 entries", time.size());
431  return;
432  }
433  if (time[0].type() != CLIPS::TYPE_INTEGER || time[1].type() != CLIPS::TYPE_INTEGER) {
434  logger->log_warn("MongoDB", "Invalid time, type mismatch");
435  return;
436  }
437 
438  try {
439  auto b = static_cast<bsoncxx::builder::basic::document *>(bson);
440  struct timeval now = {time[0].as_integer(), time[1].as_integer()};
441  bsoncxx::types::b_date nowd{
442  std::chrono::time_point<std::chrono::system_clock, std::chrono::milliseconds>{
443  std::chrono::milliseconds{now.tv_sec * 1000 + now.tv_usec / 1000}}};
444  b->append(bsoncxx::builder::basic::kvp(field_name, nowd));
445  } catch (bsoncxx::exception &e) {
446  logger->log_error("MongoDB",
447  "Failed to append time value to field %s: %s",
448  field_name.c_str(),
449  e.what());
450  }
451 }
452 
453 void
454 ClipsRobotMemoryThread::clips_robotmemory_insert(std::string collection, void *bson)
455 {
456  auto b = static_cast<bsoncxx::builder::basic::document *>(bson);
457 
458  try {
459  robot_memory->insert(b->view(), collection);
460  } catch (mongocxx::exception &e) {
461  logger->log_warn("MongoDB", "Insert failed: %s", e.what());
462  }
463 }
464 
465 void
466 ClipsRobotMemoryThread::clips_robotmemory_create_index(std::string collection, void *bson)
467 {
468  auto b = static_cast<bsoncxx::builder::basic::document *>(bson);
469 
470  try {
471  robot_memory->create_index(b->view(), collection, /* unique */ false);
472  } catch (mongocxx::exception &e) {
473  logger->log_warn("MongoDB", "Creating index failed: %s", e.what());
474  }
475 }
476 
477 void
478 ClipsRobotMemoryThread::clips_robotmemory_create_unique_index(std::string collection, void *bson)
479 {
480  auto b = static_cast<bsoncxx::builder::basic::document *>(bson);
481 
482  try {
483  robot_memory->create_index(b->view(), collection, /* unique */ true);
484  } catch (mongocxx::exception &e) {
485  logger->log_warn("MongoDB", "Creating unique index failed: %s", e.what());
486  }
487 }
488 
489 void
490 ClipsRobotMemoryThread::robotmemory_update(std::string & collection,
491  const bsoncxx::document::view &update,
492  CLIPS::Value & query,
493  bool upsert)
494 {
495  try {
496  if (query.type() == CLIPS::TYPE_STRING) {
497  robot_memory->update(bsoncxx::from_json(query.as_string()), update, collection, upsert);
498  } else if (query.type() == CLIPS::TYPE_EXTERNAL_ADDRESS) {
499  bsoncxx::builder::basic::document *qb =
500  static_cast<bsoncxx::builder::basic::document *>(query.as_address());
501  robot_memory->update(qb->view(), update, collection, upsert);
502  } else {
503  logger->log_warn("MongoDB", "Invalid query, must be string or BSON document");
504  return;
505  }
506 
507  } catch (bsoncxx::exception &e) {
508  logger->log_warn("MongoDB", "Compiling query failed: %s", e.what());
509  } catch (mongocxx::exception &e) {
510  logger->log_warn("MongoDB", "Insert failed: %s", e.what());
511  }
512 }
513 
514 void
515 ClipsRobotMemoryThread::clips_robotmemory_upsert(std::string collection,
516  void * bson,
517  CLIPS::Value query)
518 {
519  auto b = static_cast<bsoncxx::builder::basic::document *>(bson);
520  if (!b) {
521  logger->log_warn("MongoDB", "Invalid BSON Builder passed");
522  return;
523  }
524  robotmemory_update(collection, b->view(), query, true);
525 }
526 
527 void
528 ClipsRobotMemoryThread::clips_robotmemory_update(std::string collection,
529  void * bson,
530  CLIPS::Value query)
531 {
532  auto b = static_cast<bsoncxx::builder::basic::document *>(bson);
533  if (!b) {
534  logger->log_warn("MongoDB", "Invalid BSON Builder passed");
535  return;
536  }
537  robotmemory_update(collection, b->view(), query, false);
538 }
539 
540 void
541 ClipsRobotMemoryThread::clips_robotmemory_replace(std::string collection,
542  void * bson,
543  CLIPS::Value query)
544 {
545  auto b = static_cast<bsoncxx::builder::basic::document *>(bson);
546  if (!b)
547  logger->log_warn("MongoDB", "Invalid BSON Builder passed");
548  robotmemory_update(collection, b->view(), query, false);
549 }
550 
551 CLIPS::Value
552 ClipsRobotMemoryThread::clips_robotmemory_query_sort(std::string collection,
553  void * bson,
554  void * bson_sort)
555 {
556  auto b = static_cast<bsoncxx::builder::basic::document *>(bson);
557 
558  try {
559  mongocxx::options::find find_opts{};
560  if (bson_sort) {
561  auto *bs = static_cast<bsoncxx::builder::basic::document *>(bson_sort);
562  find_opts.sort(bs->view());
563  }
564 
565  auto cursor = robot_memory->query(b->view(), collection, find_opts);
566  //std::unique_ptr<mongocxx::cursor> c = new mongocxx::cursor(std::move(cursor));
567  return CLIPS::Value(new std::unique_ptr<mongocxx::cursor>(
568  new mongocxx::cursor(std::move(cursor))),
569  CLIPS::TYPE_EXTERNAL_ADDRESS);
570  } catch (std::system_error &e) {
571  logger->log_warn("MongoDB", "Query failed: %s", e.what());
572  return CLIPS::Value("FALSE", CLIPS::TYPE_SYMBOL);
573  }
574 }
575 
576 CLIPS::Value
577 ClipsRobotMemoryThread::clips_robotmemory_dump_collection(std::string collection,
578  std::string directory)
579 {
580  try {
581  int succ = robot_memory->dump_collection(collection, directory);
582  if (succ == 1) {
583  return CLIPS::Value("TRUE", CLIPS::TYPE_SYMBOL);
584  } else {
585  return CLIPS::Value("FALSE", CLIPS::TYPE_SYMBOL);
586  }
587  } catch (mongocxx::exception &e) {
588  logger->log_error("MongoDB",
589  "Dumping collection %s to %s failed: \n %s",
590  collection.c_str(),
591  directory.c_str(),
592  e.what());
593  return CLIPS::Value("FALSE", CLIPS::TYPE_SYMBOL);
594  }
595 }
596 
597 CLIPS::Value
598 ClipsRobotMemoryThread::clips_robotmemory_restore_collection(std::string collection,
599  std::string directory,
600  std::string target_collection)
601 {
602  try {
603  int succ = robot_memory->restore_collection(collection, directory, target_collection);
604  if (succ == 1) {
605  return CLIPS::Value("TRUE", CLIPS::TYPE_SYMBOL);
606  } else {
607  return CLIPS::Value("FALSE", CLIPS::TYPE_SYMBOL);
608  }
609  } catch (mongocxx::exception &e) {
610  logger->log_error("MongoDB",
611  "Restoring collection %s to %s failed: \n %s",
612  collection.c_str(),
613  directory.c_str(),
614  e.what());
615  return CLIPS::Value("FALSE", CLIPS::TYPE_SYMBOL);
616  }
617 }
618 
619 void
620 ClipsRobotMemoryThread::clips_robotmemory_remove(std::string collection, void *bson)
621 {
622  auto b = static_cast<bsoncxx::builder::basic::document *>(bson);
623  try {
624  robot_memory->remove(b->view(), collection);
625  } catch (std::system_error &e) {
626  logger->log_warn("MongoDB", "Remove failed: %s", e.what());
627  }
628 }
629 
630 CLIPS::Value
631 ClipsRobotMemoryThread::clips_robotmemory_query(const std::string &collection, void *bson)
632 {
633  return clips_robotmemory_query_sort(collection, bson, NULL);
634 }
635 
636 void
637 ClipsRobotMemoryThread::clips_robotmemory_cursor_destroy(void *cursor)
638 {
639  auto c = static_cast<std::unique_ptr<mongocxx::cursor> *>(cursor);
640  if (!c || !c->get()) {
641  logger->log_error("MongoDB", "mongodb-cursor-destroy: got invalid cursor");
642  return;
643  }
644 
645  delete c;
646 }
647 
648 CLIPS::Value
649 ClipsRobotMemoryThread::clips_robotmemory_cursor_more(void *cursor)
650 {
651  throw Exception("The function cursor-more is no longer supported. Call cursor-next and check the "
652  "return value for FALSE instead.");
653 }
654 
655 CLIPS::Value
656 ClipsRobotMemoryThread::clips_robotmemory_cursor_next(void *cursor)
657 {
658  auto c = static_cast<std::unique_ptr<mongocxx::cursor> *>(cursor);
659 
660  if (!c || !c->get()) {
661  logger->log_error("MongoDB", "mongodb-cursor-next: got invalid cursor");
662  return CLIPS::Value("FALSE", CLIPS::TYPE_SYMBOL);
663  }
664 
665  try {
666  auto it = (*c)->begin();
667  if (it == (*c)->end()) {
668  return CLIPS::Value("FALSE", CLIPS::TYPE_SYMBOL);
669  } else {
670  auto b = new bsoncxx::builder::basic::document();
671  b->append(bsoncxx::builder::concatenate(*it));
672  it++;
673  return CLIPS::Value(b);
674  }
675  } catch (std::system_error &e) {
676  logger->log_error("MongoDB", "mongodb-cursor-next: got invalid query: %s", e.what());
677  return CLIPS::Value("FALSE", CLIPS::TYPE_SYMBOL);
678  }
679 }
680 
681 CLIPS::Values
682 ClipsRobotMemoryThread::clips_bson_field_names(void *bson)
683 {
684  auto b = static_cast<bsoncxx::builder::basic::document *>(bson);
685 
686  if (!b) {
687  logger->log_error("MongoDB", "mongodb-bson-field-names: invalid object");
688  CLIPS::Values rv;
689  rv.push_back(CLIPS::Value("FALSE", CLIPS::TYPE_SYMBOL));
690  return rv;
691  }
692 
693  CLIPS::Values rv;
694  for (auto element : b->view()) {
695  rv.push_back(CLIPS::Value(std::string(element.key())));
696  }
697  return rv;
698 }
699 
700 CLIPS::Value
701 ClipsRobotMemoryThread::clips_bson_get(void *bson, std::string field_name)
702 {
703  auto b = static_cast<bsoncxx::builder::basic::document *>(bson);
704 
705  if (!b) {
706  logger->log_error("MongoDB", "mongodb-bson-get: invalid object");
707  return CLIPS::Value("FALSE", CLIPS::TYPE_SYMBOL);
708  }
709 
710  try {
711  auto el = get_dotted_field(b->view(), field_name);
712  if (!el) {
713  logger->log_warn(name(),
714  "mongodb-bson-get: failed to get '%s', no such element in doc: %s",
715  field_name.c_str(),
716  bsoncxx::to_json(b->view()).c_str());
717  return CLIPS::Value("FALSE", CLIPS::TYPE_SYMBOL);
718  }
719  switch (el.type()) {
720  case bsoncxx::type::k_double: return CLIPS::Value(el.get_double());
721  case bsoncxx::type::k_utf8: return CLIPS::Value(el.get_utf8().value.to_string());
722  case bsoncxx::type::k_bool:
723  return CLIPS::Value(el.get_bool() ? "TRUE" : "FALSE", CLIPS::TYPE_SYMBOL);
724  case bsoncxx::type::k_int32: return CLIPS::Value(el.get_int32());
725  case bsoncxx::type::k_int64: return CLIPS::Value(el.get_int64());
726  case bsoncxx::type::k_document: {
727  auto b = new bsoncxx::builder::basic::document();
728  b->append(bsoncxx::builder::concatenate(el.get_document().view()));
729  return CLIPS::Value(b);
730  }
731  case bsoncxx::type::k_oid: //ObjectId
732  return CLIPS::Value(el.get_oid().value.to_string());
733 
734  default: return CLIPS::Value("INVALID_VALUE_TYPE", CLIPS::TYPE_SYMBOL);
735  }
736  } catch (std::system_error &e) {
737  logger->log_warn(name(),
738  "mongodb-bson-get: failed to get '%s': %s",
739  field_name.c_str(),
740  e.what());
741  return CLIPS::Value("FALSE", CLIPS::TYPE_SYMBOL);
742  }
743 }
744 
745 CLIPS::Value
746 ClipsRobotMemoryThread::clips_bson_has_field(void *bson, std::string field_name)
747 {
748  auto b = static_cast<bsoncxx::builder::basic::document *>(bson);
749 
750  if (!b) {
751  logger->log_error("MongoDB", "mongodb-bson-get: invalid object");
752  return CLIPS::Value("FALSE", CLIPS::TYPE_SYMBOL);
753  }
754 
755  try {
756  auto el = get_dotted_field(b->view(), field_name);
757  if (!el.key().empty()) {
758  return CLIPS::Value("TRUE", CLIPS::TYPE_SYMBOL);
759  } else {
760  return CLIPS::Value("FALSE", CLIPS::TYPE_SYMBOL);
761  }
762  } catch (bsoncxx::exception &e) {
763  return CLIPS::Value("FALSE", CLIPS::TYPE_SYMBOL);
764  }
765 }
766 
767 CLIPS::Values
768 ClipsRobotMemoryThread::clips_bson_get_array(void *bson, std::string field_name)
769 {
770  auto b = static_cast<bsoncxx::builder::basic::document *>(bson);
771 
772  CLIPS::Values rv;
773 
774  if (!b) {
775  logger->log_error("MongoDB", "mongodb-bson-get-array: invalid object");
776  rv.push_back(CLIPS::Value("FALSE", CLIPS::TYPE_SYMBOL));
777  return rv;
778  }
779 
780  try {
781  auto el = get_dotted_field(b->view(), field_name);
782 
783  if (el.type() != bsoncxx::type::k_array) {
784  logger->log_error("MongoDB",
785  "mongodb-bson-get-array: field %s is not an array",
786  field_name.c_str());
787  rv.push_back(CLIPS::Value("FALSE", CLIPS::TYPE_SYMBOL));
788  return rv;
789  }
790 
791  bsoncxx::array::view array_view{el.get_array()};
792  for (const auto e : array_view) {
793  switch (e.type()) {
794  case bsoncxx::type::k_double: rv.push_back(CLIPS::Value(e.get_double())); break;
795  case bsoncxx::type::k_utf8: rv.push_back(CLIPS::Value(e.get_utf8().value.to_string())); break;
796  case bsoncxx::type::k_bool:
797  rv.push_back(CLIPS::Value(e.get_bool() ? "TRUE" : "FALSE", CLIPS::TYPE_SYMBOL));
798  break;
799  case bsoncxx::type::k_int32: rv.push_back(CLIPS::Value(e.get_int32())); break;
800  case bsoncxx::type::k_int64: rv.push_back(CLIPS::Value(e.get_int64())); break;
801  case bsoncxx::type::k_document: {
802  auto b = new bsoncxx::builder::basic::document();
803  b->append(bsoncxx::builder::concatenate(e.get_document().view()));
804  rv.push_back(CLIPS::Value(b));
805  } break;
806  default:
807  rv.clear();
808  rv.push_back(CLIPS::Value("FALSE", CLIPS::TYPE_SYMBOL));
809  return rv;
810  }
811  }
812  return rv;
813  } catch (bsoncxx::exception &e) {
814  logger->log_warn(name(),
815  "mongodb-bson-get: failed to get '%s': %s",
816  field_name.c_str(),
817  e.what());
818  rv.clear();
819  rv.push_back(CLIPS::Value("FALSE", CLIPS::TYPE_SYMBOL));
820  return rv;
821  }
822 }
823 
824 CLIPS::Values
825 ClipsRobotMemoryThread::clips_bson_get_time(void *bson, std::string field_name)
826 {
827  auto b = static_cast<bsoncxx::builder::basic::document *>(bson);
828 
829  CLIPS::Values rv;
830 
831  if (!b) {
832  logger->log_error("MongoDB", "mongodb-bson-get-time: invalid object");
833  rv.push_back(CLIPS::Value("FALSE", CLIPS::TYPE_SYMBOL));
834  return rv;
835  }
836 
837  try {
838  auto el = get_dotted_field(b->view(), field_name);
839  int64_t ts = 0;
840  if (el.type() == bsoncxx::type::k_date) {
841  bsoncxx::types::b_date d = el.get_date();
842  ts = d.to_int64();
843  } else if (el.type() == bsoncxx::type::k_timestamp) {
844  bsoncxx::types::b_timestamp t = el.get_timestamp();
845  ts = (int64_t)t.timestamp * 1000;
846  } else {
847  logger->log_error("MongoDB",
848  "mongodb-bson-get-time: field %s is not a time",
849  field_name.c_str());
850  rv.push_back(CLIPS::Value("FALSE", CLIPS::TYPE_SYMBOL));
851  return rv;
852  }
853 
854  rv.resize(2);
855  rv[0] = CLIPS::Value((long long int)(ts / 1000));
856  rv[1] = CLIPS::Value((ts - (rv[0].as_integer() * 1000)) * 1000);
857  return rv;
858  } catch (bsoncxx::exception &e) {
859  rv.resize(2, CLIPS::Value("FALSE", CLIPS::TYPE_SYMBOL));
860  return rv;
861  }
862 }
863 
864 CLIPS::Value
865 ClipsRobotMemoryThread::clips_robotmemory_register_trigger(std::string env_name,
866  std::string collection,
867  void * query,
868  std::string assert_name)
869 {
870  bsoncxx::document::value b{static_cast<bsoncxx::builder::basic::document *>(query)->view()};
871  std::string future_name = "register_trigger_" + assert_name;
872  if (!mutex_future_ready(future_name)) {
873  MutexLocker clips_lock(envs_[env_name].objmutex_ptr());
874  envs_[env_name]->assert_fact_f("(mutex-trigger-register-feedback FAIL \"%s\")",
875  assert_name.c_str());
876  return CLIPS::Value("FALSE", CLIPS::TYPE_SYMBOL);
877  }
878  auto fut = std::async(std::launch::async, [this, b, env_name, collection, assert_name] {
879  try {
880  ClipsRmTrigger *clips_trigger =
881  new ClipsRmTrigger(assert_name, robot_memory, envs_[env_name], logger);
882  clips_trigger->set_trigger(robot_memory->register_trigger(
883  b.view(), collection, &ClipsRmTrigger::callback, clips_trigger));
884  MutexLocker triggers_lock(&clips_triggers_mutex_);
885  clips_triggers_.push_back(clips_trigger);
886  MutexLocker clips_lock(envs_[env_name].objmutex_ptr());
887  envs_[env_name]->assert_fact_f("(mutex-trigger-register-feedback SUCCESS \"%s\" %p)",
888  assert_name.c_str(),
889  CLIPS::Value(clips_trigger).as_address());
890  return true;
891  } catch (std::system_error &e) {
892  logger->log_warn(name(), "Error while registering trigger: %s", e.what());
893  MutexLocker clips_lock(envs_[env_name].objmutex_ptr());
894  envs_[env_name]->assert_fact_f("(mutex-trigger-register-feedback FAIL \"%s\")",
895  assert_name.c_str());
896  return false;
897  }
898  });
899 
900  mutex_futures_[future_name] = std::move(fut);
901  return CLIPS::Value("TRUE", CLIPS::TYPE_SYMBOL);
902 }
903 
904 void
905 ClipsRobotMemoryThread::clips_robotmemory_destroy_trigger(void *trigger)
906 {
907  ClipsRmTrigger *clips_trigger = static_cast<ClipsRmTrigger *>(trigger);
908  MutexLocker triggers_lock(&clips_triggers_mutex_);
909  clips_triggers_.remove(clips_trigger);
910  delete clips_trigger; //the triger unregisteres itself at the robot memory
911 }
912 
913 CLIPS::Value
914 ClipsRobotMemoryThread::clips_robotmemory_mutex_create(std::string name)
915 {
916  bool rv = robot_memory->mutex_create(name);
917  return CLIPS::Value(rv ? "TRUE" : "FALSE", CLIPS::TYPE_SYMBOL);
918 }
919 
920 CLIPS::Value
921 ClipsRobotMemoryThread::clips_robotmemory_mutex_destroy(std::string name)
922 {
923  bool rv = robot_memory->mutex_destroy(name);
924  return CLIPS::Value(rv ? "TRUE" : "FALSE", CLIPS::TYPE_SYMBOL);
925 }
926 
927 CLIPS::Value
928 ClipsRobotMemoryThread::clips_robotmemory_mutex_try_lock(std::string name, std::string identity)
929 {
930  bool rv = robot_memory->mutex_try_lock(name, identity);
931  return CLIPS::Value(rv ? "TRUE" : "FALSE", CLIPS::TYPE_SYMBOL);
932 }
933 
934 CLIPS::Value
935 ClipsRobotMemoryThread::clips_robotmemory_mutex_renew_lock(std::string name, std::string identity)
936 {
937  bool rv = robot_memory->mutex_renew_lock(name, identity);
938  return CLIPS::Value(rv ? "TRUE" : "FALSE", CLIPS::TYPE_SYMBOL);
939 }
940 
941 CLIPS::Value
942 ClipsRobotMemoryThread::clips_robotmemory_mutex_force_lock(std::string name, std::string identity)
943 {
944  bool rv = robot_memory->mutex_try_lock(name, identity, /* force */ true);
945  return CLIPS::Value(rv ? "TRUE" : "FALSE", CLIPS::TYPE_SYMBOL);
946 }
947 
948 CLIPS::Value
949 ClipsRobotMemoryThread::clips_robotmemory_mutex_unlock(std::string name, std::string identity)
950 {
951  bool rv = robot_memory->mutex_unlock(name, identity);
952  return CLIPS::Value(rv ? "TRUE" : "FALSE", CLIPS::TYPE_SYMBOL);
953 }
954 
955 CLIPS::Value
956 ClipsRobotMemoryThread::clips_robotmemory_mutex_setup_ttl(float max_age_sec)
957 {
958  bool rv = robot_memory->mutex_setup_ttl(max_age_sec);
959  return CLIPS::Value(rv ? "TRUE" : "FALSE", CLIPS::TYPE_SYMBOL);
960 }
961 
962 CLIPS::Value
963 ClipsRobotMemoryThread::clips_robotmemory_mutex_expire_locks(float max_age_sec)
964 {
965  bool rv = robot_memory->mutex_expire_locks(max_age_sec);
966  return CLIPS::Value(rv ? "TRUE" : "FALSE", CLIPS::TYPE_SYMBOL);
967 }
968 
969 bool
970 ClipsRobotMemoryThread::mutex_future_ready(const std::string &name)
971 {
972  auto mf_it = mutex_futures_.find(name);
973  if (mf_it != mutex_futures_.end()) {
974  auto fut_status = mutex_futures_[name].wait_for(std::chrono::milliseconds(0));
975  if (fut_status != std::future_status::ready) {
976  return false;
977  } else {
978  mutex_futures_.erase(mf_it);
979  }
980  }
981  return true;
982 }
983 
984 CLIPS::Values
985 ClipsRobotMemoryThread::clips_robotmemory_mutex_create_async(std::string name)
986 {
987  CLIPS::Values rv;
988  if (!mutex_future_ready(name)) {
989  rv.push_back(CLIPS::Value("FALSE", CLIPS::TYPE_SYMBOL));
990  rv.push_back(CLIPS::Value("Task already running for " + name + " (create failed)"));
991  return rv;
992  }
993 
994  auto fut =
995  std::async(std::launch::async, [this, name] { return robot_memory->mutex_create(name); });
996 
997  mutex_futures_[name] = std::move(fut);
998 
999  rv.push_back(CLIPS::Value("TRUE", CLIPS::TYPE_SYMBOL));
1000  return rv;
1001 }
1002 
1003 CLIPS::Values
1004 ClipsRobotMemoryThread::clips_robotmemory_mutex_destroy_async(std::string name)
1005 {
1006  CLIPS::Values rv;
1007  if (!mutex_future_ready(name)) {
1008  rv.push_back(CLIPS::Value("FALSE", CLIPS::TYPE_SYMBOL));
1009  rv.push_back(CLIPS::Value("Task already running for " + name + " (destroy failed)"));
1010  return rv;
1011  }
1012 
1013  auto fut =
1014  std::async(std::launch::async, [this, name] { return robot_memory->mutex_destroy(name); });
1015 
1016  mutex_futures_[name] = std::move(fut);
1017 
1018  rv.push_back(CLIPS::Value("TRUE", CLIPS::TYPE_SYMBOL));
1019  return rv;
1020 }
1021 
1022 CLIPS::Values
1023 ClipsRobotMemoryThread::clips_robotmemory_mutex_try_lock_async(std::string env_name,
1024  std::string name,
1025  std::string identity)
1026 {
1027  CLIPS::Values rv;
1028  if (!mutex_future_ready(name)) {
1029  rv.push_back(CLIPS::Value("FALSE", CLIPS::TYPE_SYMBOL));
1030  rv.push_back(CLIPS::Value("Task already running for " + name + " (try-lock failed)"));
1031  envs_[env_name]->assert_fact_f("(mutex-op-feedback try-lock-async FAIL %s)", name.c_str());
1032  return rv;
1033  }
1034 
1035  auto fut = std::async(std::launch::async, [this, env_name, name, identity] {
1036  bool ok = robot_memory->mutex_try_lock(name, identity);
1037  if (!ok) {
1038  MutexLocker lock(envs_[env_name].objmutex_ptr());
1039  envs_[env_name]->assert_fact_f("(mutex-op-feedback try-lock-async FAIL %s)", name.c_str());
1040  }
1041  return ok;
1042  });
1043 
1044  mutex_futures_[name] = std::move(fut);
1045 
1046  rv.push_back(CLIPS::Value("TRUE", CLIPS::TYPE_SYMBOL));
1047  return rv;
1048 }
1049 
1050 CLIPS::Values
1051 ClipsRobotMemoryThread::clips_robotmemory_mutex_renew_lock_async(std::string env_name,
1052  std::string name,
1053  std::string identity)
1054 {
1055  CLIPS::Values rv;
1056  if (!mutex_future_ready(name)) {
1057  rv.push_back(CLIPS::Value("FALSE", CLIPS::TYPE_SYMBOL));
1058  rv.push_back(CLIPS::Value("Task already running for " + name + " (try-lock failed)"));
1059  MutexLocker lock(envs_[env_name].objmutex_ptr());
1060  envs_[env_name]->assert_fact_f("(mutex-op-feedback renew-lock-async FAIL %s)", name.c_str());
1061  return rv;
1062  }
1063 
1064  auto fut = std::async(std::launch::async, [this, env_name, name, identity] {
1065  bool ok = robot_memory->mutex_renew_lock(name, identity);
1066  MutexLocker lock(envs_[env_name].objmutex_ptr());
1067  envs_[env_name]->assert_fact_f("(mutex-op-feedback renew-lock-async %s %s)",
1068  ok ? "OK" : "FAIL",
1069  name.c_str());
1070  return ok;
1071  });
1072 
1073  mutex_futures_[name] = std::move(fut);
1074 
1075  rv.push_back(CLIPS::Value("TRUE", CLIPS::TYPE_SYMBOL));
1076  return rv;
1077 }
1078 
1079 CLIPS::Values
1080 ClipsRobotMemoryThread::clips_robotmemory_mutex_force_lock_async(std::string name,
1081  std::string identity)
1082 {
1083  CLIPS::Values rv;
1084  if (!mutex_future_ready(name)) {
1085  rv.push_back(CLIPS::Value("FALSE", CLIPS::TYPE_SYMBOL));
1086  rv.push_back(CLIPS::Value("Task already running for " + name + " (force-lock failed)"));
1087  return rv;
1088  }
1089 
1090  auto fut = std::async(std::launch::async, [this, name, identity] {
1091  return robot_memory->mutex_try_lock(name, identity, /* force */ true);
1092  });
1093 
1094  mutex_futures_[name] = std::move(fut);
1095 
1096  rv.push_back(CLIPS::Value("TRUE", CLIPS::TYPE_SYMBOL));
1097  return rv;
1098 }
1099 
1100 CLIPS::Values
1101 ClipsRobotMemoryThread::clips_robotmemory_mutex_unlock_async(std::string name, std::string identity)
1102 {
1103  CLIPS::Values rv;
1104  if (!mutex_future_ready(name)) {
1105  rv.push_back(CLIPS::Value("FALSE", CLIPS::TYPE_SYMBOL));
1106  rv.push_back(CLIPS::Value("Task already running for " + name + " (unlock failed)"));
1107  return rv;
1108  }
1109 
1110  auto fut = std::async(std::launch::async, [this, name, identity] {
1111  return robot_memory->mutex_unlock(name, identity);
1112  });
1113 
1114  mutex_futures_[name] = std::move(fut);
1115 
1116  rv.push_back(CLIPS::Value("TRUE", CLIPS::TYPE_SYMBOL));
1117  return rv;
1118 }
1119 
1120 CLIPS::Value
1121 ClipsRobotMemoryThread::clips_robotmemory_mutex_expire_locks_async(std::string env_name,
1122  float max_age_sec)
1123 {
1124  CLIPS::Values rv;
1125  if (mutex_expire_future_.valid()) {
1126  // have shared state, expire was or is running
1127  auto fut_status = mutex_expire_future_.wait_for(std::chrono::milliseconds(0));
1128  if (fut_status != std::future_status::ready) {
1129  MutexLocker lock(envs_[env_name].objmutex_ptr());
1130  envs_[env_name]->assert_fact_f("(mutex-op-feedback expire-locks-async FAIL)");
1131  return CLIPS::Value("FALSE", CLIPS::TYPE_SYMBOL);
1132  }
1133  }
1134 
1135  auto fut = std::async(std::launch::async, [this, env_name, max_age_sec] {
1136  bool ok = robot_memory->mutex_expire_locks(max_age_sec);
1137  MutexLocker lock(envs_[env_name].objmutex_ptr());
1138  envs_[env_name]->assert_fact_f("(mutex-op-feedback expire-locks-async %s)", ok ? "OK" : "FAIL");
1139  return ok;
1140  });
1141 
1142  mutex_expire_future_ = std::move(fut);
1143 
1144  return CLIPS::Value("TRUE", CLIPS::TYPE_SYMBOL);
1145 }
void callback(const bsoncxx::document::view &update)
Callback function for the trigger.
void set_trigger(EventTrigger *trigger)
Set the trigger object given by the robot memory.
virtual void loop()
Code to execute in the thread.
virtual void finalize()
Finalize the thread.
virtual void clips_context_destroyed(const std::string &env_name)
Notification that a CLIPS environment has been destroyed.
virtual void init()
Initialize the thread.
virtual void clips_context_init(const std::string &env_name, fawkes::LockPtr< CLIPS::Environment > &clips)
Initialize a CLIPS context to use the provided feature.
bool mutex_create(const std::string &name)
Explicitly create a mutex.
int dump_collection(const std::string &dbcollection, const std::string &directory="@CONFDIR@/robot-memory")
Dump (= save) a collection to the filesystem to restore it later.
bool mutex_destroy(const std::string &name)
Destroy a mutex.
bool mutex_renew_lock(const std::string &name, const std::string &identity)
Renew a mutex.
bool mutex_unlock(const std::string &name, const std::string &identity)
Release lock on mutex.
mongocxx::cursor query(bsoncxx::document::view query, const std::string &collection_name="", mongocxx::options::find query_options=mongocxx::options::find())
Query information from the robot memory.
bool mutex_setup_ttl(float max_age_sec)
Setup time-to-live index for mutexes.
int remove(const bsoncxx::document::view &query, const std::string &collection="")
Remove documents from the robot memory.
int insert(bsoncxx::document::view, const std::string &collection="")
Inserts a document into the robot memory.
EventTrigger * register_trigger(const bsoncxx::document::view &query, const std::string &collection, void(T::*callback)(const bsoncxx::document::view &), T *_obj)
Register a trigger to be notified when the robot memory is updated and the updated document matches t...
Definition: robot_memory.h:119
int update(const bsoncxx::document::view &query, const bsoncxx::document::view &update, const std::string &collection="", bool upsert=false)
Updates documents in the robot memory.
bool mutex_try_lock(const std::string &name, bool force=false)
Try to acquire a lock for a mutex.
int create_index(bsoncxx::document::view keys, const std::string &collection="", bool unique=false)
Create an index on a collection.
int restore_collection(const std::string &dbcollection, const std::string &directory="@CONFDIR@/robot-memory", std::string target_dbcollection="")
Restore a previously dumped collection from a directory.
bool mutex_expire_locks(float max_age_sec)
Expire old locks on mutexes.
Thread aspect to provide a feature to CLIPS environments.
Definition: clips_feature.h:58
CLIPS feature maintainer.
Definition: clips_feature.h:42
Base class for exceptions in Fawkes.
Definition: exception.h:36
void lock() const
Lock access to the encapsulated object.
Definition: lockptr.h:257
void unlock() const
Unlock object mutex.
Definition: lockptr.h:273
virtual void log_debug(const char *component, const char *format,...)=0
Log debug message.
virtual void log_warn(const char *component, const char *format,...)=0
Log warning message.
virtual void log_error(const char *component, const char *format,...)=0
Log error message.
Logger * logger
This is the Logger member used to access the logger.
Definition: logging.h:41
Mutex locking helper.
Definition: mutex_locker.h:34
RobotMemory * robot_memory
RobotMemory object for storing and querying information.
Thread class encapsulation of pthreads.
Definition: thread.h:46
const char * name() const
Get name of thread.
Definition: thread.h:100
Fawkes library namespace.