Fawkes API  Fawkes Development Version
mod_skiller.cpp
1 
2 /***************************************************************************
3  * mod_skiller.cpp - OpenPRS skiller module
4  *
5  * Created: Fri Aug 22 14:32:01 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 <interfaces/SkillerInterface.h>
26 #include <utils/misc/string_conversions.h>
27 
28 using namespace fawkes;
29 
30 extern "C" void finalize();
31 
32 // Global State
33 BlackBoard *blackboard;
34 SkillerInterface *skiller_if;
35 
36 std::string g_skill_string;
37 unsigned int g_skill_msgid = 0;
38 Thread_Intention_Block *g_skill_tib = NULL;
39 
40 std::string
41 gen_skill_string(TermList terms)
42 {
43  int terms_len = sl_slist_length(terms);
44  Term *name = (Term *)get_list_pos(terms, 1);
45  std::string skill_string = std::string(name->u.string) + "{";
46  for (int i = 2; i < terms_len; i += 2) {
47  Term *key_t = (Term *)get_list_pos(terms, i);
48  Term *val_t = (Term *)get_list_pos(terms, i+1);
49 
50  if (key_t->type != TT_ATOM && key_t->type != STRING) {
51  fprintf(stderr, "Error: skill argument key neither of type "
52  "SYMBOL/ATOM nor STRING (%i)\n", key_t->type);
53  return "";
54  }
55 
56  const char *arg_key;
57  if (i > 2) skill_string += ", ";
58 
59  arg_key = (key_t->type == TT_ATOM) ? key_t->u.id : key_t->u.string;
60  skill_string += std::string(arg_key) + "=";
61 
62  switch (val_t->type) {
63  case INTEGER: skill_string += StringConversions::to_string(val_t->u.intval); break;
64  case LONG_LONG: skill_string += StringConversions::to_string((long int)val_t->u.llintval); break;
65  case TT_FLOAT: skill_string += StringConversions::to_string(*val_t->u.doubleptr); break;
66  case STRING: skill_string += std::string("\"") + val_t->u.string + "\""; break;
67  case TT_ATOM: skill_string += val_t->u.id; break;
68  case VARIABLE:
69  {
70  Envar *env;
71  sl_loop_through_slist(global_var_list, env, Envar *) {
72  if (strcmp(env->name, val_t->u.var->name) == 0) {
73  skill_string += env->value->u.string;
74  } else {
75  skill_string += "nil";
76  }
77  }
78  break;
79  }
80  default:
81  fprintf(stderr, "Warning: unknown variable type for skill %s argument %s, using nil\n",
82  name->u.string, arg_key);
83  skill_string += "nil";
84  }
85  }
86  skill_string += "}";
87 
88  return skill_string;
89 }
90 
91 
92 bool
93 assert_exclusive_controller(unsigned int num_tries, unsigned int delay_msec)
94 {
95  skiller_if->read();
96  if (skiller_if->exclusive_controller() == skiller_if->serial()) return true;
97 
98  for (unsigned int i = 0; i < num_tries; ++i) {
99  if (!skiller_if->has_writer()) return false;
100 
101  skiller_if->read();
102  if (skiller_if->exclusive_controller() != skiller_if->serial()) {
104  new SkillerInterface::AcquireControlMessage(/* steal control */ false);
105  skiller_if->msgq_enqueue(msg);
106  usleep(delay_msec * 1000);
107  } else {
108  break;
109  }
110  }
111  skiller_if->read();
112  return (skiller_if->exclusive_controller() == skiller_if->serial());
113 }
114 
115 extern "C"
116 Term *
117 action_skill_call(TermList terms)
118 {
119  int terms_len = sl_slist_length(terms);
120  if (terms_len == 0) {
121  fprintf(stderr, "Error: no arguments to skill call\n");
122  ACTION_FAIL();
123  }
124 
125  Term *name;
126  ACTION_SET_AND_ASSERT_ARG_TYPE("skill-call", name, terms, 1, STRING);
127 
128  if (terms_len % 2 == 0) {
129  fprintf(stderr, "Error: invalid number of arguments (%i) to skill call for %s\n",
130  terms_len, name->u.string);
131  ACTION_FAIL();
132  }
133 
134  // there seems to be a race condition in OpenPRS. Without this mini sleep
135  // action_first_call() would almost always return true
136  usleep(500);
137  if (action_first_call()) {
138  skiller_if->read();
139  if (!skiller_if->has_writer()) {
140  fprintf(stderr, "Cannot send skill, interface has no writer\n");
141  ACTION_FAIL();
142  }
143  if (! assert_exclusive_controller(20, 100)) {
144  fprintf(stderr, "Cannot send skill, not exclusive controller\n");
145  ACTION_FAIL();
146  }
147 
148  std::string skill_string = gen_skill_string(terms);
149  if (skill_string.empty()) {
150  fprintf(stderr, "Error: failed to generate skill string\n");
151  ACTION_FAIL();
152  }
153 
154  printf("Calling skill %s\n", skill_string.c_str());
155 
157  new SkillerInterface::ExecSkillMessage(skill_string.c_str());
158  msg->ref();
159 
160  skiller_if->msgq_enqueue(msg);
161 
162  g_skill_msgid = msg->id();
163  g_skill_string = skill_string;
164 
165  msg->unref();
166 
167  g_skill_tib = current_tib;
168 
169  ACTION_WAIT();
170  } else {
171  if (current_tib != g_skill_tib) {
172  fprintf(stderr, "Skill preempted by another skill, returning fail");
173  ACTION_FAIL();
174  }
175 
176  // we are called again due to :wait
177  skiller_if->read();
178  if (skiller_if->msgid() > g_skill_msgid) {
179  fprintf(stderr, "Fail: a more recent message is being processed by the skiller (%u > %u)\n",
180  skiller_if->msgid(), g_skill_msgid);
181  ACTION_FAIL();
182  } else if (skiller_if->msgid() < g_skill_msgid) {
183  // waiting to become active
184  ACTION_WAIT();
185  } else {
186  // currently running
187  switch (skiller_if->status()) {
189  printf("Skill %s is FINAL\n", name->u.string);
190  ACTION_FINAL();
191 
194  printf("Skill %s has FAILED\n", name->u.string);
195  ACTION_FAIL();
196 
197  default: ACTION_WAIT();
198  }
199  }
200  }
201 }
202 
203 
204 
205 /** Entry function for the OpenPRS module. */
206 extern "C"
207 void init()
208 {
209  printf("*** LOADING mod_skiller\n");
210 
211  std::string fawkes_host;
212  unsigned short fawkes_port = 0;
213  get_fawkes_host_port(fawkes_host, fawkes_port);
214 
215  printf("Connecting to Fawkes at %s:%u\n", fawkes_host.c_str(), fawkes_port);
216  try {
217  blackboard = new RemoteBlackBoard(fawkes_host.c_str(), fawkes_port);
218  } catch (Exception &e) {
219  fprintf(stderr, "Error: cannot establish blackboard connection: %s\n",
220  e.what_no_backtrace());
221  }
222 
223  skiller_if = blackboard->open_for_reading<SkillerInterface>("Skiller");
224 
225  printf("Acquiring exclusive skiller control\n");
227  new SkillerInterface::AcquireControlMessage(/* steal control */ true);
228  skiller_if->msgq_enqueue(msg);
229 
230  declare_atom("true");
231  declare_atom("false");
232  make_and_declare_action("skill-call", action_skill_call, -1);
233  add_user_end_kernel_hook(finalize);
234 }
235 
236 /** Finalization function for the OpenPRS module. */
237 extern "C"
238 void finalize()
239 {
240  printf("*** DESTROYING mod_skiller\n");
241  if (skiller_if->has_writer()) {
244  skiller_if->msgq_enqueue(msg);
245  usleep(100000);
246  }
247 
248  blackboard->close(skiller_if);
249  usleep(100000);
250  delete blackboard;
251 }
The skill string has been successfully processed.
Fawkes library namespace.
ReleaseControlMessage Fawkes BlackBoard Interface Message.
uint32_t exclusive_controller() const
Get exclusive_controller value.
AcquireControlMessage Fawkes BlackBoard Interface Message.
Base class for exceptions in Fawkes.
Definition: exception.h:36
unsigned short serial() const
Get instance serial of interface.
Definition: interface.cpp:697
void read()
Read from BlackBoard into local copy.
Definition: interface.cpp:477
void ref()
Increment reference count.
Definition: refcount.cpp:70
bool has_writer() const
Check if there is a writer for the interface.
Definition: interface.cpp:834
SkillStatusEnum status() const
Get status value.
ExecSkillMessage Fawkes BlackBoard Interface Message.
virtual const char * what_no_backtrace() const
Get primary string (does not implicitly print the back trace).
Definition: exception.cpp:686
unsigned int msgq_enqueue(Message *message)
Enqueue message at end of queue.
Definition: interface.cpp:903
The execution failed and cannot succeed anymore.
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.
uint32_t msgid() const
Get msgid value.
SkillerInterface Fawkes BlackBoard Interface.
The BlackBoard abstract class.
Definition: blackboard.h:48
static std::string to_string(unsigned int i)
Convert unsigned int value to a string.
virtual void close(Interface *interface)=0
Close interface.