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 
36 using 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 
80 void
81 BlackboardCLIPSFeature::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 
160 void
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 
185 void
186 BlackboardCLIPSFeature::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 
207 bool
208 BlackboardCLIPSFeature::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 
295 void
296 BlackboardCLIPSFeature::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 
329 void
330 BlackboardCLIPSFeature::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 %u) (writing %s))",
379  iface->id(),
380  iface->type(),
381  iface->uid(),
382  iface->hash_printable(),
383  iface->serial(),
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 %u) (writing %s))",
407  iface->id(),
408  iface->type(),
409  iface->uid(),
410  iface->hash_printable(),
411  iface->serial(),
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 
425 void
426 BlackboardCLIPSFeature::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 
433 void
434 BlackboardCLIPSFeature::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 
441 void
442 BlackboardCLIPSFeature::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 
480 void
481 BlackboardCLIPSFeature::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->changed()) {
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_BOOL) {
515  value = f.get_bool() ? "TRUE" : "FALSE";
516  } else if (f.get_type() == IFT_STRING) {
517  value = f.get_value_string();
518  std::string::size_type pos = 0;
519  while ((pos = value.find("\"", pos)) != std::string::npos) {
520  value.replace(pos, 1, "\\\"");
521  pos += 2;
522  }
523  value = std::string("\"") + value + "\"";
524  } else {
525  value = f.get_value_string();
526  std::string::size_type pos;
527  while ((pos = value.find(",")) != std::string::npos) {
528  value = value.erase(pos, 1);
529  }
530 
531  if (f.get_type() == IFT_FLOAT || f.get_type() == IFT_DOUBLE) {
532  std::string::size_type pos;
533  while ((pos = value.find("-inf")) != std::string::npos) {
534  value = value.replace(pos, 4, std::to_string(std::numeric_limits<double>::min()));
535  }
536  while ((pos = value.find("inf")) != std::string::npos) {
537  value = value.replace(pos, 3, std::to_string(std::numeric_limits<double>::max()));
538  }
539  while ((pos = value.find("-nan")) != std::string::npos) {
540  value =
541  value.replace(pos, 4, std::to_string(std::numeric_limits<double>::min() + 1));
542  }
543  while ((pos = value.find("nan")) != std::string::npos) {
544  value =
545  value.replace(pos, 3, std::to_string(std::numeric_limits<double>::max() - 1));
546  }
547  }
548  }
549  fact += std::string(" (") + f.get_name() + " " + value + ")";
550  }
551  fact += ")";
552  env.assert_fact(fact);
553  }
554  }
555  }
556 }
557 
558 void
559 BlackboardCLIPSFeature::clips_blackboard_write(const std::string &env_name, const std::string &uid)
560 {
561  // no interfaces registered, that's fine
562  if (interfaces_.find(env_name) == interfaces_.end())
563  return;
564  if (envs_.find(env_name) == envs_.end()) {
565  // Environment not registered, big bug
566  logger_->log_warn(("BBCLIPS|" + env_name).c_str(),
567  "Environment %s not registered,"
568  " cannot write interface %s",
569  env_name.c_str(),
570  uid.c_str());
571  return;
572  }
573  std::string type, id;
574  Interface::parse_uid(uid.c_str(), type, id);
575  if (interfaces_[env_name].writing.find(type) != interfaces_[env_name].writing.end()) {
576  auto i = std::find_if(interfaces_[env_name].writing[type].begin(),
577  interfaces_[env_name].writing[type].end(),
578  [&uid](const Interface *iface) -> bool { return uid == iface->uid(); });
579  if (i != interfaces_[env_name].writing[type].end()) {
580  (*i)->write();
581  } else {
582  logger_->log_warn(("BBCLIPS|" + env_name).c_str(),
583  "Interface %s not opened for writing,"
584  " in environment %s",
585  uid.c_str(),
586  env_name.c_str());
587  return;
588  }
589  } else {
590  logger_->log_warn(("BBCLIPS|" + env_name).c_str(),
591  "No interface of type %s opened for,"
592  " writing in environment %s",
593  type.c_str(),
594  env_name.c_str());
595  return;
596  }
597 }
598 
599 void
600 BlackboardCLIPSFeature::clips_blackboard_get_info(const std::string &env_name)
601 {
602  if (envs_.find(env_name) == envs_.end()) {
603  // Environment not registered, big bug
604  logger_->log_warn(("BBCLIPS|" + env_name).c_str(),
605  "Environment %s not registered,"
606  " cannot read interfaces",
607  env_name.c_str());
608  return;
609  }
610 
611  fawkes::LockPtr<CLIPS::Environment> &clips = envs_[env_name];
612 
613  InterfaceInfoList *iil = blackboard_->list_all();
614 
615  fawkes::MutexLocker lock(clips.objmutex_ptr());
616  for (auto ii : *iil) {
617  const Time * timestamp = ii.timestamp();
618  std::list<std::string> quoted_readers;
619  std::list<std::string> readers = ii.readers();
620  std::for_each(readers.begin(), readers.end(), [&quoted_readers](const std::string &r) {
621  quoted_readers.push_back(std::string("\"") + r + "\"");
622  });
623  std::string quoted_readers_s = str_join(quoted_readers, ' ');
624  clips->assert_fact_f("(blackboard-interface-info (id \"%s\") (type \"%s\") "
625  "(hash \"%s\") (has-writer %s) (num-readers %u) "
626  "(writer \"%s\") (readers %s) (timestamp %u %u))",
627  ii.id(),
628  ii.type(),
629  ii.hash_printable().c_str(),
630  ii.has_writer() ? "TRUE" : "FALSE",
631  ii.num_readers(),
632  ii.writer().c_str(),
633  quoted_readers_s.c_str(),
634  timestamp->get_sec(),
635  timestamp->get_usec());
636  }
637 
638  delete iil;
639 }
640 
641 void
642 BlackboardCLIPSFeature::clips_blackboard_set(const std::string &env_name,
643  const std::string &uid,
644  const std::string &field,
645  CLIPS::Value value)
646 {
647  // no interfaces registered, that's fine
648  if (interfaces_.find(env_name) == interfaces_.end())
649  return;
650  if (envs_.find(env_name) == envs_.end()) {
651  // Environment not registered, big bug
652  logger_->log_warn(("BBCLIPS|" + env_name).c_str(),
653  "Environment %s not registered,"
654  " cannot set %s on interface %s",
655  env_name.c_str(),
656  field.c_str(),
657  uid.c_str());
658  return;
659  }
660  std::string type, id;
661  Interface::parse_uid(uid.c_str(), type, id);
662  if (interfaces_[env_name].writing.find(type) != interfaces_[env_name].writing.end()) {
663  auto i = std::find_if(interfaces_[env_name].writing[type].begin(),
664  interfaces_[env_name].writing[type].end(),
665  [&uid](const Interface *iface) -> bool { return uid == iface->uid(); });
666  if (i != interfaces_[env_name].writing[type].end()) {
667  set_field((*i)->fields(), (*i)->fields_end(), env_name, field, value);
668  } else {
669  logger_->log_error(("BBCLIPS|" + env_name).c_str(),
670  "Interface %s not opened for writing,"
671  " in environment %s",
672  uid.c_str(),
673  env_name.c_str());
674  return;
675  }
676  } else {
677  logger_->log_error(("BBCLIPS|" + env_name).c_str(),
678  "No interface of type %s opened for,"
679  " writing in environment %s",
680  type.c_str(),
681  env_name.c_str());
682  return;
683  }
684 }
685 
686 void
687 BlackboardCLIPSFeature::clips_blackboard_set_multifield(const std::string &env_name,
688  const std::string &uid,
689  const std::string &field,
690  CLIPS::Values values)
691 {
692  // no interfaces registered, that's fine
693  if (interfaces_.find(env_name) == interfaces_.end())
694  return;
695  if (envs_.find(env_name) == envs_.end()) {
696  // Environment not registered, big bug
697  logger_->log_warn(("BBCLIPS|" + env_name).c_str(),
698  "Environment %s not registered,"
699  " cannot set %s on interface %s",
700  env_name.c_str(),
701  field.c_str(),
702  uid.c_str());
703  return;
704  }
705  std::string type, id;
706  Interface::parse_uid(uid.c_str(), type, id);
707  if (interfaces_[env_name].writing.find(type) != interfaces_[env_name].writing.end()) {
708  auto i = std::find_if(interfaces_[env_name].writing[type].begin(),
709  interfaces_[env_name].writing[type].end(),
710  [&uid](const Interface *iface) -> bool { return uid == iface->uid(); });
711  if (i != interfaces_[env_name].writing[type].end()) {
712  set_multifield((*i)->fields(), (*i)->fields_end(), env_name, field, values);
713  } else {
714  logger_->log_error(("BBCLIPS|" + env_name).c_str(),
715  "Interface %s not opened for writing,"
716  " in environment %s",
717  uid.c_str(),
718  env_name.c_str());
719  return;
720  }
721  } else {
722  logger_->log_error(("BBCLIPS|" + env_name).c_str(),
723  "No interface of type %s opened for,"
724  " writing in environment %s",
725  type.c_str(),
726  env_name.c_str());
727  return;
728  }
729 }
730 
731 CLIPS::Value
732 BlackboardCLIPSFeature::clips_blackboard_create_msg(const std::string &env_name,
733  const std::string &uid,
734  const std::string &msg_type)
735 {
736  // no interfaces registered, that's fine
737  if (interfaces_.find(env_name) == interfaces_.end()) {
738  return CLIPS::Value(new std::shared_ptr<Message>());
739  }
740  if (envs_.find(env_name) == envs_.end()) {
741  // Environment not registered, big bug
742  logger_->log_warn(("BBCLIPS|" + env_name).c_str(),
743  "Environment %s not registered,"
744  " cannot read interfaces",
745  env_name.c_str());
746  return CLIPS::Value(new std::shared_ptr<Message>());
747  }
748  fawkes::MutexLocker lock(envs_[env_name].objmutex_ptr());
749 
750  std::string if_type, id;
751  Interface::parse_uid(uid.c_str(), if_type, id);
752 
753  //get interface
754  if (interfaces_[env_name].reading.find(if_type) == interfaces_[env_name].reading.end()) {
755  logger_->log_warn(
756  ("BBCLIPS|" + env_name).c_str(),
757  "Can't create message for interface %s, because there is no opened interface with this type",
758  uid.c_str());
759  return CLIPS::Value(new std::shared_ptr<Message>());
760  }
761  auto i = std::find_if(interfaces_[env_name].reading[if_type].begin(),
762  interfaces_[env_name].reading[if_type].end(),
763  [&uid](const Interface *iface) -> bool { return uid == iface->uid(); });
764  if (i == interfaces_[env_name].reading[if_type].end()) {
765  logger_->log_warn(
766  ("BBCLIPS|" + env_name).c_str(),
767  "Can't create message for interface %s, because there is no opened interface with that uid",
768  uid.c_str());
769  return CLIPS::Value(new std::shared_ptr<Message>());
770  }
771 
772  //check if message type exists
773  std::list<const char *> available_types = (*i)->get_message_types();
774  bool type_exists = false;
775  for (std::list<const char *>::iterator it = available_types.begin();
776  it != available_types.end() && !type_exists;
777  ++it) {
778  if (std::string(*it).compare(msg_type) == 0) {
779  type_exists = true;
780  }
781  }
782  if (!type_exists) {
783  logger_->log_warn(("BBCLIPS|" + env_name).c_str(),
784  "Can't create message for interface %s, because there is no message type %s",
785  uid.c_str(),
786  msg_type.c_str());
787  return CLIPS::Value(new std::shared_ptr<Message>());
788  }
789 
790  //create message
791  Message *m = (*i)->create_message(msg_type.c_str());
792 
793  //save which interface belongs to the message
794  interface_of_msg_[m] = (*i);
795 
796  //send message to clips
797  return CLIPS::Value(new std::shared_ptr<Message>(m));
798 }
799 
800 CLIPS::Values
801 BlackboardCLIPSFeature::clips_blackboard_list_msg_fields(const std::string &env_name, void *msgptr)
802 {
803  std::shared_ptr<Message> *m = static_cast<std::shared_ptr<Message> *>(msgptr);
804  if (!*m) {
805  logger_->log_warn(("BBCLIPS|" + env_name).c_str(),
806  "Can't list message fields, the pointer is wrong.");
807  return CLIPS::Values();
808  }
809 
810  const int field_count = (*m)->num_fields();
811  CLIPS::Values field_names(field_count);
812  int i = 0;
813  for (InterfaceFieldIterator it = (*m)->fields(); it != (*m)->fields_end(); ++it) {
814  field_names[i].set(it.get_name(), true);
815  logger_->log_info(("BBCLIPS|" + env_name).c_str(), "Message has field %s", it.get_name());
816  i++;
817  }
818  return field_names;
819 }
820 
821 void
822 BlackboardCLIPSFeature::clips_blackboard_set_msg_field(const std::string &env_name,
823  void * msgptr,
824  const std::string &field_name,
825  CLIPS::Value value)
826 {
827  std::shared_ptr<Message> *m = static_cast<std::shared_ptr<Message> *>(msgptr);
828  if (!*m) {
829  logger_->log_warn(("BBCLIPS|" + env_name).c_str(),
830  "Can't set message field, the pointer is wrong.");
831  return;
832  }
833 
834  bool set_success = set_field((*m)->fields(), (*m)->fields_end(), env_name, field_name, value);
835  if (!set_success) {
836  logger_->log_warn(("BBCLIPS|" + env_name).c_str(), "Can't set message field.");
837  }
838 }
839 
840 void
841 BlackboardCLIPSFeature::clips_blackboard_set_msg_multifield(const std::string &env_name,
842  void * msgptr,
843  const std::string &field_name,
844  CLIPS::Values values)
845 {
846  std::shared_ptr<Message> *m = static_cast<std::shared_ptr<Message> *>(msgptr);
847  if (!*m) {
848  logger_->log_warn(("BBCLIPS|" + env_name).c_str(),
849  "Can't set message field, the pointer is wrong.");
850  return;
851  }
852 
853  bool set_success =
854  set_multifield((*m)->fields(), (*m)->fields_end(), env_name, field_name, values);
855  if (!set_success) {
856  logger_->log_warn(("BBCLIPS|" + env_name).c_str(), "Can't set message field.");
857  }
858 }
859 
860 CLIPS::Value
861 BlackboardCLIPSFeature::clips_blackboard_send_msg(const std::string &env_name, void *msgptr)
862 {
863  std::shared_ptr<Message> *m = static_cast<std::shared_ptr<Message> *>(msgptr);
864  if (!*m) {
865  logger_->log_warn(("BBCLIPS|" + env_name).c_str(),
866  "Can't set message field, the pointer is wrong.");
867  return CLIPS::Value(0);
868  }
869  if (!interface_of_msg_[m->get()]) {
870  logger_->log_warn(("BBCLIPS|" + env_name).c_str(), "Can't send message, was it already sent?");
871  return CLIPS::Value(0);
872  }
873 
874  //add reference to the message so we can return the message id (otherwise it is changed by sending)
875  m->get()->ref();
876 
877  unsigned int message_id = 0;
878 
879  //send message about the saved interface
880  try {
881  interface_of_msg_[m->get()]->msgq_enqueue(m->get());
882  message_id = m->get()->id();
884  // keep quiet, BlackBoardMessageManager will already have printed a warning
885  //logger_->log_warn(("BBCLIPS|" + env_name).c_str(), "Failed to send message: no writer");
886  } catch (Exception &e) {
887  logger_->log_warn(("BBCLIPS|" + env_name).c_str(),
888  "Failed to send message: %s",
889  e.what_no_backtrace());
890  }
891 
892  //delete saved pointer to interface
893  interface_of_msg_.erase(m->get());
894 
895  //remove added refference
896  m->get()->unref();
897 
898  return CLIPS::Value(message_id);
899 }
900 
901 /**
902  Set array of an InterfaceFieldIterator of an Interface or Message
903  to an CLIPS-Multifield.
904  @return if field could successfully be set
905  */
906 bool
907 BlackboardCLIPSFeature::set_multifield(InterfaceFieldIterator fit_begin,
908  InterfaceFieldIterator fit_end,
909  const std::string & env_name,
910  const std::string & field,
911  CLIPS::Values values)
912 {
913  //find field and check for length of the interface array/multifield
915  for (fit = fit_begin; fit != fit_end; ++fit) {
916  if (field == fit.get_name()) {
917  size_t min_length = fit.get_length();
918  if (values.size() < min_length) {
919  min_length = values.size();
920  }
921  //set each entry
922  for (size_t i = 0; i < min_length; i++) {
923  bool success = set_field(fit, fit_end, env_name, field, values[i], i);
924  if (!success) {
925  return false;
926  }
927  }
928  break;
929  }
930  }
931 
932  if (fit == fit_end) {
933  logger_->log_error(("BBCLIPS|" + env_name).c_str(), "Can't find field %s", field.c_str());
934  return false;
935  }
936  return true;
937 }
938 
939 /**
940  Set field of an InterfaceFieldIterator of an Interface or Message.
941  @index index in an array of the interface (leave default for non array value)
942  @return if field could successfully be set
943  */
944 bool
945 BlackboardCLIPSFeature::set_field(InterfaceFieldIterator fit_begin,
946  InterfaceFieldIterator fit_end,
947  const std::string & env_name,
948  const std::string & field,
949  CLIPS::Value value,
950  int index)
951 {
953  for (fit = fit_begin; fit != fit_end; ++fit) {
954  if (field == fit.get_name()) {
955  switch (fit.get_type()) {
956  case IFT_BOOL:
957  if (value.type() != CLIPS::TYPE_SYMBOL && value.type() != CLIPS::TYPE_STRING) {
958  logger_->log_error(("BBCLIPS|" + env_name).c_str(),
959  "Cannot set field %s: invalid value (not a symbol)",
960  field.c_str());
961  return false;
962  } else {
963  std::string val_s = value.as_string();
964  if (value == "TRUE") {
965  fit.set_bool(true, index);
966  } else if (value == "FALSE") {
967  fit.set_bool(false, index);
968  } else {
969  logger_->log_error(("BBCLIPS|" + env_name).c_str(),
970  "Cannot set field %s: invalid value %s (not a bool)",
971  field.c_str(),
972  val_s.c_str());
973  return false;
974  }
975  }
976  break;
977 
978  case IFT_INT8:
979  if (value.type() != CLIPS::TYPE_INTEGER) {
980  logger_->log_error(("BBCLIPS|" + env_name).c_str(),
981  "Cannot set field %s: invalid value (not an integer)",
982  field.c_str());
983  return false;
984  } else {
985  long long int val = value.as_integer();
986  fit.set_int8((int8_t)val, index);
987  }
988  break;
989 
990  case IFT_UINT8:
991  if (value.type() != CLIPS::TYPE_INTEGER) {
992  logger_->log_error(("BBCLIPS|" + env_name).c_str(),
993  "Cannot set field %s: invalid value (not an integer)",
994  field.c_str());
995  return false;
996  } else {
997  long long int val = value.as_integer();
998  fit.set_uint8((uint8_t)val, index);
999  }
1000  break;
1001 
1002  case IFT_INT16:
1003  if (value.type() != CLIPS::TYPE_INTEGER) {
1004  logger_->log_error(("BBCLIPS|" + env_name).c_str(),
1005  "Cannot set field %s: invalid value (not an integer)",
1006  field.c_str());
1007  return false;
1008  } else {
1009  long long int val = value.as_integer();
1010  fit.set_int16((int16_t)val, index);
1011  }
1012  break;
1013 
1014  case IFT_UINT16:
1015  if (value.type() != CLIPS::TYPE_INTEGER) {
1016  logger_->log_error(("BBCLIPS|" + env_name).c_str(),
1017  "Cannot set field %s: invalid value (not an integer)",
1018  field.c_str());
1019  return false;
1020  } else {
1021  long long int val = value.as_integer();
1022  fit.set_uint16((uint16_t)val, index);
1023  }
1024  break;
1025 
1026  case IFT_INT32:
1027  if (value.type() != CLIPS::TYPE_INTEGER) {
1028  logger_->log_error(("BBCLIPS|" + env_name).c_str(),
1029  "Cannot set field %s: invalid value (not an integer)",
1030  field.c_str());
1031  return false;
1032  } else {
1033  long long int val = value.as_integer();
1034  fit.set_int32((int32_t)val, index);
1035  }
1036  break;
1037 
1038  case IFT_UINT32:
1039  if (value.type() != CLIPS::TYPE_INTEGER) {
1040  logger_->log_error(("BBCLIPS|" + env_name).c_str(),
1041  "Cannot set field %s: invalid value (not an integer)",
1042  field.c_str());
1043  return false;
1044  } else {
1045  long long int val = value.as_integer();
1046  fit.set_uint32((uint32_t)val, index);
1047  }
1048  break;
1049 
1050  case IFT_INT64:
1051  if (value.type() != CLIPS::TYPE_INTEGER) {
1052  logger_->log_error(("BBCLIPS|" + env_name).c_str(),
1053  "Cannot set field %s: invalid value (not an integer)",
1054  field.c_str());
1055  return false;
1056  } else {
1057  long long int val = value.as_integer();
1058  fit.set_int64((int64_t)val, index);
1059  }
1060  break;
1061 
1062  case IFT_UINT64:
1063  if (value.type() != CLIPS::TYPE_INTEGER) {
1064  logger_->log_error(("BBCLIPS|" + env_name).c_str(),
1065  "Cannot set field %s: invalid value (not an integer)",
1066  field.c_str());
1067  return false;
1068  } else {
1069  long long int val = value.as_integer();
1070  fit.set_uint64((uint64_t)val, index);
1071  }
1072  break;
1073 
1074  case IFT_FLOAT:
1075  if (value.type() != CLIPS::TYPE_FLOAT && value.type() != CLIPS::TYPE_INTEGER) {
1076  logger_->log_error(("BBCLIPS|" + env_name).c_str(),
1077  "Cannot set field %s: invalid value "
1078  "(neither float nor integer)",
1079  field.c_str());
1080  return false;
1081  } else {
1082  if (value.type() == CLIPS::TYPE_FLOAT) {
1083  double val = value.as_float();
1084  fit.set_float((float)val, index);
1085  } else {
1086  long long int val = value.as_integer();
1087  fit.set_float((float)val, index);
1088  }
1089  }
1090  break;
1091 
1092  case IFT_DOUBLE:
1093  if (value.type() != CLIPS::TYPE_FLOAT && value.type() != CLIPS::TYPE_INTEGER) {
1094  logger_->log_error(("BBCLIPS|" + env_name).c_str(),
1095  "Cannot set field %s: invalid value "
1096  "(neither double nor integer)",
1097  field.c_str());
1098  return false;
1099  } else {
1100  if (value.type() == CLIPS::TYPE_FLOAT) {
1101  double val = value.as_float();
1102  fit.set_double((double)val, index);
1103  } else {
1104  long long int val = value.as_integer();
1105  fit.set_double((double)val, index);
1106  }
1107  }
1108  break;
1109 
1110  case IFT_STRING:
1111  if (value.type() != CLIPS::TYPE_SYMBOL && value.type() != CLIPS::TYPE_STRING) {
1112  logger_->log_error(("BBCLIPS|" + env_name).c_str(),
1113  "Cannot set field %s: invalid value "
1114  "(neither symbol nor string)",
1115  field.c_str());
1116  return false;
1117  } else {
1118  std::string val = value.as_string();
1119  fit.set_string(val.c_str());
1120  if (index != 0) {
1121  logger_->log_error(("BBCLIPS|" + env_name).c_str(),
1122  "Cannot set field %s[%d]: "
1123  "there are no string arrays in interfaces",
1124  field.c_str(),
1125  index);
1126  }
1127  }
1128  break;
1129 
1130  case IFT_ENUM:
1131  if (value.type() != CLIPS::TYPE_SYMBOL) {
1132  logger_->log_error(("BBCLIPS|" + env_name).c_str(),
1133  "Cannot set field %s: invalid value "
1134  "(not a symbol)",
1135  field.c_str());
1136  } else {
1137  try {
1138  std::string val = value.as_string();
1139  fit.set_enum_string(val.c_str(), index);
1140  } catch (Exception &e) {
1141  logger_->log_error(("BBCLIPS|" + env_name).c_str(),
1142  "Failed to set enum field %s to %s, exception follows",
1143  field.c_str(),
1144  value.as_string().c_str());
1145  logger_->log_error(("BBCLIPS|" + env_name).c_str(), e);
1146  return false;
1147  }
1148  }
1149  break;
1150 
1151  default:
1152  logger_->log_error(("BBCLIPS|" + env_name).c_str(),
1153  "Setting of field type %s for %s not supported",
1154  fit.get_typename(),
1155  field.c_str());
1156  return false;
1157  }
1158 
1159  break;
1160  }
1161  }
1162 
1163  if (fit == fit_end) {
1164  logger_->log_error(("BBCLIPS|" + env_name).c_str(), "Can't find field %s", field.c_str());
1165  return false;
1166  }
1167  return true;
1168 }
64 bit integer field
Definition: types.h:44
Interface field iterator.
void set_int64(int64_t i, unsigned int index=0)
Set value of current field as integer.
virtual void clips_context_init(const std::string &env_name, fawkes::LockPtr< CLIPS::Environment > &clips)
Initialize a CLIPS context to use the provided feature.
Base class for all messages passed through interfaces in Fawkes BlackBoard.
Definition: message.h:41
unsigned int id() const
Get message ID.
Definition: message.cpp:190
void set_float(float f, unsigned int index=0)
Set value of current field as float.
const char * get_typename() const
Get type of current field as string.
virtual void log_info(const char *component, const char *format,...)=0
Log informational message.
void unref()
Decrement reference count and conditionally delete this instance.
Definition: refcount.cpp:95
void set_bool(bool b, unsigned int index=0)
Set value of current field as bool.
void set_int16(int16_t i, unsigned int index=0)
Set value of current field as integer.
virtual ~BlackboardCLIPSFeature()
Destructor.
Fawkes library namespace.
bool get_bool(unsigned int index=0) const
Get value of current field as bool.
8 bit unsigned integer field
Definition: types.h:39
Mutex locking helper.
Definition: mutex_locker.h:33
void set_uint16(uint16_t i, unsigned int index=0)
Set value of current field as unsigned integer.
16 bit unsigned integer field
Definition: types.h:41
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
const char * id() const
Get identifier of interface.
Definition: interface.cpp:649
void set_int8(int8_t i, unsigned int index=0)
Set value of current field as integer.
interface_fieldtype_t get_type() const
Get type of current field.
void set_uint8(uint8_t i, unsigned int index=0)
Set value of current field as unsigned integer.
string field
Definition: types.h:48
A class for handling time.
Definition: time.h:92
byte field, alias for uint8
Definition: types.h:49
Base class for all Fawkes BlackBoard interfaces.
Definition: interface.h:78
16 bit integer field
Definition: types.h:40
void set_int32(int32_t i, unsigned int index=0)
Set value of current field as integer.
Thrown if no writer interface is alive.
Definition: exceptions.h:150
void set_double(double f, unsigned int index=0)
Set value of current field as double.
Interface information list.
const char * type() const
Get type of interface.
Definition: interface.cpp:640
Base class for exceptions in Fawkes.
Definition: exception.h:35
unsigned short serial() const
Get instance serial of interface.
Definition: interface.cpp:683
Mutex * objmutex_ptr() const
Get object mutex.
Definition: lockptr.h:284
const char * get_name() const
Get name of current field.
void ref()
Increment reference count.
Definition: refcount.cpp:67
CLIPS feature maintainer.
Definition: clips_feature.h:41
const char * uid() const
Get unique identifier of interface.
Definition: interface.cpp:674
virtual const char * what_no_backtrace() const
Get primary string (does not implicitly print the back trace).
Definition: exception.cpp:663
void set_string(const char *s)
Set value of current field as string.
64 bit unsigned integer field
Definition: types.h:45
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.
void set_uint32(uint32_t i, unsigned int index=0)
Set value of current field as unsigned integer.
float field
Definition: types.h:46
BlackboardCLIPSFeature(fawkes::Logger *logger, fawkes::BlackBoard *blackboard, bool retract_early)
Constructor.
size_t get_length() const
Get length of current field.
bool is_writer() const
Check if this is a writing instance.
Definition: interface.cpp:438
long get_sec() const
Get seconds.
Definition: time.h:117
32 bit integer field
Definition: types.h:42
InterfaceFieldIterator fields_end()
Invalid iterator.
Definition: interface.cpp:1204
long get_usec() const
Get microseconds.
Definition: time.h:127
virtual void log_debug(const char *component, const char *format,...)=0
Log debug message.
virtual InterfaceInfoList * list_all()=0
Get list of all currently existing interfaces.
virtual Interface * open_for_reading(const char *interface_type, const char *identifier, const char *owner=NULL)=0
Open interface for reading.
void set_enum_string(const char *e, unsigned int index=0)
Set value of current field as enum (from an integer).
void set_uint64(uint64_t i, unsigned int index=0)
Set value of current field as unsigned integer.
The BlackBoard abstract class.
Definition: blackboard.h:45
InterfaceFieldIterator fields()
Get iterator over all fields of this interface instance.
Definition: interface.cpp:1195
boolean field
Definition: types.h:37
virtual Interface * open_for_writing(const char *interface_type, const char *identifier, const char *owner=NULL)=0
Open interface for writing.
32 bit unsigned integer field
Definition: types.h:43
field with interface specific enum type
Definition: types.h:50
8 bit integer field
Definition: types.h:38
double field
Definition: types.h:47
const char * hash_printable() const
Get printable interface hash.
Definition: interface.cpp:307
virtual void clips_context_destroyed(const std::string &env_name)
Notification that a CLIPS environment has been destroyed.
virtual void close(Interface *interface)=0
Close interface.
Interface for logging.
Definition: logger.h:41