Fawkes API Fawkes Development Version
globals_adapter.cpp
1
2/***************************************************************************
3 * globals_adapter.cpp - PLEXIL adapter for global state
4 *
5 * Created: Thu Aug 16 11:06:55 2018
6 * Copyright 2006-2018 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 "globals_adapter.h"
23
24#include "utils.h"
25
26#include <AdapterConfiguration.hh>
27#include <AdapterExecInterface.hh>
28#include <AdapterFactory.hh>
29#include <Command.hh>
30#include <InterfaceManager.hh>
31#include <StateCacheEntry.hh>
32#include <limits>
33
34using namespace fawkes;
35
36/** @class GlobalStatePlexilAdapter "config_adapter.h"
37 * Plexil adapter to provide access to the Fawkes configuration.
38 * @author Tim Niemueller
39 */
40
41/** Constructor.
42 * @param execInterface Reference to the parent AdapterExecInterface object.
43 */
44GlobalStatePlexilAdapter::GlobalStatePlexilAdapter(PLEXIL::AdapterExecInterface &execInterface)
45: InterfaceAdapter(execInterface)
46{
47}
48
49/** Constructor from configuration XML.
50 * @param execInterface Reference to the parent AdapterExecInterface object.
51 * @param xml A const reference to the XML element describing this adapter
52 * @note The instance maintains a shared pointer to the XML.
53 */
54GlobalStatePlexilAdapter::GlobalStatePlexilAdapter(PLEXIL::AdapterExecInterface &execInterface,
55 pugi::xml_node const xml)
56: InterfaceAdapter(execInterface, xml)
57{
58}
59
60/** Destructor. */
62{
63}
64
65/** Initialize adapter.
66 * @return true if initialization was successful, false otherwise.
67 */
68bool
70{
71 config_ =
72 reinterpret_cast<fawkes::Configuration *>(m_execInterface.getProperty("::Fawkes::Config"));
73 logger_ = reinterpret_cast<fawkes::Logger *>(m_execInterface.getProperty("::Fawkes::Logger"));
74
75 if (!config_ || !logger_) {
76 warn("GlobalState:initialize: requires FawkesRemoteAdapter or must run in plexil plugin");
77 return false;
78 }
79
80 cfg_default_adapter_ = false;
81 const pugi::xml_node config = getXml();
82 for (const auto &c : config.children()) {
83 if (strcmp(c.name(), "DefaultLookupAdapter") == 0) {
84 cfg_default_adapter_ = true;
85 logger_->log_warn("GlobalState",
86 "Default lookup adapter, allowing on-the-fly state registration");
87 }
88 }
89
90 std::string cfg_prefix;
91 try {
92 std::string cfg_spec = config_->get_string("/plexil/spec");
93 cfg_prefix = "/plexil/" + cfg_spec + "/";
94 } catch (fawkes::Exception &e) {
95 warn("GlobalState:initialize: failed to read config: " << e.what_no_backtrace());
96 return false;
97 }
98 cfg_prefix += "global-states/";
99
100 struct GlobalStateValueConfig
101 {
102 GlobalStateValueConfig() : arity(0)
103 {
104 }
105 std::string name;
106 PLEXIL::ValueType value_type;
107 unsigned int arity;
108 struct StateValue
109 {
110 std::vector<std::string> args;
111 PLEXIL::Value value;
112 };
113 std::map<std::string, StateValue> values;
114 };
115 std::map<std::string, GlobalStateValueConfig> configured_values;
116 std::unique_ptr<Configuration::ValueIterator> cfg_item{config_->search(cfg_prefix)};
117 while (cfg_item->next()) {
118 std::string path = cfg_item->path();
119
120 std::string::size_type start_pos = cfg_prefix.size();
121 std::string::size_type slash_pos = path.find("/", start_pos + 1);
122 if (slash_pos != std::string::npos) {
123 std::string id = path.substr(start_pos, slash_pos - start_pos);
124
125 start_pos = slash_pos + 1;
126 slash_pos = path.find("/", start_pos);
127 std::string what = path.substr(start_pos, slash_pos - start_pos);
128
129 if (what == "name") {
130 configured_values[id].name = cfg_item->get_string();
131 } else if (what == "type") {
132 configured_values[id].value_type = PLEXIL::parseValueType(cfg_item->get_string());
133 } else if (what == "arity") {
134 configured_values[id].arity = cfg_item->get_uint();
135 } else if (what == "values") {
136 start_pos = slash_pos + 1;
137 slash_pos = path.find("/", start_pos);
138 std::string value_id = path.substr(start_pos, slash_pos - start_pos);
139
140 start_pos = slash_pos + 1;
141 slash_pos = path.find("/", start_pos);
142 std::string value_what = path.substr(start_pos, slash_pos - start_pos);
143
144 if (value_what == "args") {
145 configured_values[id].values[value_id].args = cfg_item->get_strings();
146 } // ignore value here, we may not know its type, yet
147 }
148 }
149 }
150
151 for (const auto &c : configured_values) {
152 if (c.second.name.empty()) {
153 warn("GlobalState:initialize: no name for state at index " << c.first);
154 return false;
155 }
156 if (c.second.value_type == PLEXIL::UNKNOWN_TYPE) {
157 warn("GlobalState:initialize: missing or invalid type at index " << c.first);
158 return false;
159 }
160 for (const auto &v : c.second.values) {
161 if (v.second.args.size() != c.second.arity) {
162 warn("GlobalState:initialize: invalid arity value for state " << c.first << " of "
163 << c.second.name);
164 return false;
165 }
166 }
167
168 if (c.second.values.empty()) {
169 PLEXIL::State s(c.second.name);
170 values_[s] = std::make_pair(c.second.value_type, PLEXIL::Value());
171 } else {
172 for (const auto &vc : c.second.values) {
173 PLEXIL::State s(c.second.name, vc.second.args.size());
174 for (size_t i = 0; i < vc.second.args.size(); ++i) {
175 s.setParameter(i, vc.second.args[i]);
176 }
177
178 PLEXIL::Value v;
179 std::string conf_path = cfg_prefix + c.first + "/values/" + vc.first + "/value";
180 if (config_->exists(conf_path)) {
181 switch (c.second.value_type) {
182 case PLEXIL::STRING_TYPE: v = config_->get_string(conf_path); break;
183 case PLEXIL::INTEGER_TYPE: v = config_->get_int(conf_path); break;
184 case PLEXIL::REAL_TYPE: v = config_->get_float(conf_path); break;
185 case PLEXIL::BOOLEAN_TYPE: v = config_->get_bool(conf_path); break;
186 default:
187 warn("GlobalState:initialize: Unsupported value type "
188 << PLEXIL::valueTypeName(c.second.value_type) << " for " << s.toString() << " at "
189 << conf_path);
190 return false;
191 }
192 }
193 values_[s] = std::make_pair(c.second.value_type, v);
194 }
195 }
196 }
197
198 for (const auto &v : values_) {
199 logger_->log_debug("GlobalState",
200 "Registering value %s=%s",
201 v.first.toString().c_str(),
202 v.second.second.valueToString().c_str());
203 PLEXIL::g_configuration->registerLookupInterface(v.first.name(), this);
204 }
205
206 namespace p = std::placeholders;
207 commands_ = {
208 {"global_set_int",
209 std::bind(&GlobalStatePlexilAdapter::global_set_value, this, p::_1, PLEXIL::INTEGER_TYPE)},
210 {"global_set_real",
211 std::bind(&GlobalStatePlexilAdapter::global_set_value, this, p::_1, PLEXIL::REAL_TYPE)},
212 {"global_set_bool",
213 std::bind(&GlobalStatePlexilAdapter::global_set_value, this, p::_1, PLEXIL::BOOLEAN_TYPE)},
214 {"global_set_string",
215 std::bind(&GlobalStatePlexilAdapter::global_set_value, this, p::_1, PLEXIL::STRING_TYPE)},
216 {"global_set_value",
217 std::bind(&GlobalStatePlexilAdapter::global_set_value, this, p::_1, PLEXIL::UNKNOWN_TYPE)},
218 {"global_print_all", std::bind(&GlobalStatePlexilAdapter::global_print_all, this, p::_1)},
219 };
220
221 for (const auto &c : commands_) {
222 PLEXIL::g_configuration->registerCommandInterface(c.first, this);
223 }
224
225 // For some reason having the respective XML tag is not enough by itself
226 if (cfg_default_adapter_) {
227 PLEXIL::g_configuration->defaultRegisterAdapter(this);
228 }
229 return true;
230}
231
232/** Start adapter.
233 * @return true if starting was successful, false otherwise.
234 */
235bool
237{
238 return true;
239}
240
241/** Stop adapter.
242 * @return true if successful, false otherwise.
243 */
244bool
246{
247 return true;
248}
249
250/** Reset adapter.
251 * @return true if successful, false otherwise.
252 */
253bool
255{
256 return true;
257}
258
259/** Shut adapter down.
260 * @return true if successful, false otherwise.
261 */
262bool
264{
265 return true;
266}
267
268/** Perform given command.
269 * @param cmd command to execute
270 */
271void
273{
274 std::string const &name = cmd->getName();
275
276 auto c = commands_.find(name);
277 if (c != commands_.end()) {
278 c->second(cmd);
279 } else {
280 warn("GlobalState:executeCommand: called for unknown"
281 " command "
282 << name);
283 m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
284 m_execInterface.notifyOfExternalEvent();
285 }
286}
287
288/** Abort currently running execution.
289 * @param cmd command to abort
290 */
291void
293{
294 m_execInterface.handleCommandAbortAck(cmd, false);
295 m_execInterface.notifyOfExternalEvent();
296}
297
298/** Immediate lookup of value.
299 * @param state state variable to lookup
300 * @param cache_entry cache entry for retrieved value
301 */
302void
303GlobalStatePlexilAdapter::lookupNow(PLEXIL::State const & state,
304 PLEXIL::StateCacheEntry &cache_entry)
305{
306 if (values_.find(state) == values_.end()) {
307 cache_entry.setUnknown();
308 return;
309 }
310
311 //printf("Returning %s = %s\n", state.toString().c_str(), values_.at(state).second.valueToString().c_str());
312 cache_entry.update(values_.at(state).second);
313}
314
315/** Subscribe to updates for given state.
316 * @param state state variable to subscribe for
317 */
318void
319GlobalStatePlexilAdapter::subscribe(const PLEXIL::State &state)
320{
321 subscribed_states_.insert(state);
322}
323
324/** Unsubscribe from updates.
325 * @param state state variable to unsubscribe from
326 */
327void
328GlobalStatePlexilAdapter::unsubscribe(const PLEXIL::State &state)
329{
330 subscribed_states_.erase(state);
331}
332
333void
334GlobalStatePlexilAdapter::global_set_value(PLEXIL::Command *cmd, PLEXIL::ValueType value_type)
335{
336 std::vector<PLEXIL::Value> const &args = cmd->getArgValues();
337 if (value_type != PLEXIL::UNKNOWN_TYPE) {
338 if (!verify_args(args,
339 "GlobalState:global_set_value",
340 {{"name", PLEXIL::STRING_TYPE}, {"value", value_type}})) {
341 m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
342 m_execInterface.notifyOfExternalEvent();
343 return;
344 }
345 } else {
346 if (args.size() < 2) {
347 warn("GlobalState:global_set_value: Command requires at least 2 arguments, got "
348 << args.size());
349 m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
350 m_execInterface.notifyOfExternalEvent();
351 return;
352 }
353 for (size_t i = 0; i < args.size() - 1; ++i) {
354 if (args[i].valueType() != PLEXIL::STRING_TYPE) {
355 warn("GlobalState:global_set_value: "
356 << " argument " << i << " expected to be of type "
357 << "String, but is of type " << PLEXIL::valueTypeName(args[i].valueType()));
358 m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
359 m_execInterface.notifyOfExternalEvent();
360 return;
361 }
362 }
363 }
364
365 std::string name;
366 args[0].getValue(name);
367
368 // first is name, last is value, everything in between are params
369 size_t num_args = args.size() - 2;
370
371 PLEXIL::State s(name, num_args);
372 for (size_t i = 0; i < args.size() - 2; ++i) {
373 s.setParameter(i, args[i + 1]);
374 }
375
376 if (values_.find(s) == values_.end()) {
377 if (cfg_default_adapter_) {
378 if (args.back().valueType() != PLEXIL::UNKNOWN_TYPE) {
379 logger_->log_debug("GlobalState",
380 "Adding state %s (value type %s)",
381 s.toString().c_str(),
382 PLEXIL::valueTypeName(args.back().valueType()).c_str());
383 values_[s] = std::make_pair(args.back().valueType(), args.back());
384 }
385 } else {
386 warn("GlobalState:global_set_value: called for unknown state " << s.toString()
387 << " and not default adapter");
388 m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
389 m_execInterface.notifyOfExternalEvent();
390 return;
391 }
392 }
393
394 if (args.back().valueType() != values_[s].first) {
395 warn("GlobalState:global_set_value: state "
396 << s.toString() << " is of type " << PLEXIL::valueTypeName(values_[s].first)
397 << ", but called "
398 << "with " << PLEXIL::valueTypeName(args.back().valueType()));
399 m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
400 m_execInterface.notifyOfExternalEvent();
401 return;
402 }
403
404 logger_->log_debug("GlobalState",
405 "Setting %s = %s",
406 s.toString().c_str(),
407 args.back().valueToString().c_str());
408 values_[s].second = args.back();
409
410 if (subscribed_states_.find(s) != subscribed_states_.end()) {
411 m_execInterface.handleValueChange(s, args.back());
412 }
413
414 m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_SUCCESS);
415 m_execInterface.notifyOfExternalEvent();
416}
417
418void
419GlobalStatePlexilAdapter::global_print_all(PLEXIL::Command *cmd)
420{
421 logger_->log_info("GlobalState", "Current globals");
422 for (const auto &v : values_) {
423 logger_->log_info("GlobalState",
424 "%-40s %-10s %s",
425 v.first.toString().c_str(),
426 PLEXIL::valueTypeName(v.second.first).c_str(),
427 v.second.second.valueToString().c_str());
428 }
429 m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_SUCCESS);
430 m_execInterface.notifyOfExternalEvent();
431}
432
433extern "C" {
434void
435initGlobalState()
436{
437 REGISTER_ADAPTER(GlobalStatePlexilAdapter, "GlobalState");
438}
439}
Interface adapter to provide logging facilities.
virtual void executeCommand(PLEXIL::Command *cmd)
Perform given command.
virtual bool start()
Start adapter.
virtual bool initialize()
Initialize adapter.
virtual ~GlobalStatePlexilAdapter()
Destructor.
virtual void subscribe(const PLEXIL::State &state)
Subscribe to updates for given state.
virtual void unsubscribe(const PLEXIL::State &state)
Unsubscribe from updates.
virtual bool stop()
Stop adapter.
virtual bool shutdown()
Shut adapter down.
virtual bool reset()
Reset adapter.
GlobalStatePlexilAdapter(PLEXIL::AdapterExecInterface &execInterface)
Constructor.
virtual void invokeAbort(PLEXIL::Command *cmd)
Abort currently running execution.
virtual void lookupNow(PLEXIL::State const &state, PLEXIL::StateCacheEntry &cache_entry)
Immediate lookup of value.
virtual const char * path() const =0
Path of value.
Interface for configuration handling.
Definition: config.h:68
virtual bool get_bool(const char *path)=0
Get value from configuration which is of type bool.
virtual float get_float(const char *path)=0
Get value from configuration which is of type float.
virtual ValueIterator * search(const char *path)=0
Iterator with search results.
virtual bool exists(const char *path)=0
Check if a given value exists.
virtual std::string get_string(const char *path)=0
Get value from configuration which is of type string.
virtual int get_int(const char *path)=0
Get value from configuration which is of type int.
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 for logging.
Definition: logger.h:42
virtual void log_debug(const char *component, const char *format,...)=0
Log debug message.
virtual void log_warn(const char *component, const char *format,...)=0
Log warning message.
virtual void log_info(const char *component, const char *format,...)=0
Log informational message.
Fawkes library namespace.