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
27using namespace fawkes;
28
29extern "C" void finalize();
30
31// Global State
32BlackBoard * blackboard;
33SkillerInterface *skiller_if;
34
35std::string g_skill_string;
36unsigned int g_skill_msgid = 0;
37Thread_Intention_Block *g_skill_tib = NULL;
38
39std::string
40gen_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
97bool
98assert_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
122extern "C" Term *
123action_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
200 case SkillerInterface::S_FAILED:
201 case SkillerInterface::S_INACTIVE:
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. */
212extern "C" void
213init()
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. */
242extern "C" void
243finalize()
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 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 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
unsigned int msgq_enqueue(Message *message, bool proxy=false)
Enqueue message at end of queue.
Definition: interface.cpp:915
Uuid serial() const
Get instance serial of interface.
Definition: interface.cpp:695
void read()
Read from BlackBoard into local copy.
Definition: interface.cpp:479
bool has_writer() const
Check if there is a writer for the interface.
Definition: interface.cpp:848
unsigned int id() const
Get message ID.
Definition: message.cpp:181
void unref()
Decrement reference count and conditionally delete this instance.
Definition: refcount.cpp:95
void ref()
Increment reference count.
Definition: refcount.cpp:67
Remote BlackBoard.
Definition: remote.h:50
AcquireControlMessage Fawkes BlackBoard Interface Message.
ExecSkillMessage Fawkes BlackBoard Interface Message.
ReleaseControlMessage Fawkes BlackBoard Interface Message.
SkillerInterface Fawkes BlackBoard Interface.
SkillStatusEnum status() const
Get status value.
uint32_t msgid() const
Get msgid value.
char * exclusive_controller() const
Get exclusive_controller value.
Fawkes library namespace.