Fawkes API  Fawkes Development Version
mod_blackboard.cpp
1 
2 /***************************************************************************
3  * mod_blackboard.cpp - OpenPRS blackboard module
4  *
5  * Created: Tue Sep 02 10:38:03 2014
6  * Copyright 2014 Tim Niemueller [www.niemueller.de]
7  ****************************************************************************/
8 
9 /* This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU Library General Public License for more details.
18  *
19  * Read the full text in the LICENSE.GPL file in the doc directory.
20  */
21 
22 #include <blackboard/remote.h>
23 #include <plugins/openprs/mod_utils.h>
24 #include <utils/misc/string_conversions.h>
25 #include <utils/time/time.h>
26 
27 #include <lisp-list_f-pub.h>
28 #include <oprs-array_f-pub.h>
29 #include <oprs-type_f-pub.h>
30 #include <oprs_f-pub.h>
31 #include <slistPack_f.h>
32 
33 using namespace fawkes;
34 
35 extern "C" void finalize();
36 
37 // Global variables
38 BlackBoard * g_blackboard = NULL;
39 std::map<std::string, Interface *> g_interfaces_read;
40 std::map<std::string, Interface *> g_interfaces_write;
41 Symbol g_bb_read_sym;
42 Symbol g_bb_write_sym;
43 Symbol g_bb_data_sym;
44 
45 extern "C" Term *
46 action_blackboard_open(TermList terms)
47 {
48  int terms_len = sl_slist_length(terms);
49  if (terms_len != 3) {
50  fprintf(stderr,
51  "Error[bb-open-interface]: invalid number of "
52  "arguments: req 3, got %i\n",
53  terms_len);
54  ACTION_FAIL();
55  }
56 
57  Term *type = (Term *)get_list_pos(terms, 1);
58  Term *id = (Term *)get_list_pos(terms, 2);
59  Term *mode = (Term *)get_list_pos(terms, 3);
60  if (type->type != STRING) {
61  fprintf(stderr, "Error[bb-open-interface]: interface type is not a STRING\n");
62  ACTION_FAIL();
63  }
64  if (id->type != STRING) {
65  fprintf(stderr, "Error[bb-open-interface]: interface ID is not a STRING\n");
66  ACTION_FAIL();
67  }
68  if (id->type != STRING) {
69  fprintf(stderr, "Error[bb-open-interface]: interface ID is not a STRING\n");
70  ACTION_FAIL();
71  }
72  if (mode->type != TT_ATOM) {
73  fprintf(stderr, "Error[bb-open-interface]: interface mode is not a symbol\n");
74  ACTION_FAIL();
75  }
76  if (mode->u.id != g_bb_read_sym && mode->u.id != g_bb_write_sym) {
77  fprintf(stderr, "Error[bb-open-interface]: interface mode must be BB-READ or BB-WRITE\n");
78  ACTION_FAIL();
79  }
80 
81  std::string uid = std::string(type->u.string) + "::" + id->u.string;
82 
83  if (mode->u.id == g_bb_read_sym) {
84  if (g_interfaces_read.find(uid) == g_interfaces_read.end()) {
85  try {
86  printf("Opening interface %s::%s for reading\n", type->u.string, id->u.string);
87  Interface *iface = g_blackboard->open_for_reading(type->u.string, id->u.string);
88  g_interfaces_read[uid] = iface;
89  } catch (Exception &e) {
90  fprintf(stderr,
91  "Failed to open interface %s::%s: %s",
92  type->u.string,
93  id->u.string,
94  e.what_no_backtrace());
95  ACTION_FAIL();
96  }
97  }
98  } else {
99  if (g_interfaces_write.find(uid) == g_interfaces_write.end()) {
100  try {
101  printf("Opening interface %s::%s for writing\n", type->u.string, id->u.string);
102  Interface *iface = g_blackboard->open_for_writing(type->u.string, id->u.string);
103  g_interfaces_write[uid] = iface;
104  } catch (Exception &e) {
105  fprintf(stderr,
106  "Failed to open interface %s::%s: %s\n",
107  type->u.string,
108  id->u.string,
109  e.what_no_backtrace());
110  ACTION_FAIL();
111  }
112  }
113  }
114 
115  ACTION_FINAL();
116 }
117 
118 extern "C" Term *
119 action_blackboard_close(TermList terms)
120 {
121  int terms_len = sl_slist_length(terms);
122  if (terms_len != 2) {
123  fprintf(stderr,
124  "Error[bb-close-interface]: invalid number of "
125  "arguments: req 2, got %i\n",
126  terms_len);
127  ACTION_FAIL();
128  }
129 
130  Term *type = (Term *)get_list_pos(terms, 1);
131  Term *id = (Term *)get_list_pos(terms, 2);
132  if (type->type != STRING) {
133  fprintf(stderr, "Error[bb-close-interface]: interface type is not a STRING\n");
134  ACTION_FAIL();
135  }
136  if (id->type != STRING) {
137  fprintf(stderr, "Error[bb-close-interface]: interface ID is not a STRING\n");
138  ACTION_FAIL();
139  }
140  if (id->type != STRING) {
141  fprintf(stderr, "Error[bb-close-interface]: interface ID is not a STRING\n");
142  ACTION_FAIL();
143  }
144 
145  std::string uid = std::string(type->u.string) + "::" + id->u.string;
146 
147  if (g_interfaces_read.find(uid) != g_interfaces_read.end()) {
148  try {
149  printf("Closing reading interface %s::%s\n", type->u.string, id->u.string);
150  g_blackboard->close(g_interfaces_read[uid]);
151  g_interfaces_read.erase(uid);
152  } catch (Exception &e) {
153  fprintf(stderr,
154  "Failed to close interface %s::%s: %s",
155  type->u.string,
156  id->u.string,
157  e.what_no_backtrace());
158  ACTION_FAIL();
159  }
160  } else if (g_interfaces_write.find(uid) != g_interfaces_write.end()) {
161  try {
162  printf("Closing writing interface %s::%s\n", type->u.string, id->u.string);
163  g_blackboard->close(g_interfaces_write[uid]);
164  g_interfaces_write.erase(uid);
165  } catch (Exception &e) {
166  fprintf(stderr,
167  "Failed to close interface %s::%s: %s\n",
168  type->u.string,
169  id->u.string,
170  e.what_no_backtrace());
171  ACTION_FAIL();
172  }
173  }
174 
175  ACTION_FINAL();
176 }
177 
178 extern "C" Term *
179 action_blackboard_print(TermList terms)
180 {
181  Term *type = (Term *)get_list_pos(terms, 1);
182  Term *id = (Term *)get_list_pos(terms, 2);
183  if (type->type != STRING) {
184  fprintf(stderr, "Error[bb-print]: interface type is not a STRING\n");
185  ACTION_FAIL();
186  }
187  if (id->type != STRING) {
188  fprintf(stderr, "Error[bb-print]: interface ID is not a STRING\n");
189  ACTION_FAIL();
190  }
191  if (id->type != STRING) {
192  fprintf(stderr, "Error[bb-print]: interface ID is not a STRING\n");
193  ACTION_FAIL();
194  }
195 
196  std::string uid = std::string(type->u.string) + "::" + id->u.string;
197  //printf("*** Called to print %s\n", uid.c_str());
198 
199  Interface *i = NULL;
200  if (g_interfaces_read.find(uid) != g_interfaces_read.end()) {
201  i = g_interfaces_read[uid];
202  } else if (g_interfaces_write.find(uid) != g_interfaces_write.end()) {
203  i = g_interfaces_write[uid];
204  } else {
205  fprintf(stderr, "Error[bb-print]: interface %s has not been opened\n", uid.c_str());
206  fprintf(stderr, "Error[bb-print]: Open interfaces are:\n");
207  for (auto j : g_interfaces_read) {
208  fprintf(stderr, "Error[bb-print]: [R] %s\n", j.second->uid());
209  }
210  for (auto j : g_interfaces_write) {
211  fprintf(stderr, "Error[bb-print]: [W] %s\n", j.second->uid());
212  }
213  fprintf(stderr, "Error[bb-print]: -----\n");
214  ACTION_FAIL();
215  }
216 
217  try {
218  i->read();
219  const Time *t = i->timestamp();
220 
221  std::string fact = std::string("(bb-data \"type\" \"") + i->type() + "\"" + " \"id\" \""
222  + i->id() + "\"" + " \"time\" " + StringConversions::to_string(t->get_sec())
223  + " " + StringConversions::to_string(t->get_usec()) + "" + " (. ";
224 
225  InterfaceFieldIterator f, f_end = i->fields_end();
226  for (f = i->fields(); f != f_end; ++f) {
227  std::string value;
228  if (f.get_type() == IFT_STRING) {
229  value = f.get_value_string();
230  std::string::size_type pos = 0;
231  while ((pos = value.find("\"", pos)) != std::string::npos) {
232  value.replace(pos, 1, "\\\"");
233  pos += 2;
234  }
235  value = std::string("\"") + value + "\"";
236  } else if (f.get_type() == IFT_ENUM) {
237  value = std::string("\"") + f.get_value_string(" ") + "\"";
238  } else {
239  value = f.get_value_string(" ");
240  std::string::size_type pos;
241  while ((pos = value.find(",")) != std::string::npos) {
242  value = value.erase(pos, 1);
243  }
244  }
245  if (f.get_length() > 1) {
246  fact += std::string(" \"") + f.get_name() + "\" [ " + value + " ]";
247  } else {
248  fact += std::string(" \"") + f.get_name() + "\" " + value;
249  }
250  }
251  fact += " .))";
252  //envs_[env_name]->assert_fact(fact);
253  printf("%s\n", fact.c_str());
254  } catch (Exception &e) {
255  fprintf(stderr, "Error[bb-print]: %s\n", e.what_no_backtrace());
256  ACTION_FAIL();
257  }
258  ACTION_FINAL();
259 }
260 
261 #define ADD_ARRAY(src_type, target_type, array_type) \
262  do { \
263  target_type * array = (target_type *)OPRS_MALLOC(sizeof(target_type) * f.get_length()); \
264  src_type##_t *src_array = f.get_##src_type##s(); \
265  for (unsigned int j = 0; j < f.get_length(); ++j) \
266  array[j] = src_array[j]; \
267  data = l_add_to_tail(data, make_##array_type##_array_from_array(f.get_length(), array)); \
268  } while (0);
269 
270 #define BUILD_FUNC(singular_type) build_##singular_type
271 #define GET_FUNC(src_type) get_##src_type
272 
273 #define ADD_NUM_DATA(src_type, target_type, array_type, singular_type) \
274  do { \
275  if (f.get_length() > 1) { \
276  ADD_ARRAY(src_type, target_type, array_type); \
277  } else { \
278  data = l_add_to_tail(data, BUILD_FUNC(singular_type)(f.GET_FUNC(src_type)())); \
279  } \
280  } while (0);
281 
282 static void
283 post_interface(Interface *i)
284 {
285  i->read();
286  if (i->refreshed()) {
287  const Time *t = i->timestamp();
288 
289  TermList tl = sl_make_slist();
290  tl = build_term_list(tl, build_string("type"));
291  tl = build_term_list(tl, build_string(i->type()));
292  tl = build_term_list(tl, build_string("id"));
293  tl = build_term_list(tl, build_string(i->id()));
294  tl = build_term_list(tl, build_string("time"));
295  tl = build_term_list(tl, build_long_long(t->get_sec()));
296  tl = build_term_list(tl, build_long_long(t->get_usec()));
297 
298  L_List data = l_nil;
299  InterfaceFieldIterator f, f_end = i->fields_end();
300  for (f = i->fields(); f != f_end; ++f) {
301  data = l_add_to_tail(data, build_string(f.get_name()));
302 
303  switch (f.get_type()) {
304  case IFT_BOOL:
305  data = l_add_to_tail(data, build_id(f.get_bool() ? lisp_t_sym : nil_sym));
306  break;
307  case IFT_INT8: ADD_NUM_DATA(int8, int, int, integer); break;
308  case IFT_UINT8: ADD_NUM_DATA(uint8, int, int, integer); break;
309  case IFT_INT16: ADD_NUM_DATA(int16, int, int, integer); break;
310  case IFT_UINT16: ADD_NUM_DATA(uint16, int, int, integer); break;
311  case IFT_INT32: ADD_NUM_DATA(int32, int, int, integer); break;
312  case IFT_UINT32: ADD_NUM_DATA(uint32, double, float, long_long); break;
313  case IFT_INT64: ADD_NUM_DATA(int64, double, float, long_long); break;
314  case IFT_UINT64: ADD_NUM_DATA(uint64, double, float, long_long); break;
315  case IFT_FLOAT: ADD_NUM_DATA(float, double, float, float); break;
316  case IFT_DOUBLE: ADD_NUM_DATA(double, double, float, float); break;
317  case IFT_STRING: data = l_add_to_tail(data, build_string(f.get_value_string())); break;
318  case IFT_BYTE: ADD_NUM_DATA(uint8, int, int, integer); break;
319  case IFT_ENUM: data = l_add_to_tail(data, build_string(f.get_value_string())); break;
320  }
321  }
322 
323  tl = build_term_list(tl, build_l_list(data));
324  add_external_fact((char *)"bb-data", tl);
325  }
326 }
327 
328 extern "C" Term *
329 action_blackboard_read_all(TermList terms)
330 {
331  try {
332  for (auto &if_entry : g_interfaces_read) {
333  Interface *i = if_entry.second;
334  post_interface(i);
335  }
336  } catch (Exception &e) {
337  fprintf(stderr, "Error[bb-read]: read failed: %s\n", e.what_no_backtrace());
338  ACTION_FAIL();
339  }
340  ACTION_FINAL();
341 }
342 
343 extern "C" Term *
344 action_blackboard_read(TermList terms)
345 {
346  int terms_len = sl_slist_length(terms);
347  if (terms_len != 2) {
348  fprintf(stderr,
349  "Error[bb-read]: invalid number of "
350  "arguments: req 2, got %i\n",
351  terms_len);
352  ACTION_FAIL();
353  }
354 
355  Term *type = (Term *)get_list_pos(terms, 1);
356  Term *id = (Term *)get_list_pos(terms, 2);
357  if (type->type != STRING) {
358  fprintf(stderr, "Error[bb-read]: interface type is not a STRING\n");
359  ACTION_FAIL();
360  }
361  if (id->type != STRING) {
362  fprintf(stderr, "Error[bb-read]: interface ID is not a STRING\n");
363  ACTION_FAIL();
364  }
365  if (id->type != STRING) {
366  fprintf(stderr, "Error[bb-read]: interface ID is not a STRING\n");
367  ACTION_FAIL();
368  }
369 
370  std::string uid = std::string(type->u.string) + "::" + id->u.string;
371 
372  if (g_interfaces_read.find(uid) != g_interfaces_read.end()) {
373  try {
374  post_interface(g_interfaces_read[uid]);
375  } catch (Exception &e) {
376  fprintf(stderr,
377  "Failed to read interface %s::%s: %s",
378  type->u.string,
379  id->u.string,
380  e.what_no_backtrace());
381  ACTION_FAIL();
382  }
383  } else {
384  fprintf(stderr,
385  "Failed to read interface %s::%s: interface not opened",
386  type->u.string,
387  id->u.string);
388  ACTION_FAIL();
389  }
390 
391  ACTION_FINAL();
392 }
393 
394 /** Searches for a given entry in the bb-date object
395  * specified by String. Returns nil if entry not present */
396 extern "C" Term *
397 func_blackboard_value(TermList terms)
398 {
399  int terms_len = sl_slist_length(terms);
400  if (terms_len != 2) {
401  fprintf(stderr,
402  "Error[bb-value]: invalid number of "
403  "arguments: req 2, got %i\n",
404  terms_len);
405  ACTION_FAIL();
406  }
407  Term *dlist = (Term *)get_list_pos(terms, 1);
408  Term *name = (Term *)get_list_pos(terms, 2);
409  Term *restemp;
410 
411  if (dlist->type != LISP_LIST) {
412  fprintf(stderr, "Error[bb-value]: first argument is not a LISP_LIST\n");
413  ACTION_FAIL();
414  }
415  if (name->type != STRING) {
416  fprintf(stderr, "Error[bb-value]: interface ID is not a STRING\n");
417  ACTION_FAIL();
418  }
419  char *pattern = name->u.string;
420  int i = 1;
421  while (i < l_length(dlist->u.l_list) - 1) {
422  Term *t1 = get_term_from_l_car(l_nth(dlist->u.l_list, i));
423  if (t1->type == STRING) {
424  char *searched = t1->u.string;
425  if (strcmp(pattern, searched) == 0) {
426  restemp = get_term_from_l_car(l_nth((dlist->u).l_list, i + 1));
427  // cast string objects to symbols to prevent upper-/lowercase
428  // differences in db.
429  if (restemp->type == STRING) {
430  std::string outputs = std::string(restemp->u.string);
431  std::transform(outputs.begin(), outputs.end(), outputs.begin(), ::tolower);
432  restemp = build_id(declare_atom(outputs.c_str()));
433  }
434  return copy_term(restemp);
435  }
436  ++i;
437  }
438  }
439  fprintf(stderr, "Error[bb-value]: wanted entry in bb-data not present\n");
440  ACTION_FAIL();
441 }
442 
443 /** Entry function for the OpenPRS module. */
444 extern "C" void
445 init()
446 {
447  printf("*** LOADING mod_blackboard\n");
448 
449  std::string fawkes_host;
450  unsigned short fawkes_port = 0;
451  get_fawkes_host_port(fawkes_host, fawkes_port);
452 
453  printf("Connecting to Fawkes at %s:%u\n", fawkes_host.c_str(), fawkes_port);
454  try {
455  g_blackboard = new RemoteBlackBoard(fawkes_host.c_str(), fawkes_port);
456  } catch (Exception &e) {
457  fprintf(stderr, "Error: cannot establish blackboard connection: %s\n", e.what_no_backtrace());
458  }
459 
460  g_bb_read_sym = declare_atom("BB-READ");
461  g_bb_write_sym = declare_atom("BB-WRITE");
462  g_bb_data_sym = declare_atom("bb-data");
463  declare_pred_from_symbol(g_bb_data_sym);
464  make_and_declare_eval_funct("bb-value", func_blackboard_value, 2);
465  make_and_declare_action("bb-open", action_blackboard_open, 3);
466  make_and_declare_action("bb-close", action_blackboard_close, 2);
467  make_and_declare_action("bb-read", action_blackboard_read, 2);
468  make_and_declare_action("bb-read-all", action_blackboard_read_all, 0);
469  make_and_declare_action("bb-print", action_blackboard_print, 2);
470  add_user_end_kernel_hook(finalize);
471 }
472 
473 /** Finalization function for the OpenPRS module. */
474 extern "C" void
475 finalize()
476 {
477  printf("*** DESTROYING mod_skiller\n");
478  for (auto &iface : g_interfaces_read) {
479  g_blackboard->close(iface.second);
480  }
481  g_interfaces_read.clear();
482  for (auto &iface : g_interfaces_write) {
483  g_blackboard->close(iface.second);
484  }
485  g_interfaces_write.clear();
486 
487  delete g_blackboard;
488  g_blackboard = NULL;
489 }
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 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.
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.
size_t get_length() const
Get length of current field.
interface_fieldtype_t get_type() const
Get type of current field.
const char * get_name() const
Get name of current field.
bool get_bool(unsigned int index=0) const
Get value of current field as bool.
const char * get_value_string(const char *array_sep=", ")
Get value of current field as string.
Base class for all Fawkes BlackBoard interfaces.
Definition: interface.h:80
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
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
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
Remote BlackBoard.
Definition: remote.h:50
static std::string to_string(unsigned int i)
Convert unsigned int value to a string.
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
Fawkes library namespace.
@ 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