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