23#include "clips_robot_memory_thread.h"
25#include <core/threading/mutex_locker.h>
26#include <plugins/mongodb/utils.h>
28#include <bsoncxx/document/element.hpp>
29#include <bsoncxx/exception/exception.hpp>
30#include <bsoncxx/types.hpp>
31#include <mongocxx/exception/exception.hpp>
42ClipsRobotMemoryThread::ClipsRobotMemoryThread()
43:
Thread(
"ClipsRobotMemoryThread",
Thread::OPMODE_WAITFORWAKEUP),
72 envs_[env_name] = clips;
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)));
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 *>(
132 &ClipsRobotMemoryThread::clips_robotmemory_query_sort)));
133 clips->add_function(
"robmem-dump-collection",
134 sigc::slot<CLIPS::Value, std::string, std::string>(
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 *>(
143 &ClipsRobotMemoryThread::clips_robotmemory_cursor_destroy)));
144 clips->add_function(
"robmem-cursor-more",
145 sigc::slot<CLIPS::Value, void *>(
147 &ClipsRobotMemoryThread::clips_robotmemory_cursor_more)));
148 clips->add_function(
"robmem-cursor-next",
149 sigc::slot<CLIPS::Value, void *>(
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>(
155 &ClipsRobotMemoryThread::clips_robotmemory_register_trigger),
157 clips->add_function(
"robmem-trigger-destroy",
158 sigc::slot<void, void *>(
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 *>(
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)));
184 clips->add_function(
"robmem-mutex-create",
185 sigc::slot<CLIPS::Value, std::string>(
187 &ClipsRobotMemoryThread::clips_robotmemory_mutex_create)));
188 clips->add_function(
"robmem-mutex-destroy",
189 sigc::slot<CLIPS::Value, std::string>(
191 &ClipsRobotMemoryThread::clips_robotmemory_mutex_destroy)));
192 clips->add_function(
"robmem-mutex-try-lock",
193 sigc::slot<CLIPS::Value, std::string, std::string>(
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>(
205 &ClipsRobotMemoryThread::clips_robotmemory_mutex_unlock)));
206 clips->add_function(
"robmem-mutex-setup-ttl",
207 sigc::slot<CLIPS::Value, float>(
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)));
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)));
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),
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),
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)));
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),
242 clips->build(
"(deffacts have-feature-mongodb (have-feature MongoDB))");
245 clips->batch_evaluate(SRCDIR
"/robot-memory.clp");
253 envs_.erase(env_name);
258ClipsRobotMemoryThread::clips_bson_create()
260 return CLIPS::Value(
new bsoncxx::builder::basic::document());
264ClipsRobotMemoryThread::clips_bson_parse(std::string document)
266 auto b =
new bsoncxx::builder::basic::document();
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());
272 return CLIPS::Value(b);
276ClipsRobotMemoryThread::clips_bson_destroy(
void *bson)
278 auto b =
static_cast<bsoncxx::builder::basic::document *
>(bson);
283ClipsRobotMemoryThread::clips_bson_tostring(
void *bson)
285 auto b =
static_cast<bsoncxx::builder::basic::document *
>(bson);
286 return bsoncxx::to_json(b->view());
290ClipsRobotMemoryThread::clips_bson_append(
void *bson, std::string field_name, CLIPS::Value value)
292 using namespace bsoncxx::builder;
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;
298 case CLIPS::TYPE_INTEGER:
299 b->append(basic::kvp(field_name,
static_cast<int64_t
>(value.as_integer())));
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()));
311 logger->
log_warn(
"RefBox",
"Tried to add unknown type to BSON field %s", field_name.c_str());
314 }
catch (bsoncxx::exception &e) {
316 "Failed to append array value to field %s: %s",
323ClipsRobotMemoryThread::clips_bson_append_regex(
void * bson,
324 std::string field_name,
325 CLIPS::Value regex_string)
327 using namespace bsoncxx::builder;
328 if (regex_string.type() != CLIPS::TYPE_STRING) {
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) {
337 "Failed to append regex to field %s: %s",
344ClipsRobotMemoryThread::clips_bson_append_array(
void * bson,
345 std::string field_name,
346 CLIPS::Values values)
348 using namespace bsoncxx::builder;
350 auto b =
static_cast<basic::document *
>(bson);
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;
357 case CLIPS::TYPE_INTEGER: array.append(static_cast<int64_t>(value.as_integer())); break;
359 case CLIPS::TYPE_SYMBOL:
360 case CLIPS::TYPE_STRING:
361 case CLIPS::TYPE_INSTANCE_NAME: array.append(value.as_string()); break;
363 case CLIPS::TYPE_EXTERNAL_ADDRESS: {
364 auto subb = static_cast<bsoncxx::builder::basic::document *>(value.as_address());
365 array.append(subb->view());
370 "Tried to add unknown type to BSON array field %s",
376 }
catch (bsoncxx::exception &e) {
378 "Failed to append array value to field %s: %s",
385ClipsRobotMemoryThread::clips_bson_array_start()
387 return CLIPS::Value(
new bsoncxx::builder::basic::array{});
391ClipsRobotMemoryThread::clips_bson_array_finish(
void *bson, std::string field_name,
void *array)
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()));
401ClipsRobotMemoryThread::clips_bson_array_append(
void *array, CLIPS::Value value)
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;
408 case CLIPS::TYPE_INTEGER: array_doc->append(
static_cast<int64_t
>(value.as_integer()));
break;
410 case CLIPS::TYPE_SYMBOL:
411 case CLIPS::TYPE_STRING:
412 case CLIPS::TYPE_INSTANCE_NAME: array_doc->append(value.as_string());
break;
414 case CLIPS::TYPE_EXTERNAL_ADDRESS: {
415 auto subb =
static_cast<basic::document *
>(value.as_address());
416 array_doc->append(subb->view());
419 logger->
log_warn(
"MongoDB",
"bson-array-append: tried to add unknown type to BSON array field");
425ClipsRobotMemoryThread::clips_bson_append_time(
void * bson,
426 std::string field_name,
429 if (time.size() != 2) {
430 logger->
log_warn(
"MongoDB",
"Invalid time, %zu instead of 2 entries", time.size());
433 if (time[0].type() != CLIPS::TYPE_INTEGER || time[1].type() != CLIPS::TYPE_INTEGER) {
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) {
447 "Failed to append time value to field %s: %s",
454ClipsRobotMemoryThread::clips_robotmemory_insert(std::string collection,
void *bson)
456 auto b =
static_cast<bsoncxx::builder::basic::document *
>(bson);
460 }
catch (mongocxx::exception &e) {
466ClipsRobotMemoryThread::clips_robotmemory_create_index(std::string collection,
void *bson)
468 auto b =
static_cast<bsoncxx::builder::basic::document *
>(bson);
472 }
catch (mongocxx::exception &e) {
473 logger->
log_warn(
"MongoDB",
"Creating index failed: %s", e.what());
478ClipsRobotMemoryThread::clips_robotmemory_create_unique_index(std::string collection,
void *bson)
480 auto b =
static_cast<bsoncxx::builder::basic::document *
>(bson);
484 }
catch (mongocxx::exception &e) {
485 logger->
log_warn(
"MongoDB",
"Creating unique index failed: %s", e.what());
490ClipsRobotMemoryThread::robotmemory_update(std::string & collection,
491 const bsoncxx::document::view &update,
492 CLIPS::Value & query,
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());
503 logger->
log_warn(
"MongoDB",
"Invalid query, must be string or BSON document");
507 }
catch (bsoncxx::exception &e) {
508 logger->
log_warn(
"MongoDB",
"Compiling query failed: %s", e.what());
509 }
catch (mongocxx::exception &e) {
515ClipsRobotMemoryThread::clips_robotmemory_upsert(std::string collection,
519 auto b =
static_cast<bsoncxx::builder::basic::document *
>(bson);
524 robotmemory_update(collection, b->view(), query,
true);
528ClipsRobotMemoryThread::clips_robotmemory_update(std::string collection,
532 auto b =
static_cast<bsoncxx::builder::basic::document *
>(bson);
537 robotmemory_update(collection, b->view(), query,
false);
541ClipsRobotMemoryThread::clips_robotmemory_replace(std::string collection,
545 auto b =
static_cast<bsoncxx::builder::basic::document *
>(bson);
548 robotmemory_update(collection, b->view(), query,
false);
552ClipsRobotMemoryThread::clips_robotmemory_query_sort(std::string collection,
556 auto b =
static_cast<bsoncxx::builder::basic::document *
>(bson);
559 mongocxx::options::find find_opts{};
561 auto *bs =
static_cast<bsoncxx::builder::basic::document *
>(bson_sort);
562 find_opts.sort(bs->view());
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) {
572 return CLIPS::Value(
"FALSE", CLIPS::TYPE_SYMBOL);
577ClipsRobotMemoryThread::clips_robotmemory_dump_collection(std::string collection,
578 std::string directory)
583 return CLIPS::Value(
"TRUE", CLIPS::TYPE_SYMBOL);
585 return CLIPS::Value(
"FALSE", CLIPS::TYPE_SYMBOL);
587 }
catch (mongocxx::exception &e) {
589 "Dumping collection %s to %s failed: \n %s",
593 return CLIPS::Value(
"FALSE", CLIPS::TYPE_SYMBOL);
598ClipsRobotMemoryThread::clips_robotmemory_restore_collection(std::string collection,
599 std::string directory,
600 std::string target_collection)
605 return CLIPS::Value(
"TRUE", CLIPS::TYPE_SYMBOL);
607 return CLIPS::Value(
"FALSE", CLIPS::TYPE_SYMBOL);
609 }
catch (mongocxx::exception &e) {
611 "Restoring collection %s to %s failed: \n %s",
615 return CLIPS::Value(
"FALSE", CLIPS::TYPE_SYMBOL);
620ClipsRobotMemoryThread::clips_robotmemory_remove(std::string collection,
void *bson)
622 auto b =
static_cast<bsoncxx::builder::basic::document *
>(bson);
625 }
catch (std::system_error &e) {
631ClipsRobotMemoryThread::clips_robotmemory_query(
const std::string &collection,
void *bson)
633 return clips_robotmemory_query_sort(collection, bson, NULL);
637ClipsRobotMemoryThread::clips_robotmemory_cursor_destroy(
void *cursor)
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");
649ClipsRobotMemoryThread::clips_robotmemory_cursor_more(
void *cursor)
651 throw Exception(
"The function cursor-more is no longer supported. Call cursor-next and check the "
652 "return value for FALSE instead.");
656ClipsRobotMemoryThread::clips_robotmemory_cursor_next(
void *cursor)
658 auto c =
static_cast<std::unique_ptr<mongocxx::cursor> *
>(cursor);
660 if (!c || !c->get()) {
661 logger->
log_error(
"MongoDB",
"mongodb-cursor-next: got invalid cursor");
662 return CLIPS::Value(
"FALSE", CLIPS::TYPE_SYMBOL);
666 auto it = (*c)->begin();
667 if (it == (*c)->end()) {
668 return CLIPS::Value(
"FALSE", CLIPS::TYPE_SYMBOL);
670 auto b =
new bsoncxx::builder::basic::document();
671 b->append(bsoncxx::builder::concatenate(*it));
673 return CLIPS::Value(b);
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);
682ClipsRobotMemoryThread::clips_bson_field_names(
void *bson)
684 auto b =
static_cast<bsoncxx::builder::basic::document *
>(bson);
687 logger->
log_error(
"MongoDB",
"mongodb-bson-field-names: invalid object");
689 rv.push_back(CLIPS::Value(
"FALSE", CLIPS::TYPE_SYMBOL));
694 for (
auto element : b->view()) {
695 rv.push_back(CLIPS::Value(std::string(element.key())));
701ClipsRobotMemoryThread::clips_bson_get(
void *bson, std::string field_name)
703 auto b =
static_cast<bsoncxx::builder::basic::document *
>(bson);
707 return CLIPS::Value(
"FALSE", CLIPS::TYPE_SYMBOL);
711 auto el = get_dotted_field(b->view(), field_name);
714 "mongodb-bson-get: failed to get '%s', no such element in doc: %s",
716 bsoncxx::to_json(b->view()).c_str());
717 return CLIPS::Value(
"FALSE", CLIPS::TYPE_SYMBOL);
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);
731 case bsoncxx::type::k_oid:
732 return CLIPS::Value(el.get_oid().value.to_string());
734 default:
return CLIPS::Value(
"INVALID_VALUE_TYPE", CLIPS::TYPE_SYMBOL);
736 }
catch (std::system_error &e) {
738 "mongodb-bson-get: failed to get '%s': %s",
741 return CLIPS::Value(
"FALSE", CLIPS::TYPE_SYMBOL);
746ClipsRobotMemoryThread::clips_bson_has_field(
void *bson, std::string field_name)
748 auto b =
static_cast<bsoncxx::builder::basic::document *
>(bson);
752 return CLIPS::Value(
"FALSE", CLIPS::TYPE_SYMBOL);
756 auto el = get_dotted_field(b->view(), field_name);
757 if (!el.key().empty()) {
758 return CLIPS::Value(
"TRUE", CLIPS::TYPE_SYMBOL);
760 return CLIPS::Value(
"FALSE", CLIPS::TYPE_SYMBOL);
762 }
catch (bsoncxx::exception &e) {
763 return CLIPS::Value(
"FALSE", CLIPS::TYPE_SYMBOL);
768ClipsRobotMemoryThread::clips_bson_get_array(
void *bson, std::string field_name)
770 auto b =
static_cast<bsoncxx::builder::basic::document *
>(bson);
775 logger->
log_error(
"MongoDB",
"mongodb-bson-get-array: invalid object");
776 rv.push_back(CLIPS::Value(
"FALSE", CLIPS::TYPE_SYMBOL));
781 auto el = get_dotted_field(b->view(), field_name);
783 if (el.type() != bsoncxx::type::k_array) {
785 "mongodb-bson-get-array: field %s is not an array",
787 rv.push_back(CLIPS::Value(
"FALSE", CLIPS::TYPE_SYMBOL));
791 bsoncxx::array::view array_view{el.get_array()};
792 for (
const auto e : array_view) {
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));
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));
808 rv.push_back(CLIPS::Value(
"FALSE", CLIPS::TYPE_SYMBOL));
813 }
catch (bsoncxx::exception &e) {
815 "mongodb-bson-get: failed to get '%s': %s",
819 rv.push_back(CLIPS::Value(
"FALSE", CLIPS::TYPE_SYMBOL));
825ClipsRobotMemoryThread::clips_bson_get_time(
void *bson, std::string field_name)
827 auto b =
static_cast<bsoncxx::builder::basic::document *
>(bson);
833 rv.push_back(CLIPS::Value(
"FALSE", CLIPS::TYPE_SYMBOL));
838 auto el = get_dotted_field(b->view(), field_name);
840 if (el.type() == bsoncxx::type::k_date) {
841 bsoncxx::types::b_date d = el.get_date();
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;
848 "mongodb-bson-get-time: field %s is not a time",
850 rv.push_back(CLIPS::Value(
"FALSE", CLIPS::TYPE_SYMBOL));
855 rv[0] = CLIPS::Value((
long long int)(ts / 1000));
856 rv[1] = CLIPS::Value((ts - (rv[0].as_integer() * 1000)) * 1000);
858 }
catch (bsoncxx::exception &e) {
859 rv.resize(2, CLIPS::Value(
"FALSE", CLIPS::TYPE_SYMBOL));
865ClipsRobotMemoryThread::clips_robotmemory_register_trigger(std::string env_name,
866 std::string collection,
868 std::string assert_name)
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);
878 auto fut = std::async(std::launch::async, [
this, b, env_name, collection, assert_name] {
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)",
889 CLIPS::Value(clips_trigger).as_address());
891 }
catch (std::system_error &e) {
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());
900 mutex_futures_[future_name] = std::move(fut);
901 return CLIPS::Value(
"TRUE", CLIPS::TYPE_SYMBOL);
905ClipsRobotMemoryThread::clips_robotmemory_destroy_trigger(
void *trigger)
909 clips_triggers_.remove(clips_trigger);
910 delete clips_trigger;
914ClipsRobotMemoryThread::clips_robotmemory_mutex_create(std::string name)
917 return CLIPS::Value(rv ?
"TRUE" :
"FALSE", CLIPS::TYPE_SYMBOL);
921ClipsRobotMemoryThread::clips_robotmemory_mutex_destroy(std::string name)
924 return CLIPS::Value(rv ?
"TRUE" :
"FALSE", CLIPS::TYPE_SYMBOL);
928ClipsRobotMemoryThread::clips_robotmemory_mutex_try_lock(std::string name, std::string identity)
931 return CLIPS::Value(rv ?
"TRUE" :
"FALSE", CLIPS::TYPE_SYMBOL);
935ClipsRobotMemoryThread::clips_robotmemory_mutex_renew_lock(std::string name, std::string identity)
938 return CLIPS::Value(rv ?
"TRUE" :
"FALSE", CLIPS::TYPE_SYMBOL);
942ClipsRobotMemoryThread::clips_robotmemory_mutex_force_lock(std::string name, std::string identity)
945 return CLIPS::Value(rv ?
"TRUE" :
"FALSE", CLIPS::TYPE_SYMBOL);
949ClipsRobotMemoryThread::clips_robotmemory_mutex_unlock(std::string name, std::string identity)
952 return CLIPS::Value(rv ?
"TRUE" :
"FALSE", CLIPS::TYPE_SYMBOL);
956ClipsRobotMemoryThread::clips_robotmemory_mutex_setup_ttl(
float max_age_sec)
959 return CLIPS::Value(rv ?
"TRUE" :
"FALSE", CLIPS::TYPE_SYMBOL);
963ClipsRobotMemoryThread::clips_robotmemory_mutex_expire_locks(
float max_age_sec)
966 return CLIPS::Value(rv ?
"TRUE" :
"FALSE", CLIPS::TYPE_SYMBOL);
970ClipsRobotMemoryThread::mutex_future_ready(
const std::string &name)
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) {
978 mutex_futures_.erase(mf_it);
985ClipsRobotMemoryThread::clips_robotmemory_mutex_create_async(std::string name)
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)"));
997 mutex_futures_[
name] = std::move(fut);
999 rv.push_back(CLIPS::Value(
"TRUE", CLIPS::TYPE_SYMBOL));
1004ClipsRobotMemoryThread::clips_robotmemory_mutex_destroy_async(std::string name)
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)"));
1016 mutex_futures_[
name] = std::move(fut);
1018 rv.push_back(CLIPS::Value(
"TRUE", CLIPS::TYPE_SYMBOL));
1023ClipsRobotMemoryThread::clips_robotmemory_mutex_try_lock_async(std::string env_name,
1025 std::string identity)
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());
1035 auto fut = std::async(std::launch::async, [
this, env_name,
name, identity] {
1039 envs_[env_name]->assert_fact_f(
"(mutex-op-feedback try-lock-async FAIL %s)",
name.c_str());
1044 mutex_futures_[
name] = std::move(fut);
1046 rv.push_back(CLIPS::Value(
"TRUE", CLIPS::TYPE_SYMBOL));
1051ClipsRobotMemoryThread::clips_robotmemory_mutex_renew_lock_async(std::string env_name,
1053 std::string identity)
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)"));
1060 envs_[env_name]->assert_fact_f(
"(mutex-op-feedback renew-lock-async FAIL %s)",
name.c_str());
1064 auto fut = std::async(std::launch::async, [
this, env_name,
name, identity] {
1067 envs_[env_name]->assert_fact_f(
"(mutex-op-feedback renew-lock-async %s %s)",
1073 mutex_futures_[
name] = std::move(fut);
1075 rv.push_back(CLIPS::Value(
"TRUE", CLIPS::TYPE_SYMBOL));
1080ClipsRobotMemoryThread::clips_robotmemory_mutex_force_lock_async(std::string name,
1081 std::string identity)
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)"));
1090 auto fut = std::async(std::launch::async, [
this,
name, identity] {
1094 mutex_futures_[
name] = std::move(fut);
1096 rv.push_back(CLIPS::Value(
"TRUE", CLIPS::TYPE_SYMBOL));
1101ClipsRobotMemoryThread::clips_robotmemory_mutex_unlock_async(std::string name, std::string identity)
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)"));
1110 auto fut = std::async(std::launch::async, [
this,
name, identity] {
1114 mutex_futures_[
name] = std::move(fut);
1116 rv.push_back(CLIPS::Value(
"TRUE", CLIPS::TYPE_SYMBOL));
1121ClipsRobotMemoryThread::clips_robotmemory_mutex_expire_locks_async(std::string env_name,
1125 if (mutex_expire_future_.valid()) {
1127 auto fut_status = mutex_expire_future_.wait_for(std::chrono::milliseconds(0));
1128 if (fut_status != std::future_status::ready) {
1130 envs_[env_name]->assert_fact_f(
"(mutex-op-feedback expire-locks-async FAIL)");
1131 return CLIPS::Value(
"FALSE", CLIPS::TYPE_SYMBOL);
1135 auto fut = std::async(std::launch::async, [
this, env_name, max_age_sec] {
1138 envs_[env_name]->assert_fact_f(
"(mutex-op-feedback expire-locks-async %s)", ok ?
"OK" :
"FAIL");
1142 mutex_expire_future_ = std::move(fut);
1144 return CLIPS::Value(
"TRUE", CLIPS::TYPE_SYMBOL);
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.
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...
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.
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.
CLIPS feature maintainer.
Base class for exceptions in Fawkes.
void lock() const
Lock access to the encapsulated object.
void unlock() const
Unlock object mutex.
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.
virtual void log_error(const char *component, const char *format,...)
Log error message.
RobotMemory * robot_memory
RobotMemory object for storing and querying information.
Thread class encapsulation of pthreads.
const char * name() const
Get name of thread.
Fawkes library namespace.