Fawkes API Fawkes Development Version
feature_blackboard.cpp
1
2/***************************************************************************
3 * feature_blackboard.cpp - CLIPS blackboard feature
4 *
5 * Created: Thu Oct 03 11:48:58 2013
6 * Copyright 2006-2013 Tim Niemueller [www.niemueller.de]
7 *
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 "feature_blackboard.h"
24
25#include <blackboard/blackboard.h>
26#include <blackboard/exceptions.h>
27#include <core/threading/mutex_locker.h>
28#include <interface/interface_info.h>
29#include <logging/logger.h>
30#include <utils/misc/string_conversions.h>
31#include <utils/misc/string_split.h>
32#include <utils/time/time.h>
33
34#include <clipsmm.h>
35
36using namespace fawkes;
37
38/** @class BlackboardCLIPSFeature "feature_blackboard.h"
39 * CLIPS blackboard feature.
40 * @author Tim Niemueller
41 */
42
43/** Constructor.
44 * @param logger message logger
45 * @param blackboard blackboard to use for opening interfaces
46 * @param retract_early Retract blackboard facts at the end of the same
47 * execution cycle they have been asserted in. If false (default),
48 * blackboard facts are only retracted immediately before a new
49 * fact representing a particular interface is asserted.
50 */
52 fawkes::BlackBoard *blackboard,
53 bool retract_early)
54: CLIPSFeature("blackboard"),
55 logger_(logger),
56 blackboard_(blackboard),
57 cfg_retract_early_(retract_early)
58{
59}
60
61/** Destructor. */
63{
64 for (auto &iface_map : interfaces_) {
65 for (auto &iface_list : iface_map.second.reading) {
66 for (auto iface : iface_list.second) {
67 blackboard_->close(iface);
68 }
69 }
70 for (auto &iface_list : iface_map.second.writing) {
71 for (auto iface : iface_list.second) {
72 blackboard_->close(iface);
73 }
74 }
75 }
76 interfaces_.clear();
77 envs_.clear();
78}
79
80void
81BlackboardCLIPSFeature::clips_context_init(const std::string & env_name,
83{
84 envs_[env_name] = clips;
85 clips->evaluate("(path-load \"blackboard.clp\")");
86 clips->add_function(
87 "blackboard-enable-time-read",
88 sigc::slot<void>(sigc::bind<0>(
89 sigc::mem_fun(*this, &BlackboardCLIPSFeature::clips_blackboard_enable_time_read), env_name)));
90 clips->add_function(
91 "blackboard-open",
92 sigc::slot<void, std::string, std::string>(sigc::bind<0>(
93 sigc::mem_fun(*this, &BlackboardCLIPSFeature::clips_blackboard_open_interface_reading),
94 env_name)));
95 clips->add_function(
96 "blackboard-open-reading",
97 sigc::slot<void, std::string, std::string>(sigc::bind<0>(
98 sigc::mem_fun(*this, &BlackboardCLIPSFeature::clips_blackboard_open_interface_reading),
99 env_name)));
100 clips->add_function(
101 "blackboard-open-writing",
102 sigc::slot<void, std::string, std::string>(sigc::bind<0>(
103 sigc::mem_fun(*this, &BlackboardCLIPSFeature::clips_blackboard_open_interface_writing),
104 env_name)));
105 clips->add_function(
106 "blackboard-close",
107 sigc::slot<void, std::string, std::string>(
108 sigc::bind<0>(sigc::mem_fun(*this, &BlackboardCLIPSFeature::clips_blackboard_close_interface),
109 env_name)));
110 clips->add_function("blackboard-preload",
111 sigc::slot<void, std::string>(sigc::bind<0>(
112 sigc::mem_fun(*this, &BlackboardCLIPSFeature::clips_blackboard_preload),
113 env_name)));
114 clips->add_function("blackboard-read",
115 sigc::slot<void>(sigc::bind<0>(
116 sigc::mem_fun(*this, &BlackboardCLIPSFeature::clips_blackboard_read),
117 env_name)));
118 clips->add_function("blackboard-write",
119 sigc::slot<void, std::string>(sigc::bind<0>(
120 sigc::mem_fun(*this, &BlackboardCLIPSFeature::clips_blackboard_write),
121 env_name)));
122 clips->add_function("blackboard-get-info",
123 sigc::slot<void>(sigc::bind<0>(
124 sigc::mem_fun(*this, &BlackboardCLIPSFeature::clips_blackboard_get_info),
125 env_name)));
126 clips->add_function("blackboard-set",
127 sigc::slot<void, std::string, std::string, CLIPS::Value>(sigc::bind<0>(
128 sigc::mem_fun(*this, &BlackboardCLIPSFeature::clips_blackboard_set),
129 env_name)));
130 clips->add_function(
131 "blackboard-set-multifield",
132 sigc::slot<void, std::string, std::string, CLIPS::Values>(
133 sigc::bind<0>(sigc::mem_fun(*this, &BlackboardCLIPSFeature::clips_blackboard_set_multifield),
134 env_name)));
135 clips->add_function("blackboard-create-msg",
136 sigc::slot<CLIPS::Value, std::string, std::string>(sigc::bind<0>(
137 sigc::mem_fun(*this, &BlackboardCLIPSFeature::clips_blackboard_create_msg),
138 env_name)));
139 clips->add_function(
140 "blackboard-list-msg-fields",
141 sigc::slot<CLIPS::Values, void *>(
142 sigc::bind<0>(sigc::mem_fun(*this, &BlackboardCLIPSFeature::clips_blackboard_list_msg_fields),
143 env_name)));
144 clips->add_function(
145 "blackboard-set-msg-field",
146 sigc::slot<void, void *, std::string, CLIPS::Value>(
147 sigc::bind<0>(sigc::mem_fun(*this, &BlackboardCLIPSFeature::clips_blackboard_set_msg_field),
148 env_name)));
149 clips->add_function("blackboard-set-msg-multifield",
150 sigc::slot<void, void *, std::string, CLIPS::Values>(sigc::bind<0>(
151 sigc::mem_fun(*this,
152 &BlackboardCLIPSFeature::clips_blackboard_set_msg_multifield),
153 env_name)));
154 clips->add_function("blackboard-send-msg",
155 sigc::slot<CLIPS::Value, void *>(sigc::bind<0>(
156 sigc::mem_fun(*this, &BlackboardCLIPSFeature::clips_blackboard_send_msg),
157 env_name)));
158}
159
160void
162{
163 if (interfaces_.find(env_name) != interfaces_.end()) {
164 for (auto &iface_map : interfaces_[env_name].reading) {
165 for (auto iface : iface_map.second) {
166 logger_->log_debug(("BBCLIPS|" + env_name).c_str(),
167 "Closing reading interface %s",
168 iface->uid());
169 blackboard_->close(iface);
170 }
171 }
172 for (auto &iface_map : interfaces_[env_name].writing) {
173 for (auto iface : iface_map.second) {
174 logger_->log_debug(("BBCLIPS|" + env_name).c_str(),
175 "Closing writing interface %s",
176 iface->uid());
177 blackboard_->close(iface);
178 }
179 }
180 interfaces_.erase(env_name);
181 }
182 envs_.erase(env_name);
183}
184
185void
186BlackboardCLIPSFeature::clips_blackboard_enable_time_read(const std::string &env_name)
187{
188 if (envs_.find(env_name) == envs_.end()) {
189 logger_->log_warn(("BBCLIPS|" + env_name).c_str(),
190 "Cannot enable reading for environment %s "
191 "(not defined)",
192 env_name.c_str());
193 return;
194 }
195
196 std::string bb_read_defrule = "(defrule blackboard-read\n"
197 " (declare (salience 1000))\n"
198 " (time $?)\n"
199 " =>\n"
200 " (blackboard-read)\n"
201 ")";
202
203 fawkes::MutexLocker lock(envs_[env_name].objmutex_ptr());
204 envs_[env_name]->build(bb_read_defrule);
205}
206
207bool
208BlackboardCLIPSFeature::clips_assert_interface_type(const std::string &env_name,
209 const std::string &log_name,
210 fawkes::Interface *iface,
211 const std::string &type)
212{
213 std::string deftemplate = "(deftemplate " + type + "\n" + " (slot id (type STRING))\n"
214 + " (multislot time (type INTEGER) (cardinality 2 2))\n";
215
216 InterfaceFieldIterator f, f_end = iface->fields_end();
217
218 for (f = iface->fields(); f != f_end; ++f) {
219 std::string type;
220
221 switch (f.get_type()) {
222 case IFT_BOOL:
223 deftemplate += std::string() + " (" + ((f.get_length() > 1) ? "multi" : "") + "slot "
224 + f.get_name() + " (type SYMBOL) (allowed-values TRUE FALSE))\n";
225 break;
226
227 case IFT_INT8:
228 case IFT_UINT8:
229 case IFT_INT16:
230 case IFT_UINT16:
231 case IFT_INT32:
232 case IFT_UINT32:
233 case IFT_INT64:
234 case IFT_UINT64:
235 case IFT_BYTE:
236 deftemplate += std::string() + " (" + ((f.get_length() > 1) ? "multi" : "") + "slot "
237 + f.get_name() + " (type INTEGER))\n";
238 break;
239
240 case IFT_FLOAT:
241 case IFT_DOUBLE:
242 deftemplate += std::string() + " (" + ((f.get_length() > 1) ? "multi" : "") + "slot "
243 + f.get_name() + " (type FLOAT))\n";
244 break;
245
246 case IFT_STRING:
247 deftemplate += std::string() + " (slot " + f.get_name() + " (type STRING))\n";
248 break;
249
250 case IFT_ENUM:
251 deftemplate += std::string() + " (" + ((f.get_length() > 1) ? "multi" : "") + "slot "
252 + f.get_name() + " (type SYMBOL))\n";
253 break;
254 }
255 }
256
257 deftemplate += ")";
258
259 std::string retract;
260 std::string logstr;
261
262 if (cfg_retract_early_) {
263 retract = "(defrule " + type + "-cleanup\n" + " (declare (salience -10000))\n" + " ?f <- ("
264 + type + ")\n"
265 + " =>\n"
266 " (retract ?f)\n"
267 ")";
268 logstr = "Defrule";
269 } else {
270 retract = "(deffunction " + type
271 + "-cleanup-late (?id)\n"
272 " (delayed-do-for-all-facts ((?f "
273 + type
274 + "))\n"
275 " (eq ?f:id ?id)\n"
276 " (retract ?f)\n"
277 " )\n"
278 ")";
279 logstr = "Deffunction";
280 }
281
282 if (envs_[env_name]->build(deftemplate) && envs_[env_name]->build(retract)) {
283 logger_->log_debug(log_name.c_str(), "Deftemplate:\n%s", deftemplate.c_str());
284 logger_->log_debug(log_name.c_str(), "%s:\n%s", logstr.c_str(), retract.c_str());
285 return true;
286 } else {
287 logger_->log_warn(log_name.c_str(),
288 "Defining blackboard type for %s in %s failed",
289 type.c_str(),
290 env_name.c_str());
291 return false;
292 }
293}
294
295void
296BlackboardCLIPSFeature::clips_blackboard_preload(const std::string &env_name,
297 const std::string &type)
298{
299 std::string name = "BBCLIPS|" + env_name;
300
301 if (envs_.find(env_name) == envs_.end()) {
302 logger_->log_warn(name.c_str(),
303 "Environment %s has not been registered "
304 "for blackboard feature",
305 env_name.c_str());
306 return;
307 }
308
309 if (interfaces_[env_name].reading.find(type) == interfaces_[env_name].reading.end()
310 && interfaces_[env_name].writing.find(type) == interfaces_[env_name].writing.end()) {
311 // no interface of this type registered yet, add deftemplate for it
312 Interface *iface = NULL;
313 try {
314 iface = blackboard_->open_for_reading(type.c_str(), "__clips_blackboard_preload__");
315 clips_assert_interface_type(env_name, name, iface, type);
316 blackboard_->close(iface);
317 interfaces_[env_name].reading.insert(std::make_pair(type, std::list<fawkes::Interface *>()));
318 } catch (Exception &e) {
319 logger_->log_warn(name.c_str(),
320 "Failed to preload interface type %s, "
321 "exception follows",
322 type.c_str());
323 logger_->log_warn(name.c_str(), e);
324 return;
325 }
326 }
327}
328
329void
330BlackboardCLIPSFeature::clips_blackboard_open_interface(const std::string &env_name,
331 const std::string &type,
332 const std::string &id,
333 bool writing)
334{
335 std::string name = "BBCLIPS|" + env_name;
336 std::string owner = "CLIPS:" + env_name;
337
338 if (envs_.find(env_name) == envs_.end()) {
339 logger_->log_warn(name.c_str(),
340 "Environment %s has not been registered "
341 "for blackboard feature",
342 env_name.c_str());
343 return;
344 }
345
346 fawkes::LockPtr<CLIPS::Environment> clips = envs_[env_name];
347
348 Interface * iface = NULL;
349 InterfaceMap &iface_map = writing ? interfaces_[env_name].writing : interfaces_[env_name].reading;
350
351 if (iface_map.find(type) == iface_map.end()) {
352 // no interface of this type registered yet, add deftemplate for it
353 try {
354 if (writing) {
355 iface = blackboard_->open_for_writing(type.c_str(), id.c_str(), owner.c_str());
356 } else {
357 iface = blackboard_->open_for_reading(type.c_str(), id.c_str(), owner.c_str());
358 }
359 } catch (Exception &e) {
360 logger_->log_warn(name.c_str(),
361 "Failed to open interface %s:%s, exception follows",
362 type.c_str(),
363 id.c_str());
364 logger_->log_warn(name.c_str(), e);
365 return;
366 }
367
368 if (!clips_assert_interface_type(env_name, name, iface, type)) {
369 blackboard_->close(iface);
370 } else {
371 logger_->log_info(name.c_str(),
372 "Added interface %s for %s",
373 iface->uid(),
374 iface->is_writer() ? "writing" : "reading");
375 iface_map.insert(std::make_pair(type, std::list<fawkes::Interface *>(1, iface)));
376 fawkes::MutexLocker lock(clips.objmutex_ptr());
377 clips->assert_fact_f("(blackboard-interface (id \"%s\") (type \"%s\") (uid \"%s\") "
378 " (hash \"%s\") (serial \"%s\") (writing %s))",
379 iface->id(),
380 iface->type(),
381 iface->uid(),
382 iface->hash_printable(),
383 iface->serial().get_string().c_str(),
384 writing ? "TRUE" : "FALSE");
385 }
386 } else {
387 auto &iface_list = iface_map[type];
388 if (std::none_of(iface_list.begin(),
389 iface_list.end(),
390 [&type, &id](const Interface *i) -> bool {
391 return (type == i->type()) && (id == i->id());
392 })) {
393 try {
394 if (writing) {
395 iface = blackboard_->open_for_writing(type.c_str(), id.c_str(), owner.c_str());
396 } else {
397 iface = blackboard_->open_for_reading(type.c_str(), id.c_str(), owner.c_str());
398 }
399 iface_map[type].push_back(iface);
400 logger_->log_info(name.c_str(),
401 "Added interface %s for %s",
402 iface->uid(),
403 iface->is_writer() ? "writing" : "reading");
404 fawkes::MutexLocker lock(clips.objmutex_ptr());
405 clips->assert_fact_f("(blackboard-interface (id \"%s\") (type \"%s\") (uid \"%s\") "
406 " (hash \"%s\") (serial \"%s\") (writing %s))",
407 iface->id(),
408 iface->type(),
409 iface->uid(),
410 iface->hash_printable(),
411 iface->serial().get_string().c_str(),
412 writing ? "TRUE" : "FALSE");
413 } catch (Exception &e) {
414 logger_->log_warn(name.c_str(),
415 "Failed to open interface %s:%s, exception follows",
416 type.c_str(),
417 id.c_str());
418 logger_->log_warn(name.c_str(), e);
419 return;
420 }
421 }
422 }
423}
424
425void
426BlackboardCLIPSFeature::clips_blackboard_open_interface_reading(const std::string &env_name,
427 const std::string &type,
428 const std::string &id)
429{
430 clips_blackboard_open_interface(env_name, type, id, /* writing */ false);
431}
432
433void
434BlackboardCLIPSFeature::clips_blackboard_open_interface_writing(const std::string &env_name,
435 const std::string &type,
436 const std::string &id)
437{
438 clips_blackboard_open_interface(env_name, type, id, /* writing */ true);
439}
440
441void
442BlackboardCLIPSFeature::clips_blackboard_close_interface(const std::string &env_name,
443 const std::string &type,
444 const std::string &id)
445{
446 std::string name = "BBCLIPS|" + env_name;
447
448 if (envs_.find(env_name) == envs_.end()) {
449 logger_->log_warn(name.c_str(),
450 "Environment %s has not been registered "
451 "for blackboard feature",
452 env_name.c_str());
453 return;
454 }
455
456 if (interfaces_[env_name].reading.find(type) != interfaces_[env_name].reading.end()) {
457 auto &l = interfaces_[env_name].reading[type];
458 auto iface_it =
459 find_if(l.begin(), l.end(), [&id](const Interface *iface) { return id == iface->id(); });
460 if (iface_it != l.end()) {
461 blackboard_->close(*iface_it);
462 l.erase(iface_it);
463 // do NOT remove the list, even if empty, because we need to remember
464 // that we already built the deftemplate and added the cleanup rule
465 }
466 }
467 if (interfaces_[env_name].writing.find(type) != interfaces_[env_name].writing.end()) {
468 auto &l = interfaces_[env_name].writing[type];
469 auto iface_it =
470 find_if(l.begin(), l.end(), [&id](const Interface *iface) { return id == iface->id(); });
471 if (iface_it != l.end()) {
472 blackboard_->close(*iface_it);
473 l.erase(iface_it);
474 // do NOT remove the list, even if empty, because we need to remember
475 // that we already built the deftemplate and added the cleanup rule
476 }
477 }
478}
479
480void
481BlackboardCLIPSFeature::clips_blackboard_read(const std::string &env_name)
482{
483 // no interfaces registered, that's fine
484 if (interfaces_.find(env_name) == interfaces_.end())
485 return;
486 if (envs_.find(env_name) == envs_.end()) {
487 // Environment not registered, big bug
488 logger_->log_warn(("BBCLIPS|" + env_name).c_str(),
489 "Environment %s not registered,"
490 " cannot read interfaces",
491 env_name.c_str());
492 return;
493 }
494
495 fawkes::MutexLocker lock(envs_[env_name].objmutex_ptr());
496 CLIPS::Environment &env = **(envs_[env_name]);
497 for (auto &iface_map : interfaces_[env_name].reading) {
498 for (auto i : iface_map.second) {
499 i->read();
500 if (i->refreshed()) {
501 if (!cfg_retract_early_) {
502 std::string fun = std::string("(") + i->type() + "-cleanup-late \"" + i->id() + "\")";
503 env.evaluate(fun);
504 }
505 const Time *t = i->timestamp();
506
507 std::string fact = std::string("(") + i->type() + " (id \"" + i->id() + "\")" + " (time "
508 + StringConversions::to_string(t->get_sec()) + " "
509 + StringConversions::to_string(t->get_usec()) + ")";
510
511 InterfaceFieldIterator f, f_end = i->fields_end();
512 for (f = i->fields(); f != f_end; ++f) {
513 std::string value;
514 if (f.get_type() == IFT_STRING) {
515 value = f.get_value_string();
516 std::string::size_type pos = 0;
517 while ((pos = value.find("\"", pos)) != std::string::npos) {
518 value.replace(pos, 1, "\\\"");
519 pos += 2;
520 }
521 value = std::string("\"") + value + "\"";
522 } else {
523 value = f.get_value_string();
524 std::string::size_type pos;
525 while ((pos = value.find(",")) != std::string::npos) {
526 value = value.erase(pos, 1);
527 }
528
529 if (f.get_type() == IFT_FLOAT || f.get_type() == IFT_DOUBLE) {
530 std::string::size_type pos;
531 while ((pos = value.find("-inf")) != std::string::npos) {
532 value = value.replace(pos, 4, std::to_string(std::numeric_limits<double>::min()));
533 }
534 while ((pos = value.find("inf")) != std::string::npos) {
535 value = value.replace(pos, 3, std::to_string(std::numeric_limits<double>::max()));
536 }
537 while ((pos = value.find("-nan")) != std::string::npos) {
538 value =
539 value.replace(pos, 4, std::to_string(std::numeric_limits<double>::min() + 1));
540 }
541 while ((pos = value.find("nan")) != std::string::npos) {
542 value =
543 value.replace(pos, 3, std::to_string(std::numeric_limits<double>::max() - 1));
544 }
545 } else if (f.get_type() == IFT_BOOL) {
546 std::string::size_type pos;
547 while ((pos = value.find("false")) != std::string::npos) {
548 value = value.replace(pos, 5, "FALSE");
549 }
550 while ((pos = value.find("true")) != std::string::npos) {
551 value = value.replace(pos, 4, "TRUE");
552 }
553 }
554 }
555 fact += std::string(" (") + f.get_name() + " " + value + ")";
556 }
557 fact += ")";
558 env.assert_fact(fact);
559 }
560 }
561 }
562}
563
564void
565BlackboardCLIPSFeature::clips_blackboard_write(const std::string &env_name, const std::string &uid)
566{
567 // no interfaces registered, that's fine
568 if (interfaces_.find(env_name) == interfaces_.end())
569 return;
570 if (envs_.find(env_name) == envs_.end()) {
571 // Environment not registered, big bug
572 logger_->log_warn(("BBCLIPS|" + env_name).c_str(),
573 "Environment %s not registered,"
574 " cannot write interface %s",
575 env_name.c_str(),
576 uid.c_str());
577 return;
578 }
579 std::string type, id;
580 Interface::parse_uid(uid.c_str(), type, id);
581 if (interfaces_[env_name].writing.find(type) != interfaces_[env_name].writing.end()) {
582 auto i = std::find_if(interfaces_[env_name].writing[type].begin(),
583 interfaces_[env_name].writing[type].end(),
584 [&uid](const Interface *iface) -> bool { return uid == iface->uid(); });
585 if (i != interfaces_[env_name].writing[type].end()) {
586 (*i)->write();
587 } else {
588 logger_->log_warn(("BBCLIPS|" + env_name).c_str(),
589 "Interface %s not opened for writing,"
590 " in environment %s",
591 uid.c_str(),
592 env_name.c_str());
593 return;
594 }
595 } else {
596 logger_->log_warn(("BBCLIPS|" + env_name).c_str(),
597 "No interface of type %s opened for,"
598 " writing in environment %s",
599 type.c_str(),
600 env_name.c_str());
601 return;
602 }
603}
604
605void
606BlackboardCLIPSFeature::clips_blackboard_get_info(const std::string &env_name)
607{
608 if (envs_.find(env_name) == envs_.end()) {
609 // Environment not registered, big bug
610 logger_->log_warn(("BBCLIPS|" + env_name).c_str(),
611 "Environment %s not registered,"
612 " cannot read interfaces",
613 env_name.c_str());
614 return;
615 }
616
617 fawkes::LockPtr<CLIPS::Environment> &clips = envs_[env_name];
618
619 InterfaceInfoList *iil = blackboard_->list_all();
620
621 fawkes::MutexLocker lock(clips.objmutex_ptr());
622 for (auto ii : *iil) {
623 const Time * timestamp = ii.timestamp();
624 std::list<std::string> quoted_readers;
625 std::list<std::string> readers = ii.readers();
626 std::for_each(readers.begin(), readers.end(), [&quoted_readers](const std::string &r) {
627 quoted_readers.push_back(std::string("\"") + r + "\"");
628 });
629 std::string quoted_readers_s = str_join(quoted_readers, ' ');
630 clips->assert_fact_f("(blackboard-interface-info (id \"%s\") (type \"%s\") "
631 "(hash \"%s\") (has-writer %s) (num-readers %u) "
632 "(writer \"%s\") (readers %s) (timestamp %u %u))",
633 ii.id(),
634 ii.type(),
635 ii.hash_printable().c_str(),
636 ii.has_writer() ? "TRUE" : "FALSE",
637 ii.num_readers(),
638 ii.writer().c_str(),
639 quoted_readers_s.c_str(),
640 timestamp->get_sec(),
641 timestamp->get_usec());
642 }
643
644 delete iil;
645}
646
647void
648BlackboardCLIPSFeature::clips_blackboard_set(const std::string &env_name,
649 const std::string &uid,
650 const std::string &field,
651 CLIPS::Value value)
652{
653 // no interfaces registered, that's fine
654 if (interfaces_.find(env_name) == interfaces_.end())
655 return;
656 if (envs_.find(env_name) == envs_.end()) {
657 // Environment not registered, big bug
658 logger_->log_warn(("BBCLIPS|" + env_name).c_str(),
659 "Environment %s not registered,"
660 " cannot set %s on interface %s",
661 env_name.c_str(),
662 field.c_str(),
663 uid.c_str());
664 return;
665 }
666 std::string type, id;
667 Interface::parse_uid(uid.c_str(), type, id);
668 if (interfaces_[env_name].writing.find(type) != interfaces_[env_name].writing.end()) {
669 auto i = std::find_if(interfaces_[env_name].writing[type].begin(),
670 interfaces_[env_name].writing[type].end(),
671 [&uid](const Interface *iface) -> bool { return uid == iface->uid(); });
672 if (i != interfaces_[env_name].writing[type].end()) {
673 set_field((*i)->fields(), (*i)->fields_end(), env_name, field, value);
674 } else {
675 logger_->log_error(("BBCLIPS|" + env_name).c_str(),
676 "Interface %s not opened for writing,"
677 " in environment %s",
678 uid.c_str(),
679 env_name.c_str());
680 return;
681 }
682 } else {
683 logger_->log_error(("BBCLIPS|" + env_name).c_str(),
684 "No interface of type %s opened for,"
685 " writing in environment %s",
686 type.c_str(),
687 env_name.c_str());
688 return;
689 }
690}
691
692void
693BlackboardCLIPSFeature::clips_blackboard_set_multifield(const std::string &env_name,
694 const std::string &uid,
695 const std::string &field,
696 CLIPS::Values values)
697{
698 // no interfaces registered, that's fine
699 if (interfaces_.find(env_name) == interfaces_.end())
700 return;
701 if (envs_.find(env_name) == envs_.end()) {
702 // Environment not registered, big bug
703 logger_->log_warn(("BBCLIPS|" + env_name).c_str(),
704 "Environment %s not registered,"
705 " cannot set %s on interface %s",
706 env_name.c_str(),
707 field.c_str(),
708 uid.c_str());
709 return;
710 }
711 std::string type, id;
712 Interface::parse_uid(uid.c_str(), type, id);
713 if (interfaces_[env_name].writing.find(type) != interfaces_[env_name].writing.end()) {
714 auto i = std::find_if(interfaces_[env_name].writing[type].begin(),
715 interfaces_[env_name].writing[type].end(),
716 [&uid](const Interface *iface) -> bool { return uid == iface->uid(); });
717 if (i != interfaces_[env_name].writing[type].end()) {
718 set_multifield((*i)->fields(), (*i)->fields_end(), env_name, field, values);
719 } else {
720 logger_->log_error(("BBCLIPS|" + env_name).c_str(),
721 "Interface %s not opened for writing,"
722 " in environment %s",
723 uid.c_str(),
724 env_name.c_str());
725 return;
726 }
727 } else {
728 logger_->log_error(("BBCLIPS|" + env_name).c_str(),
729 "No interface of type %s opened for,"
730 " writing in environment %s",
731 type.c_str(),
732 env_name.c_str());
733 return;
734 }
735}
736
737CLIPS::Value
738BlackboardCLIPSFeature::clips_blackboard_create_msg(const std::string &env_name,
739 const std::string &uid,
740 const std::string &msg_type)
741{
742 // no interfaces registered, that's fine
743 if (interfaces_.find(env_name) == interfaces_.end()) {
744 return CLIPS::Value(new std::shared_ptr<Message>());
745 }
746 if (envs_.find(env_name) == envs_.end()) {
747 // Environment not registered, big bug
748 logger_->log_warn(("BBCLIPS|" + env_name).c_str(),
749 "Environment %s not registered,"
750 " cannot read interfaces",
751 env_name.c_str());
752 return CLIPS::Value(new std::shared_ptr<Message>());
753 }
754 fawkes::MutexLocker lock(envs_[env_name].objmutex_ptr());
755
756 std::string if_type, id;
757 Interface::parse_uid(uid.c_str(), if_type, id);
758
759 //get interface
760 if (interfaces_[env_name].reading.find(if_type) == interfaces_[env_name].reading.end()) {
761 logger_->log_warn(
762 ("BBCLIPS|" + env_name).c_str(),
763 "Can't create message for interface %s, because there is no opened interface with this type",
764 uid.c_str());
765 return CLIPS::Value(new std::shared_ptr<Message>());
766 }
767 auto i = std::find_if(interfaces_[env_name].reading[if_type].begin(),
768 interfaces_[env_name].reading[if_type].end(),
769 [&uid](const Interface *iface) -> bool { return uid == iface->uid(); });
770 if (i == interfaces_[env_name].reading[if_type].end()) {
771 logger_->log_warn(
772 ("BBCLIPS|" + env_name).c_str(),
773 "Can't create message for interface %s, because there is no opened interface with that uid",
774 uid.c_str());
775 return CLIPS::Value(new std::shared_ptr<Message>());
776 }
777
778 //check if message type exists
779 std::list<const char *> available_types = (*i)->get_message_types();
780 bool type_exists = false;
781 for (std::list<const char *>::iterator it = available_types.begin();
782 it != available_types.end() && !type_exists;
783 ++it) {
784 if (std::string(*it).compare(msg_type) == 0) {
785 type_exists = true;
786 }
787 }
788 if (!type_exists) {
789 logger_->log_warn(("BBCLIPS|" + env_name).c_str(),
790 "Can't create message for interface %s, because there is no message type %s",
791 uid.c_str(),
792 msg_type.c_str());
793 return CLIPS::Value(new std::shared_ptr<Message>());
794 }
795
796 //create message
797 Message *m = (*i)->create_message(msg_type.c_str());
798
799 //save which interface belongs to the message
800 interface_of_msg_[m] = (*i);
801
802 //send message to clips
803 return CLIPS::Value(new std::shared_ptr<Message>(m));
804}
805
806CLIPS::Values
807BlackboardCLIPSFeature::clips_blackboard_list_msg_fields(const std::string &env_name, void *msgptr)
808{
809 std::shared_ptr<Message> *m = static_cast<std::shared_ptr<Message> *>(msgptr);
810 if (!*m) {
811 logger_->log_warn(("BBCLIPS|" + env_name).c_str(),
812 "Can't list message fields, the pointer is wrong.");
813 return CLIPS::Values();
814 }
815
816 const int field_count = (*m)->num_fields();
817 CLIPS::Values field_names(field_count);
818 int i = 0;
819 for (InterfaceFieldIterator it = (*m)->fields(); it != (*m)->fields_end(); ++it) {
820 field_names[i].set(it.get_name(), true);
821 logger_->log_info(("BBCLIPS|" + env_name).c_str(), "Message has field %s", it.get_name());
822 i++;
823 }
824 return field_names;
825}
826
827void
828BlackboardCLIPSFeature::clips_blackboard_set_msg_field(const std::string &env_name,
829 void * msgptr,
830 const std::string &field_name,
831 CLIPS::Value value)
832{
833 std::shared_ptr<Message> *m = static_cast<std::shared_ptr<Message> *>(msgptr);
834 if (!*m) {
835 logger_->log_warn(("BBCLIPS|" + env_name).c_str(),
836 "Can't set message field, the pointer is wrong.");
837 return;
838 }
839
840 bool set_success = set_field((*m)->fields(), (*m)->fields_end(), env_name, field_name, value);
841 if (!set_success) {
842 logger_->log_warn(("BBCLIPS|" + env_name).c_str(), "Can't set message field.");
843 }
844}
845
846void
847BlackboardCLIPSFeature::clips_blackboard_set_msg_multifield(const std::string &env_name,
848 void * msgptr,
849 const std::string &field_name,
850 CLIPS::Values values)
851{
852 std::shared_ptr<Message> *m = static_cast<std::shared_ptr<Message> *>(msgptr);
853 if (!*m) {
854 logger_->log_warn(("BBCLIPS|" + env_name).c_str(),
855 "Can't set message field, the pointer is wrong.");
856 return;
857 }
858
859 bool set_success =
860 set_multifield((*m)->fields(), (*m)->fields_end(), env_name, field_name, values);
861 if (!set_success) {
862 logger_->log_warn(("BBCLIPS|" + env_name).c_str(), "Can't set message field.");
863 }
864}
865
866CLIPS::Value
867BlackboardCLIPSFeature::clips_blackboard_send_msg(const std::string &env_name, void *msgptr)
868{
869 std::shared_ptr<Message> *m = static_cast<std::shared_ptr<Message> *>(msgptr);
870 if (!*m) {
871 logger_->log_warn(("BBCLIPS|" + env_name).c_str(),
872 "Can't set message field, the pointer is wrong.");
873 return CLIPS::Value(0);
874 }
875 if (!interface_of_msg_[m->get()]) {
876 logger_->log_warn(("BBCLIPS|" + env_name).c_str(), "Can't send message, was it already sent?");
877 return CLIPS::Value(0);
878 }
879
880 //add reference to the message so we can return the message id (otherwise it is changed by sending)
881 m->get()->ref();
882
883 unsigned int message_id = 0;
884
885 //send message about the saved interface
886 try {
887 interface_of_msg_[m->get()]->msgq_enqueue(m->get());
888 message_id = m->get()->id();
890 // keep quiet, BlackBoardMessageManager will already have printed a warning
891 //logger_->log_warn(("BBCLIPS|" + env_name).c_str(), "Failed to send message: no writer");
892 } catch (Exception &e) {
893 logger_->log_warn(("BBCLIPS|" + env_name).c_str(),
894 "Failed to send message: %s",
896 }
897
898 //delete saved pointer to interface
899 interface_of_msg_.erase(m->get());
900
901 //remove added refference
902 m->get()->unref();
903
904 return CLIPS::Value(message_id);
905}
906
907/**
908 Set array of an InterfaceFieldIterator of an Interface or Message
909 to an CLIPS-Multifield.
910 @return if field could successfully be set
911 */
912bool
913BlackboardCLIPSFeature::set_multifield(InterfaceFieldIterator fit_begin,
915 const std::string & env_name,
916 const std::string & field,
917 CLIPS::Values values)
918{
919 //find field and check for length of the interface array/multifield
921 for (fit = fit_begin; fit != fit_end; ++fit) {
922 if (field == fit.get_name()) {
923 size_t min_length = fit.get_length();
924 if (values.size() < min_length) {
925 min_length = values.size();
926 }
927 //set each entry
928 for (size_t i = 0; i < min_length; i++) {
929 bool success = set_field(fit, fit_end, env_name, field, values[i], i);
930 if (!success) {
931 return false;
932 }
933 }
934 break;
935 }
936 }
937
938 if (fit == fit_end) {
939 logger_->log_error(("BBCLIPS|" + env_name).c_str(), "Can't find field %s", field.c_str());
940 return false;
941 }
942 return true;
943}
944
945/**
946 Set field of an InterfaceFieldIterator of an Interface or Message.
947 @index index in an array of the interface (leave default for non array value)
948 @return if field could successfully be set
949 */
950bool
951BlackboardCLIPSFeature::set_field(InterfaceFieldIterator fit_begin,
953 const std::string & env_name,
954 const std::string & field,
955 CLIPS::Value value,
956 int index)
957{
959 for (fit = fit_begin; fit != fit_end; ++fit) {
960 if (field == fit.get_name()) {
961 switch (fit.get_type()) {
962 case IFT_BOOL:
963 if (value.type() != CLIPS::TYPE_SYMBOL && value.type() != CLIPS::TYPE_STRING) {
964 logger_->log_error(("BBCLIPS|" + env_name).c_str(),
965 "Cannot set field %s: invalid value (not a symbol)",
966 field.c_str());
967 return false;
968 } else {
969 std::string val_s = value.as_string();
970 if (value == "TRUE") {
971 fit.set_bool(true, index);
972 } else if (value == "FALSE") {
973 fit.set_bool(false, index);
974 } else {
975 logger_->log_error(("BBCLIPS|" + env_name).c_str(),
976 "Cannot set field %s: invalid value %s (not a bool)",
977 field.c_str(),
978 val_s.c_str());
979 return false;
980 }
981 }
982 break;
983
984 case IFT_INT8:
985 if (value.type() != CLIPS::TYPE_INTEGER) {
986 logger_->log_error(("BBCLIPS|" + env_name).c_str(),
987 "Cannot set field %s: invalid value (not an integer)",
988 field.c_str());
989 return false;
990 } else {
991 long long int val = value.as_integer();
992 fit.set_int8((int8_t)val, index);
993 }
994 break;
995
996 case IFT_UINT8:
997 if (value.type() != CLIPS::TYPE_INTEGER) {
998 logger_->log_error(("BBCLIPS|" + env_name).c_str(),
999 "Cannot set field %s: invalid value (not an integer)",
1000 field.c_str());
1001 return false;
1002 } else {
1003 long long int val = value.as_integer();
1004 fit.set_uint8((uint8_t)val, index);
1005 }
1006 break;
1007
1008 case IFT_INT16:
1009 if (value.type() != CLIPS::TYPE_INTEGER) {
1010 logger_->log_error(("BBCLIPS|" + env_name).c_str(),
1011 "Cannot set field %s: invalid value (not an integer)",
1012 field.c_str());
1013 return false;
1014 } else {
1015 long long int val = value.as_integer();
1016 fit.set_int16((int16_t)val, index);
1017 }
1018 break;
1019
1020 case IFT_UINT16:
1021 if (value.type() != CLIPS::TYPE_INTEGER) {
1022 logger_->log_error(("BBCLIPS|" + env_name).c_str(),
1023 "Cannot set field %s: invalid value (not an integer)",
1024 field.c_str());
1025 return false;
1026 } else {
1027 long long int val = value.as_integer();
1028 fit.set_uint16((uint16_t)val, index);
1029 }
1030 break;
1031
1032 case IFT_INT32:
1033 if (value.type() != CLIPS::TYPE_INTEGER) {
1034 logger_->log_error(("BBCLIPS|" + env_name).c_str(),
1035 "Cannot set field %s: invalid value (not an integer)",
1036 field.c_str());
1037 return false;
1038 } else {
1039 long long int val = value.as_integer();
1040 fit.set_int32((int32_t)val, index);
1041 }
1042 break;
1043
1044 case IFT_UINT32:
1045 if (value.type() != CLIPS::TYPE_INTEGER) {
1046 logger_->log_error(("BBCLIPS|" + env_name).c_str(),
1047 "Cannot set field %s: invalid value (not an integer)",
1048 field.c_str());
1049 return false;
1050 } else {
1051 long long int val = value.as_integer();
1052 fit.set_uint32((uint32_t)val, index);
1053 }
1054 break;
1055
1056 case IFT_INT64:
1057 if (value.type() != CLIPS::TYPE_INTEGER) {
1058 logger_->log_error(("BBCLIPS|" + env_name).c_str(),
1059 "Cannot set field %s: invalid value (not an integer)",
1060 field.c_str());
1061 return false;
1062 } else {
1063 long long int val = value.as_integer();
1064 fit.set_int64((int64_t)val, index);
1065 }
1066 break;
1067
1068 case IFT_UINT64:
1069 if (value.type() != CLIPS::TYPE_INTEGER) {
1070 logger_->log_error(("BBCLIPS|" + env_name).c_str(),
1071 "Cannot set field %s: invalid value (not an integer)",
1072 field.c_str());
1073 return false;
1074 } else {
1075 long long int val = value.as_integer();
1076 fit.set_uint64((uint64_t)val, index);
1077 }
1078 break;
1079
1080 case IFT_FLOAT:
1081 if (value.type() != CLIPS::TYPE_FLOAT && value.type() != CLIPS::TYPE_INTEGER) {
1082 logger_->log_error(("BBCLIPS|" + env_name).c_str(),
1083 "Cannot set field %s: invalid value "
1084 "(neither float nor integer)",
1085 field.c_str());
1086 return false;
1087 } else {
1088 if (value.type() == CLIPS::TYPE_FLOAT) {
1089 double val = value.as_float();
1090 fit.set_float((float)val, index);
1091 } else {
1092 long long int val = value.as_integer();
1093 fit.set_float((float)val, index);
1094 }
1095 }
1096 break;
1097
1098 case IFT_DOUBLE:
1099 if (value.type() != CLIPS::TYPE_FLOAT && value.type() != CLIPS::TYPE_INTEGER) {
1100 logger_->log_error(("BBCLIPS|" + env_name).c_str(),
1101 "Cannot set field %s: invalid value "
1102 "(neither double nor integer)",
1103 field.c_str());
1104 return false;
1105 } else {
1106 if (value.type() == CLIPS::TYPE_FLOAT) {
1107 double val = value.as_float();
1108 fit.set_double((double)val, index);
1109 } else {
1110 long long int val = value.as_integer();
1111 fit.set_double((double)val, index);
1112 }
1113 }
1114 break;
1115
1116 case IFT_STRING:
1117 if (value.type() != CLIPS::TYPE_SYMBOL && value.type() != CLIPS::TYPE_STRING) {
1118 logger_->log_error(("BBCLIPS|" + env_name).c_str(),
1119 "Cannot set field %s: invalid value "
1120 "(neither symbol nor string)",
1121 field.c_str());
1122 return false;
1123 } else {
1124 std::string val = value.as_string();
1125 fit.set_string(val.c_str());
1126 if (index != 0) {
1127 logger_->log_error(("BBCLIPS|" + env_name).c_str(),
1128 "Cannot set field %s[%d]: "
1129 "there are no string arrays in interfaces",
1130 field.c_str(),
1131 index);
1132 }
1133 }
1134 break;
1135
1136 case IFT_ENUM:
1137 if (value.type() != CLIPS::TYPE_SYMBOL) {
1138 logger_->log_error(("BBCLIPS|" + env_name).c_str(),
1139 "Cannot set field %s: invalid value "
1140 "(not a symbol)",
1141 field.c_str());
1142 } else {
1143 try {
1144 std::string val = value.as_string();
1145 fit.set_enum_string(val.c_str(), index);
1146 } catch (Exception &e) {
1147 logger_->log_error(("BBCLIPS|" + env_name).c_str(),
1148 "Failed to set enum field %s to %s, exception follows",
1149 field.c_str(),
1150 value.as_string().c_str());
1151 logger_->log_error(("BBCLIPS|" + env_name).c_str(), e);
1152 return false;
1153 }
1154 }
1155 break;
1156
1157 default:
1158 logger_->log_error(("BBCLIPS|" + env_name).c_str(),
1159 "Setting of field type %s for %s not supported",
1160 fit.get_typename(),
1161 field.c_str());
1162 return false;
1163 }
1164
1165 break;
1166 }
1167 }
1168
1169 if (fit == fit_end) {
1170 logger_->log_error(("BBCLIPS|" + env_name).c_str(), "Can't find field %s", field.c_str());
1171 return false;
1172 }
1173 return true;
1174}
virtual void clips_context_destroyed(const std::string &env_name)
Notification that a CLIPS environment has been destroyed.
virtual void clips_context_init(const std::string &env_name, fawkes::LockPtr< CLIPS::Environment > &clips)
Initialize a CLIPS context to use the provided feature.
virtual ~BlackboardCLIPSFeature()
Destructor.
BlackboardCLIPSFeature(fawkes::Logger *logger, fawkes::BlackBoard *blackboard, bool retract_early)
Constructor.
Thrown if no writer interface is alive.
Definition: exceptions.h:151
The BlackBoard abstract class.
Definition: blackboard.h:46
virtual Interface * open_for_reading(const char *interface_type, const char *identifier, const char *owner=NULL)=0
Open interface for reading.
virtual InterfaceInfoList * list_all()=0
Get list of all currently existing interfaces.
virtual Interface * open_for_writing(const char *interface_type, const char *identifier, const char *owner=NULL)=0
Open interface for writing.
virtual void close(Interface *interface)=0
Close interface.
CLIPS feature maintainer.
Definition: clips_feature.h:42
Base class for exceptions in Fawkes.
Definition: exception.h:36
virtual const char * what_no_backtrace() const noexcept
Get primary string (does not implicitly print the back trace).
Definition: exception.cpp:663
Interface field iterator.
void set_int64(int64_t i, unsigned int index=0)
Set value of current field as integer.
void set_string(const char *s)
Set value of current field as string.
size_t get_length() const
Get length of current field.
void set_double(double f, unsigned int index=0)
Set value of current field as double.
void set_int16(int16_t i, unsigned int index=0)
Set value of current field as integer.
void set_uint64(uint64_t i, unsigned int index=0)
Set value of current field as unsigned integer.
interface_fieldtype_t get_type() const
Get type of current field.
const char * get_name() const
Get name of current field.
void set_float(float f, unsigned int index=0)
Set value of current field as float.
void set_enum_string(const char *e, unsigned int index=0)
Set value of current field as enum (from an integer).
void set_uint16(uint16_t i, unsigned int index=0)
Set value of current field as unsigned integer.
void set_int32(int32_t i, unsigned int index=0)
Set value of current field as integer.
const char * get_value_string(const char *array_sep=", ")
Get value of current field as string.
void set_bool(bool b, unsigned int index=0)
Set value of current field as bool.
void set_int8(int8_t i, unsigned int index=0)
Set value of current field as integer.
void set_uint8(uint8_t i, unsigned int index=0)
Set value of current field as unsigned integer.
void set_uint32(uint32_t i, unsigned int index=0)
Set value of current field as unsigned integer.
const char * get_typename() const
Get type of current field as string.
Interface information list.
Base class for all Fawkes BlackBoard interfaces.
Definition: interface.h:80
const char * hash_printable() const
Get printable interface hash.
Definition: interface.cpp:314
const char * type() const
Get type of interface.
Definition: interface.cpp:652
const Time * timestamp() const
Get timestamp of last write.
Definition: interface.cpp:714
InterfaceFieldIterator fields_end()
Invalid iterator.
Definition: interface.cpp:1240
bool is_writer() const
Check if this is a writing instance.
Definition: interface.cpp:445
void write()
Write from local copy into BlackBoard memory.
Definition: interface.cpp:501
const char * id() const
Get identifier of interface.
Definition: interface.cpp:661
InterfaceFieldIterator fields()
Get iterator over all fields of this interface instance.
Definition: interface.cpp:1231
Uuid serial() const
Get instance serial of interface.
Definition: interface.cpp:695
const char * uid() const
Get unique identifier of interface.
Definition: interface.cpp:686
void read()
Read from BlackBoard into local copy.
Definition: interface.cpp:479
bool refreshed() const
Check if data has been refreshed.
Definition: interface.cpp:811
Mutex * objmutex_ptr() const
Get object mutex.
Definition: lockptr.h:284
Interface for logging.
Definition: logger.h:42
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.
virtual void log_info(const char *component, const char *format,...)=0
Log informational message.
Base class for all messages passed through interfaces in Fawkes BlackBoard.
Definition: message.h:44
Mutex locking helper.
Definition: mutex_locker.h:34
A class for handling time.
Definition: time.h:93
long get_usec() const
Get microseconds.
Definition: time.h:127
long get_sec() const
Get seconds.
Definition: time.h:117
std::string get_string() const
Get the string representation of the Uuid.
Definition: uuid.cpp:107
Fawkes library namespace.
std::string str_join(const InputIterator &first, const InputIterator &last, char delim='/')
Join list of strings string using given delimiter.
Definition: string_split.h:139
@ IFT_INT8
8 bit integer field
Definition: types.h:38
@ IFT_UINT32
32 bit unsigned integer field
Definition: types.h:43
@ IFT_FLOAT
float field
Definition: types.h:46
@ IFT_BYTE
byte field, alias for uint8
Definition: types.h:49
@ IFT_UINT64
64 bit unsigned integer field
Definition: types.h:45
@ IFT_UINT16
16 bit unsigned integer field
Definition: types.h:41
@ IFT_INT32
32 bit integer field
Definition: types.h:42
@ IFT_INT64
64 bit integer field
Definition: types.h:44
@ IFT_DOUBLE
double field
Definition: types.h:47
@ IFT_INT16
16 bit integer field
Definition: types.h:40
@ IFT_STRING
string field
Definition: types.h:48
@ IFT_BOOL
boolean field
Definition: types.h:37
@ IFT_ENUM
field with interface specific enum type
Definition: types.h:50
@ IFT_UINT8
8 bit unsigned integer field
Definition: types.h:39