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