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