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 
34 using 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  */
44 GlobalStatePlexilAdapter::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  */
54 GlobalStatePlexilAdapter::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  */
68 bool
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  */
235 bool
237 {
238  return true;
239 }
240 
241 /** Stop adapter.
242  * @return true if successful, false otherwise.
243  */
244 bool
246 {
247  return true;
248 }
249 
250 /** Reset adapter.
251  * @return true if successful, false otherwise.
252  */
253 bool
255 {
256  return true;
257 }
258 
259 /** Shut adapter down.
260  * @return true if successful, false otherwise.
261  */
262 bool
264 {
265  return true;
266 }
267 
268 /** Perform given command.
269  * @param cmd command to execute
270  */
271 void
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  */
291 void
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  */
302 void
303 GlobalStatePlexilAdapter::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  */
318 void
319 GlobalStatePlexilAdapter::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  */
327 void
328 GlobalStatePlexilAdapter::unsubscribe(const PLEXIL::State &state)
329 {
330  subscribed_states_.erase(state);
331 }
332 
333 void
334 GlobalStatePlexilAdapter::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 
418 void
419 GlobalStatePlexilAdapter::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 
433 extern "C" {
434 void
435 initGlobalState()
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:65
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
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.