Fawkes API  Fawkes Development Version
protobuf_adapter.cpp
1 
2 /***************************************************************************
3  * protobuf_adapter.cpp - PLEXIL adapter for protobuf_comm
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 "protobuf_adapter.h"
23 
24 #include "utils.h"
25 
26 #include <utils/misc/string_split.h>
27 
28 #include <AdapterConfiguration.hh>
29 #include <AdapterExecInterface.hh>
30 #include <AdapterFactory.hh>
31 #include <Command.hh>
32 #include <InterfaceManager.hh>
33 #include <StateCacheEntry.hh>
34 #include <algorithm>
35 #include <chrono>
36 
37 using namespace fawkes;
38 using namespace protobuf_comm;
39 using namespace google::protobuf;
40 using namespace boost::placeholders;
41 
42 /** @class ProtobufCommPlexilAdapter "protobuf_adapter.h"
43  * Plexil adapter to provide access to protobuf_comm.
44  * @author Tim Niemueller
45  */
46 
47 /** Constructor.
48  * @param execInterface Reference to the parent AdapterExecInterface object.
49  */
50 ProtobufCommPlexilAdapter::ProtobufCommPlexilAdapter(PLEXIL::AdapterExecInterface &execInterface)
51 : InterfaceAdapter(execInterface)
52 {
53 }
54 
55 /** Constructor from configuration XML.
56  * @param execInterface Reference to the parent AdapterExecInterface object.
57  * @param xml A const reference to the XML element describing this adapter
58  * @note The instance maintains a shared pointer to the XML.
59  */
60 ProtobufCommPlexilAdapter::ProtobufCommPlexilAdapter(PLEXIL::AdapterExecInterface &execInterface,
61  pugi::xml_node const xml)
62 : InterfaceAdapter(execInterface, xml)
63 {
64 }
65 
66 /** Destructor. */
68 {
69 }
70 
71 /** Initialize adapter.
72  * @return true if initialization was successful, false otherwise.
73  */
74 bool
76 {
77  logger_ = reinterpret_cast<fawkes::Logger *>(m_execInterface.getProperty("::Fawkes::Logger"));
78  config_ =
79  reinterpret_cast<fawkes::Configuration *>(m_execInterface.getProperty("::Fawkes::Config"));
80  clock_ = reinterpret_cast<fawkes::Clock *>(m_execInterface.getProperty("::Fawkes::Clock"));
81 
82  std::string cfg_proto_dir = get_xml_config_value(getXml(), "protos");
83  replace_tokens(cfg_proto_dir);
84 
85  std::vector<std::string> cfg_proto_dirs{cfg_proto_dir};
86  logger_->log_info("PlexilProtobuf", "Protobuf message spec dir: %s", cfg_proto_dirs[0].c_str());
87 
88  msg_counter_ = 0;
89  next_client_id_ = 0;
90 
91  message_register_ = std::make_shared<MessageRegister>(cfg_proto_dirs);
92 
93  namespace p = std::placeholders;
94  commands_ = {
95  {"ReceiveCommand", std::bind(&ProtobufCommPlexilAdapter::proc_receive_command, this, p::_1)},
96  {"GetParameter", std::bind(&ProtobufCommPlexilAdapter::proc_get_param_command, this, p::_1)},
97  {"SendReturnValue", std::bind(&ProtobufCommPlexilAdapter::proc_send_rv_command, this, p::_1)},
98  {"pb_create", std::bind(&ProtobufCommPlexilAdapter::pb_create, this, p::_1)},
99  {"pb_destroy", std::bind(&ProtobufCommPlexilAdapter::pb_destroy, this, p::_1)},
100  {"pb_set_value", std::bind(&ProtobufCommPlexilAdapter::pb_set_value, this, p::_1)},
101  {"pb_get_int",
102  std::bind(&ProtobufCommPlexilAdapter::pb_get_value, this, p::_1, PLEXIL::INTEGER_TYPE)},
103  {"pb_get_real",
104  std::bind(&ProtobufCommPlexilAdapter::pb_get_value, this, p::_1, PLEXIL::REAL_TYPE)},
105  {"pb_get_bool",
106  std::bind(&ProtobufCommPlexilAdapter::pb_get_value, this, p::_1, PLEXIL::BOOLEAN_TYPE)},
107  {"pb_get_string",
108  std::bind(&ProtobufCommPlexilAdapter::pb_get_value, this, p::_1, PLEXIL::STRING_TYPE)},
109  {"pb_get_length", std::bind(&ProtobufCommPlexilAdapter::pb_get_length, this, p::_1)},
110  {"pb_has_field", std::bind(&ProtobufCommPlexilAdapter::pb_has_field, this, p::_1)},
111  {"pb_broadcast", std::bind(&ProtobufCommPlexilAdapter::pb_broadcast, this, p::_1)},
112  {"pb_tostring", std::bind(&ProtobufCommPlexilAdapter::pb_tostring, this, p::_1)},
113  {"pb_peer_create", std::bind(&ProtobufCommPlexilAdapter::pb_peer_create, this, p::_1)},
114  {"pb_peer_destroy", std::bind(&ProtobufCommPlexilAdapter::pb_get_length, this, p::_1)},
115  {"pb_peer_create_local",
116  std::bind(&ProtobufCommPlexilAdapter::pb_peer_create_local, this, p::_1)},
117  {"pb_peer_create_crypto",
118  std::bind(&ProtobufCommPlexilAdapter::pb_peer_create_crypto, this, p::_1)},
119  {"pb_peer_create_local_crypto",
120  std::bind(&ProtobufCommPlexilAdapter::pb_peer_create_local_crypto, this, p::_1, nullptr)},
121  {"pb_peer_setup_crypto",
122  std::bind(&ProtobufCommPlexilAdapter::pb_peer_setup_crypto, this, p::_1)},
123  };
124 
125  for (const auto &c : commands_) {
126  PLEXIL::g_configuration->registerCommandInterface(c.first, this);
127  }
128 
129  return true;
130 }
131 
132 /** Start adapter.
133  * @return true if starting was successful, false otherwise.
134  */
135 bool
137 {
138  return true;
139 }
140 
141 /** Stop adapter.
142  * @return true if successful, false otherwise.
143  */
144 bool
146 {
147  std::lock_guard<std::mutex> lock(queue_mutex_);
148  peers_.clear();
149  queue_.clear();
150  messages_.clear();
151  return true;
152 }
153 
154 /** Reset adapter.
155  * @return true if successful, false otherwise.
156  */
157 bool
159 {
160  return true;
161 }
162 
163 /** Shut adapter down.
164  * @return true if successful, false otherwise.
165  */
166 bool
168 {
169  return true;
170 }
171 
172 /** Perform given command.
173  * @param cmd command to execute
174  */
175 void
177 {
178  std::string const &name = cmd->getName();
179 
180  //logger_->log_info("PlexilProtobuf", "Processing %s", name.c_str());
181 
182  auto c = commands_.find(name);
183  if (c != commands_.end()) {
184  c->second(cmd);
185  } else {
186  warn("ProtobufCommAdapter:executeCommand: called for unknown"
187  " command "
188  << name);
189  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
190  m_execInterface.notifyOfExternalEvent();
191  }
192 }
193 
194 void
195 ProtobufCommPlexilAdapter::proc_receive_command(PLEXIL::Command *cmd)
196 {
197  std::vector<PLEXIL::Value> const &args = cmd->getArgValues();
198  if (args.size() != 1) {
199  warn("ProtobufCommAdapter: The ReceiveCommand"
200  " command requires exactly one argument");
201  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
202  m_execInterface.notifyOfExternalEvent();
203  return;
204  }
205 
206  if (args.front().valueType() != PLEXIL::STRING_TYPE) {
207  warn("ProtobufCommAdapter: The argument to the ReceiveCommand"
208  " command, "
209  << args.front() << ", is not a string");
210  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
211  m_execInterface.notifyOfExternalEvent();
212  return;
213  }
214 
215  std::string msg_type;
216  if (!args.front().getValue(msg_type)) {
217  warn("ProtobufCommAdapter: message type is unknown");
218  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
219  m_execInterface.notifyOfExternalEvent();
220  return;
221  }
222 
223  add_recipient(msg_type, cmd);
224  proc_queue(msg_type);
225 
226  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_SENT_TO_SYSTEM);
227  m_execInterface.notifyOfExternalEvent();
228 }
229 
230 void
231 ProtobufCommPlexilAdapter::proc_get_param_command(PLEXIL::Command *cmd)
232 {
233  std::vector<PLEXIL::Value> const &args = cmd->getArgValues();
234  if (args.size() < 1 || args.size() > 2) {
235  warn("ProtobufCommAdapter:GetParameter: "
236  "Command requires either one or two arguments");
237  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
238  m_execInterface.notifyOfExternalEvent();
239  return;
240  }
241 
242  if (args.front().valueType() != PLEXIL::STRING_TYPE) {
243  warn("ProtobufCommAdapter:GetParameter: first argument "
244  " '"
245  << args.front() << "' is not a string");
246  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
247  m_execInterface.notifyOfExternalEvent();
248  return;
249  }
250 
251  std::string msg_id;
252  if (!args.front().getValue(msg_id)) {
253  warn("ProtobufCommAdapter:GetParameter: message ID is unknown");
254  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
255  m_execInterface.notifyOfExternalEvent();
256  return;
257  }
258 
259  std::lock_guard<std::mutex> lock(queue_mutex_);
260 
261  if (messages_.find(msg_id) == messages_.end()) {
262  warn("ProtobufCommAdapter:GetParameter: message ID not in queued messages");
263  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
264  m_execInterface.notifyOfExternalEvent();
265  return;
266  }
267 
268  std::vector<PLEXIL::Value>::const_iterator it = ++args.begin();
269  int32_t id = 0;
270  if (it != args.end()) {
271  if (it->valueType() != PLEXIL::INTEGER_TYPE) {
272  warn("ProtobufCommAdapter:GetParameter: second argument "
273  " '"
274  << *it << "' is not an integer");
275  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
276  m_execInterface.notifyOfExternalEvent();
277  return;
278  }
279 
280  if (!it->getValue(id)) {
281  warn("ProtobufCommAdapter:GetParameter: second argument is unknown");
282  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
283  m_execInterface.notifyOfExternalEvent();
284  return;
285  }
286 
287  if (id < 0 || id > 3) {
288  warn("ProtobufCommAdapter:GetParameter: second argument "
289  " '"
290  << *it << "' is not a valid index (must be 0..3)");
291  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
292  m_execInterface.notifyOfExternalEvent();
293  return;
294  }
295  }
296 
297  const message_meta &msgmeta = messages_[msg_id];
298 
299  switch (id) {
300  case 0: m_execInterface.handleCommandReturn(cmd, PLEXIL::Value(msg_id)); break;
301  case 1: m_execInterface.handleCommandReturn(cmd, PLEXIL::Value(msgmeta.from_host)); break;
302  case 2: m_execInterface.handleCommandReturn(cmd, PLEXIL::Value(msgmeta.from_port)); break;
303  case 3:
304  m_execInterface.handleCommandReturn(cmd, PLEXIL::Value(msgmeta.time_received.in_sec()));
305  break;
306  }
307 
308  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_SUCCESS);
309  m_execInterface.notifyOfExternalEvent();
310 }
311 
312 void
313 ProtobufCommPlexilAdapter::proc_send_rv_command(PLEXIL::Command *cmd)
314 {
315  std::vector<PLEXIL::Value> const &args = cmd->getArgValues();
316  if (args.size() == 0) {
317  warn("ProtobufCommAdapter:SendReturnValue: The SendReturnValue"
318  " command requires at least one argument");
319  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
320  m_execInterface.notifyOfExternalEvent();
321  return;
322  }
323 
324  if (args.front().valueType() != PLEXIL::STRING_TYPE) {
325  warn("ProtobufCommAdapter:SendReturnValue: The argument to the "
326  "SendReturnValue command, "
327  << args.front() << ", is not a string");
328  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
329  m_execInterface.notifyOfExternalEvent();
330  return;
331  }
332 
333  std::string msg_id;
334  if (!args.front().getValue(msg_id)) {
335  warn("ProtobufCommAdapter:SendReturnValue: message ID is unknown");
336  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
337  m_execInterface.notifyOfExternalEvent();
338  return;
339  }
340 
341  release_message(msg_id);
342 
343  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_SUCCESS);
344  m_execInterface.notifyOfExternalEvent();
345 }
346 
347 /** Abort currently running execution.
348  * @param cmd command to abort
349  */
350 void
352 {
353  m_execInterface.handleCommandAbortAck(cmd, false);
354  m_execInterface.notifyOfExternalEvent();
355 }
356 
357 void
358 ProtobufCommPlexilAdapter::add_recipient(const std::string &msg_type, PLEXIL::Command *cmd)
359 {
360  std::lock_guard<std::mutex> lock(queue_mutex_);
361  queue_entry & q = get_queue(msg_type);
362  q.recipients.push_back(cmd);
363 }
364 
365 void
366 ProtobufCommPlexilAdapter::remove_recipient(const std::string &msg_type, PLEXIL::Command *cmd)
367 {
368  std::lock_guard<std::mutex> lock(queue_mutex_);
369  queue_entry & q = get_queue(msg_type);
370  q.recipients.erase(std::remove(q.recipients.begin(), q.recipients.end(), cmd),
371  q.recipients.end());
372 }
373 
374 void
375 ProtobufCommPlexilAdapter::add_message(const std::string &msg_type, message_meta &&msg)
376 {
377  std::lock_guard<std::mutex> lock(queue_mutex_);
378  queue_entry & q = get_queue(msg_type);
379  std::string msg_id = gen_msgid(msg_type);
380 
381  messages_[msg_id] = std::move(msg);
382 
383  q.messages.push_back(msg_id);
384 }
385 
386 void
387 ProtobufCommPlexilAdapter::release_message(const std::string &msg_id)
388 {
389  std::lock_guard<std::mutex> lock(queue_mutex_);
390 
391  std::string::size_type colon_pos = msg_id.find(':');
392  if (colon_pos != std::string::npos) {
393  std::string msg_type{msg_id.substr(0, colon_pos)};
394  queue_entry &q = get_queue(msg_type);
395 
396  q.messages.erase(std::remove(q.messages.begin(), q.messages.end(), msg_id), q.messages.end());
397  messages_.erase(msg_id);
398  }
399 }
400 
401 std::shared_ptr<google::protobuf::Message>
402 ProtobufCommPlexilAdapter::get_message(const std::string &msg_id)
403 {
404  std::shared_ptr<google::protobuf::Message> m;
405 
406  std::lock_guard<std::mutex> lock(queue_mutex_);
407 
408  if (messages_.find(msg_id) != messages_.end()) {
409  m = messages_[msg_id].message;
410  }
411  return m;
412 }
413 
414 std::string
415 ProtobufCommPlexilAdapter::gen_msgid(const std::string &msg_type)
416 {
417  unsigned long int id = ++msg_counter_;
418  return msg_type + ":" + std::to_string(id);
419 }
420 
421 void
422 ProtobufCommPlexilAdapter::proc_queue(const std::string &msg_type)
423 {
424  std::lock_guard<std::mutex> lock(queue_mutex_);
425  queue_entry & q = get_queue(msg_type);
426  auto mi = q.messages.begin();
427  auto ri = q.recipients.begin();
428  bool notify = !q.messages.empty() && !q.recipients.empty();
429  while (mi != q.messages.end() && ri != q.recipients.end()) {
430  m_execInterface.handleCommandReturn(*ri, *mi);
431  ri = q.recipients.erase(ri);
432  mi = q.messages.erase(mi);
433  }
434  if (notify) {
435  m_execInterface.notifyOfExternalEvent();
436  }
437 }
438 
439 ProtobufCommPlexilAdapter::queue_entry &
440 ProtobufCommPlexilAdapter::get_queue(const std::string &msg_type)
441 {
442  auto q = queue_.find(msg_type);
443  if (q != queue_.end()) {
444  return q->second;
445  } else {
446  auto new_q = queue_.insert(std::make_pair(msg_type, queue_entry()));
447  return new_q.first->second;
448  }
449 }
450 
451 /** Create Protobuf message.
452  */
453 void
454 ProtobufCommPlexilAdapter::pb_create(PLEXIL::Command *cmd)
455 {
456  std::vector<PLEXIL::Value> const &args = cmd->getArgValues();
457  if (!verify_args(args, "ProtobufCommAdapter:pb_create", {{"msg_type", PLEXIL::STRING_TYPE}})) {
458  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
459  m_execInterface.notifyOfExternalEvent();
460  return;
461  }
462 
463  std::string msg_type;
464  args[0].getValue(msg_type);
465 
466  std::lock_guard<std::mutex> lock(queue_mutex_);
467  std::string msg_id = gen_msgid(msg_type);
468 
469  try {
470  std::shared_ptr<google::protobuf::Message> m = message_register_->new_message_for(msg_type);
471 
472  message_meta msgmeta{.time_received = fawkes::Time(clock_),
473  .from_host = "",
474  .from_port = 0,
475  .message = std::shared_ptr<google::protobuf::Message>(m)};
476 
477  messages_[msg_id] = std::move(msgmeta);
478 
479  m_execInterface.handleCommandReturn(cmd, PLEXIL::Value(msg_id));
480  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_SUCCESS);
481  m_execInterface.notifyOfExternalEvent();
482  } catch (std::runtime_error &e) {
483  logger_->log_warn("PlexilProtobuf",
484  "Cannot create message of type %s: %s",
485  msg_type.c_str(),
486  e.what());
487  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
488  m_execInterface.notifyOfExternalEvent();
489  }
490 }
491 
492 /** Destroy Protobuf message.
493  */
494 void
495 ProtobufCommPlexilAdapter::pb_destroy(PLEXIL::Command *cmd)
496 {
497  std::vector<PLEXIL::Value> const &args = cmd->getArgValues();
498  if (!verify_args(args, "ProtobufCommAdapter:pb_destroy", {{"msg_id", PLEXIL::STRING_TYPE}})) {
499  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
500  m_execInterface.notifyOfExternalEvent();
501  return;
502  }
503 
504  std::string msg_id;
505  args[0].getValue(msg_id);
506 
507  std::lock_guard<std::mutex> lock(queue_mutex_);
508  messages_.erase(msg_id);
509 }
510 
511 static std::pair<std::string, long int>
512 parse_field_name(const std::string &field_name, const std::string &func)
513 {
514  std::string field;
515  long int index = -1;
516 
517  std::string::size_type opening = field_name.find('[');
518  if (opening != std::string::npos) {
519  std::string::size_type closing = field_name.find(']', opening);
520  if (closing != std::string::npos) {
521  field = field_name.substr(0, opening);
522  std::string idx_str = field_name.substr(opening + 1, closing - opening - 1);
523  if (idx_str.empty()) {
524  index = std::numeric_limits<long int>::max();
525  } else {
526  index = std::stol(idx_str);
527  }
528  } else {
529  warn("ProtobufCommAdapter:" << func << ":"
530  << " Missing closing bracket in " << field_name);
531  }
532  } else {
533  field = field_name;
534  }
535  return std::make_pair(field, index);
536 }
537 
538 static bool
539 traverse_field(google::protobuf::Message *&msg,
540  const std::string & field_name,
541  const FieldDescriptor *& field,
542  std::string & partial_name,
543  long int & partial_index,
544  const std::string & func)
545 
546 {
547  std::vector<std::string> field_path = str_split(field_name, '.');
548  for (size_t i = 0; i < field_path.size(); ++i) {
549  std::pair<std::string, long int> parsed_field = parse_field_name(field_path[i], func);
550  partial_name = parsed_field.first;
551  partial_index = parsed_field.second;
552 
553  if (partial_name.empty()) {
554  warn("ProtobufCommAdapter:" << func << ":"
555  << " Invalid sub-field " << field_path[i]);
556  return false;
557  }
558 
559  const Descriptor *desc = msg->GetDescriptor();
560  field = desc->FindFieldByName(partial_name);
561  if (!field) {
562  warn("ProtobufCommAdapter:" << func << ":"
563  << " Field " << partial_name << " not found");
564  return false;
565  }
566 
567  if (partial_index >= 0 && !field->is_repeated()) {
568  warn("ProtobufCommAdapter:" << func << ":"
569  << " Index for non-repeated field " << partial_name);
570  return false;
571 
572  } else if (partial_index < 0 && field->is_repeated()) {
573  warn("ProtobufCommAdapter:" << func << ":"
574  << " No index for repeated field " << partial_name);
575  return false;
576  }
577 
578  const Reflection *refl = msg->GetReflection();
579 
580  // special case: if someone tries to access the array just one
581  // behind the last boundary, treat this as adding an element.
582  // this makes consecutive accesses to the same just added value
583  // more natural, e.g., my_field[0].value1 and then my_field[0].value2.
584  if (field->is_repeated() && partial_index >= 0
585  && partial_index == refl->FieldSize(*msg, field)) {
586  partial_index = std::numeric_limits<long int>::max();
587  }
588 
589  if (partial_index >= 0 && partial_index < std::numeric_limits<long int>::max()
590  && partial_index >= refl->FieldSize(*msg, field)) {
591  warn("ProtobufCommAdapter:" << func << ":"
592  << " Index " << partial_index << " out of bounds for "
593  << partial_name);
594  return false;
595  }
596 
597  if (i < (field_path.size() - 1)) {
598  if (field->type() != FieldDescriptor::TYPE_MESSAGE) {
599  warn("ProtobufCommAdapter:" << func << ":"
600  << " Sub-field " << field_path[i] << " is not a message");
601  return false;
602  }
603 
604  if (field->is_repeated()) {
605  if (partial_index == std::numeric_limits<long int>::max()) {
606  msg = refl->AddMessage(msg, field);
607  } else {
608  // out of bounds check already done above
609  msg = refl->MutableRepeatedMessage(msg, field, partial_index);
610  }
611  } else {
612  msg = refl->MutableMessage(msg, field);
613  }
614  }
615  }
616  return true;
617 }
618 
619 void
620 ProtobufCommPlexilAdapter::pb_set_value(PLEXIL::Command *cmd)
621 {
622  std::vector<PLEXIL::Value> const &args = cmd->getArgValues();
623  if (!verify_args(args,
624  "ProtobufCommAdapter:pb_set_value",
625  {{"msg_id", PLEXIL::STRING_TYPE},
626  {"field", PLEXIL::STRING_TYPE},
627  {"value", PLEXIL::UNKNOWN_TYPE}})) {
628  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
629  m_execInterface.notifyOfExternalEvent();
630  return;
631  }
632 
633  std::string msg_id;
634  std::string field_name;
635  PLEXIL::Value value;
636  args[0].getValue(msg_id);
637  args[1].getValue(field_name);
638  value = args[2];
639 
640  std::shared_ptr<google::protobuf::Message> m = get_message(msg_id);
641  if (!m) {
642  warn("ProtobufCommAdapter:pb_set_value:"
643  << " Unknown message " << msg_id);
644  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
645  m_execInterface.notifyOfExternalEvent();
646  return;
647  }
648 
649  const FieldDescriptor * field = nullptr;
650  google::protobuf::Message *msg = m.get();
651 
652  std::string partial_name;
653  long int partial_index = -1;
654 
655  if (!traverse_field(msg, field_name, field, partial_name, partial_index, "pb_set_value")) {
656  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
657  m_execInterface.notifyOfExternalEvent();
658  return;
659  }
660 
661  if (!field) {
662  warn("ProtobufCommAdapter:pb_set_value:"
663  << " Unknown field " << field_name << " for message " << msg_id);
664  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
665  m_execInterface.notifyOfExternalEvent();
666  return;
667  }
668 
669  if (field->type() == FieldDescriptor::TYPE_MESSAGE) {
670  warn("ProtobufCommAdapter:pb_set_value:"
671  << " Final sub-field " << field_name << " is a message");
672  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
673  m_execInterface.notifyOfExternalEvent();
674  return;
675  }
676 
677  const Reflection *refl = msg->GetReflection();
678 
679  bool add_repeated = false;
680  if (field->is_repeated() && partial_index == std::numeric_limits<long int>::max()) {
681  add_repeated = true;
682  }
683 
684  try {
685  switch (field->type()) {
686  case FieldDescriptor::TYPE_DOUBLE:
687  if (value.valueType() != PLEXIL::REAL_TYPE) {
688  warn("ProtobufCommAdapter:pb_set_value:"
689  "Invalid type for field "
690  << field_name << ", expects Real, got " << PLEXIL::valueTypeName(value.valueType()));
691  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
692  m_execInterface.notifyOfExternalEvent();
693  return;
694  } else {
695  double v;
696  value.getValue(v);
697  if (field->is_repeated()) {
698  if (add_repeated) {
699  refl->AddDouble(msg, field, v);
700  } else {
701  refl->SetRepeatedDouble(msg, field, partial_index, v);
702  }
703  } else {
704  refl->SetDouble(msg, field, v);
705  }
706  }
707  break;
708 
709  case FieldDescriptor::TYPE_FLOAT:
710  if (value.valueType() != PLEXIL::REAL_TYPE) {
711  warn("ProtobufCommAdapter:pb_set_value:"
712  "Invalid type for field "
713  << field_name << ", expects Real, got " << PLEXIL::valueTypeName(value.valueType()));
714  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
715  m_execInterface.notifyOfExternalEvent();
716  return;
717  } else {
718  double v;
719  value.getValue(v);
720  if (field->is_repeated()) {
721  if (add_repeated) {
722  refl->AddFloat(msg, field, v);
723  } else {
724  refl->SetRepeatedFloat(msg, field, partial_index, v);
725  }
726  } else {
727  refl->SetFloat(msg, field, v);
728  }
729  }
730  break;
731 
732  case FieldDescriptor::TYPE_SFIXED64:
733  case FieldDescriptor::TYPE_SINT64:
734  case FieldDescriptor::TYPE_INT64:
735  if (value.valueType() != PLEXIL::INTEGER_TYPE) {
736  warn("ProtobufCommAdapter:pb_set_value:"
737  "Invalid type for field "
738  << field_name << ", expects Integer, got "
739  << PLEXIL::valueTypeName(value.valueType()));
740  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
741  m_execInterface.notifyOfExternalEvent();
742  return;
743  } else {
744  int v;
745  value.getValue(v);
746  refl->SetInt64(msg, field, v);
747  }
748  break;
749 
750  case FieldDescriptor::TYPE_FIXED64:
751  case FieldDescriptor::TYPE_UINT64:
752  if (value.valueType() != PLEXIL::INTEGER_TYPE) {
753  warn("ProtobufCommAdapter:pb_set_value:"
754  "Invalid type for field "
755  << field_name << ", expects Integer, got "
756  << PLEXIL::valueTypeName(value.valueType()));
757  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
758  m_execInterface.notifyOfExternalEvent();
759  return;
760  } else {
761  int v;
762  value.getValue(v);
763  if (field->is_repeated()) {
764  if (add_repeated) {
765  refl->AddUInt64(msg, field, v);
766  } else {
767  refl->SetRepeatedUInt64(msg, field, partial_index, v);
768  }
769  } else {
770  refl->SetUInt64(msg, field, v);
771  }
772  }
773  break;
774 
775  case FieldDescriptor::TYPE_SFIXED32:
776  case FieldDescriptor::TYPE_SINT32:
777  case FieldDescriptor::TYPE_INT32:
778  if (value.valueType() != PLEXIL::INTEGER_TYPE) {
779  warn("ProtobufCommAdapter:pb_set_value:"
780  "Invalid type for field "
781  << field_name << ", expects Integer, got "
782  << PLEXIL::valueTypeName(value.valueType()));
783  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
784  m_execInterface.notifyOfExternalEvent();
785  return;
786  } else {
787  int v;
788  value.getValue(v);
789  if (field->is_repeated()) {
790  if (add_repeated) {
791  refl->AddInt32(msg, field, v);
792  } else {
793  refl->SetRepeatedInt32(msg, field, partial_index, v);
794  }
795  } else {
796  refl->SetInt32(msg, field, v);
797  }
798  }
799  break;
800 
801  case FieldDescriptor::TYPE_BOOL:
802  if (value.valueType() != PLEXIL::BOOLEAN_TYPE) {
803  warn("ProtobufCommAdapter:pb_set_value:"
804  "Invalid type for field "
805  << field_name << ", expects Boolean, got "
806  << PLEXIL::valueTypeName(value.valueType()));
807  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
808  m_execInterface.notifyOfExternalEvent();
809  return;
810  } else {
811  bool v;
812  value.getValue(v);
813  if (field->is_repeated()) {
814  if (add_repeated) {
815  refl->AddBool(msg, field, v);
816  } else {
817  refl->SetRepeatedBool(msg, field, partial_index, v);
818  }
819  } else {
820  refl->SetBool(msg, field, v);
821  }
822  }
823  break;
824 
825  case FieldDescriptor::TYPE_STRING:
826  if (value.valueType() != PLEXIL::STRING_TYPE) {
827  warn("ProtobufCommAdapter:pb_set_value:"
828  "Invalid type for field "
829  << field_name << ", expects String, got " << PLEXIL::valueTypeName(value.valueType()));
830  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
831  m_execInterface.notifyOfExternalEvent();
832  return;
833  } else {
834  std::string v;
835  value.getValue(v);
836  if (field->is_repeated()) {
837  if (add_repeated) {
838  refl->AddString(msg, field, v);
839  } else {
840  refl->SetRepeatedString(msg, field, partial_index, v);
841  }
842  } else {
843  refl->SetString(msg, field, v);
844  }
845  }
846  break;
847 
848  case FieldDescriptor::TYPE_MESSAGE:
849  // does not occur with dotted access
850  return;
851 
852  case FieldDescriptor::TYPE_BYTES:
853  warn("ProtobufCommAdapter:pb_set_value: cannot set byte field.");
854  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
855  m_execInterface.notifyOfExternalEvent();
856  break;
857 
858  case FieldDescriptor::TYPE_FIXED32:
859  case FieldDescriptor::TYPE_UINT32:
860  if (value.valueType() != PLEXIL::INTEGER_TYPE) {
861  warn("ProtobufCommAdapter:pb_set_value:"
862  "Invalid type for field "
863  << field_name << ", expects Integer, got "
864  << PLEXIL::valueTypeName(value.valueType()));
865  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
866  m_execInterface.notifyOfExternalEvent();
867  return;
868  } else {
869  int v;
870  value.getValue(v);
871  if (field->is_repeated()) {
872  if (add_repeated) {
873  refl->AddUInt32(msg, field, v);
874  } else {
875  refl->SetRepeatedUInt32(msg, field, partial_index, v);
876  }
877  } else {
878  refl->SetUInt32(msg, field, v);
879  }
880  }
881  break;
882 
883  case FieldDescriptor::TYPE_ENUM:
884  if (value.valueType() != PLEXIL::STRING_TYPE) {
885  warn("ProtobufCommAdapter:pb_set_value:"
886  "Invalid type for field "
887  << field_name << ", expects String, got " << PLEXIL::valueTypeName(value.valueType()));
888  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
889  m_execInterface.notifyOfExternalEvent();
890  return;
891  } else {
892  std::string v;
893  value.getValue(v);
894 
895  const EnumDescriptor * enumdesc = field->enum_type();
896  const EnumValueDescriptor *enumval = enumdesc->FindValueByName(v);
897  if (enumval) {
898  if (field->is_repeated()) {
899  if (add_repeated) {
900  refl->AddEnum(msg, field, enumval);
901  } else {
902  refl->SetRepeatedEnum(msg, field, partial_index, enumval);
903  }
904  } else {
905  refl->SetEnum(msg, field, enumval);
906  }
907  } else {
908  warn("ProtobufCommAdapter:pb_set_value:"
909  "Invalid enum value '"
910  << v << "' for field " << field_name);
911  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
912  m_execInterface.notifyOfExternalEvent();
913  return;
914  }
915  }
916  break;
917  default: break;
918  }
919  } catch (std::logic_error &e) {
920  warn("ProtobufCommAdapter:pb_set_value:"
921  "Failed to set field "
922  << field_name << ": " << e.what());
923  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
924  m_execInterface.notifyOfExternalEvent();
925  }
926 
927  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_SUCCESS);
928  m_execInterface.notifyOfExternalEvent();
929 }
930 
931 void
932 ProtobufCommPlexilAdapter::pb_get_value(PLEXIL::Command *cmd, PLEXIL::ValueType value_type)
933 {
934  std::vector<PLEXIL::Value> const &args = cmd->getArgValues();
935  if (!verify_args(args,
936  "ProtobufCommAdapter:pb_get_value",
937  {{"msg_id", PLEXIL::STRING_TYPE}, {"field", PLEXIL::STRING_TYPE}})) {
938  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
939  m_execInterface.notifyOfExternalEvent();
940  return;
941  }
942 
943  std::string msg_id;
944  std::string field_name;
945  args[0].getValue(msg_id);
946  args[1].getValue(field_name);
947 
948  std::shared_ptr<google::protobuf::Message> m = get_message(msg_id);
949  if (!m) {
950  warn("ProtobufCommAdapter:pb_get_value:"
951  << " Unknown message " << msg_id);
952  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
953  m_execInterface.notifyOfExternalEvent();
954  return;
955  }
956 
957  const FieldDescriptor * field = nullptr;
958  google::protobuf::Message *msg = m.get();
959 
960  std::string partial_name;
961  long int partial_index = -1;
962 
963  if (!traverse_field(msg, field_name, field, partial_name, partial_index, "pb_get_value")) {
964  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
965  m_execInterface.notifyOfExternalEvent();
966  return;
967  }
968 
969  if (!field) {
970  warn("ProtobufCommAdapter:pb_get_value:"
971  << " Unknown field " << field_name << " for message " << msg_id);
972  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
973  m_execInterface.notifyOfExternalEvent();
974  return;
975  }
976 
977  if (field->type() == FieldDescriptor::TYPE_MESSAGE) {
978  warn("ProtobufCommAdapter:pb_get_value:"
979  << " Final sub-field " << field_name << " is a message");
980  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
981  m_execInterface.notifyOfExternalEvent();
982  return;
983  }
984 
985  const Reflection *refl = msg->GetReflection();
986 
987  // check return value
988  switch (field->type()) {
989  case FieldDescriptor::TYPE_DOUBLE:
990  case FieldDescriptor::TYPE_FLOAT:
991  if (value_type != PLEXIL::REAL_TYPE) {
992  warn("ProtobufCommAdapter:pb_get_value:" << m->GetTypeName() << ":" << field_name
993  << " not of expected type Real");
994  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
995  m_execInterface.notifyOfExternalEvent();
996  return;
997  }
998  break;
999 
1000  case FieldDescriptor::TYPE_INT64:
1001  case FieldDescriptor::TYPE_SFIXED64:
1002  case FieldDescriptor::TYPE_SINT64:
1003  case FieldDescriptor::TYPE_UINT64:
1004  case FieldDescriptor::TYPE_FIXED64:
1005  case FieldDescriptor::TYPE_INT32:
1006  case FieldDescriptor::TYPE_SFIXED32:
1007  case FieldDescriptor::TYPE_SINT32:
1008  case FieldDescriptor::TYPE_UINT32:
1009  case FieldDescriptor::TYPE_FIXED32:
1010  if (value_type != PLEXIL::INTEGER_TYPE) {
1011  warn("ProtobufCommAdapter:pb_get_value:" << m->GetTypeName() << ":" << field_name
1012  << " not of expected type Integer");
1013  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
1014  m_execInterface.notifyOfExternalEvent();
1015  return;
1016  }
1017  break;
1018 
1019  case FieldDescriptor::TYPE_BOOL:
1020  if (value_type != PLEXIL::BOOLEAN_TYPE) {
1021  warn("ProtobufCommAdapter:pb_get_value:" << m->GetTypeName() << ":" << field_name
1022  << " not of expected type Boolean");
1023  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
1024  m_execInterface.notifyOfExternalEvent();
1025  return;
1026  }
1027  break;
1028 
1029  case FieldDescriptor::TYPE_STRING:
1030  case FieldDescriptor::TYPE_ENUM:
1031  if (value_type != PLEXIL::STRING_TYPE) {
1032  warn("ProtobufCommAdapter:pb_get_value:" << m->GetTypeName() << ":" << field_name
1033  << " not of expected type String");
1034  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
1035  m_execInterface.notifyOfExternalEvent();
1036  return;
1037  }
1038  break;
1039 
1040  default:
1041  if (value_type != PLEXIL::STRING_TYPE) {
1042  warn("ProtobufCommAdapter:pb_get_value:" << m->GetTypeName() << ":" << field_name
1043  << " of unsupported protobuf type");
1044  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
1045  m_execInterface.notifyOfExternalEvent();
1046  return;
1047  }
1048  break;
1049  }
1050 
1051  if (field->is_repeated()) {
1052  if (partial_index >= refl->FieldSize(*msg, field)) {
1053  warn("ProtobufCommAdapter:pb_get_value:" << m->GetTypeName() << "Index " << partial_index
1054  << " for " << partial_name << " is out of bounds");
1055  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
1056  m_execInterface.notifyOfExternalEvent();
1057  return;
1058  }
1059 
1060  switch (field->type()) {
1061  case FieldDescriptor::TYPE_DOUBLE:
1062  m_execInterface.handleCommandReturn(cmd,
1063  PLEXIL::Value(
1064  refl->GetRepeatedDouble(*msg, field, partial_index)));
1065  break;
1066  case FieldDescriptor::TYPE_FLOAT:
1067  m_execInterface.handleCommandReturn(cmd,
1068  PLEXIL::Value(
1069  refl->GetRepeatedFloat(*msg, field, partial_index)));
1070  break;
1071  case FieldDescriptor::TYPE_UINT64:
1072  case FieldDescriptor::TYPE_FIXED64:
1073  m_execInterface.handleCommandReturn(
1074  cmd, PLEXIL::Value((PLEXIL::Integer)refl->GetRepeatedUInt64(*msg, field, partial_index)));
1075  break;
1076  case FieldDescriptor::TYPE_UINT32:
1077  case FieldDescriptor::TYPE_FIXED32:
1078  m_execInterface.handleCommandReturn(
1079  cmd, PLEXIL::Value((PLEXIL::Integer)refl->GetRepeatedUInt32(*msg, field, partial_index)));
1080  break;
1081  case FieldDescriptor::TYPE_BOOL:
1082  m_execInterface.handleCommandReturn(
1083  cmd, PLEXIL::Value((PLEXIL::Integer)refl->GetRepeatedBool(*msg, field, partial_index)));
1084  break;
1085  case FieldDescriptor::TYPE_STRING:
1086  m_execInterface.handleCommandReturn(cmd,
1087  PLEXIL::Value(
1088  refl->GetRepeatedString(*msg, field, partial_index)));
1089  break;
1090  case FieldDescriptor::TYPE_ENUM:
1091  m_execInterface.handleCommandReturn(
1092  cmd, PLEXIL::Value(refl->GetRepeatedEnum(*msg, field, partial_index)->name()));
1093  break;
1094  case FieldDescriptor::TYPE_SFIXED32:
1095  case FieldDescriptor::TYPE_INT32:
1096  case FieldDescriptor::TYPE_SINT32:
1097  m_execInterface.handleCommandReturn(
1098  cmd, PLEXIL::Value((PLEXIL::Integer)refl->GetRepeatedInt32(*msg, field, partial_index)));
1099  break;
1100  case FieldDescriptor::TYPE_SFIXED64:
1101  case FieldDescriptor::TYPE_SINT64:
1102  case FieldDescriptor::TYPE_INT64:
1103  m_execInterface.handleCommandReturn(
1104  cmd, PLEXIL::Value((PLEXIL::Integer)refl->GetRepeatedInt64(*msg, field, partial_index)));
1105  break;
1106  default: break;
1107  }
1108 
1109  } else {
1110  if (!refl->HasField(*msg, field)) {
1111  warn("ProtobufCommAdapter:pb_get_value:" << m->GetTypeName() << ":" << field_name
1112  << " not set");
1113  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
1114  m_execInterface.notifyOfExternalEvent();
1115  return;
1116  }
1117 
1118  // Now get the actual value
1119  switch (field->type()) {
1120  case FieldDescriptor::TYPE_DOUBLE:
1121  m_execInterface.handleCommandReturn(cmd, PLEXIL::Value(refl->GetDouble(*msg, field)));
1122  break;
1123  case FieldDescriptor::TYPE_FLOAT:
1124  m_execInterface.handleCommandReturn(cmd, PLEXIL::Value(refl->GetFloat(*msg, field)));
1125  break;
1126  case FieldDescriptor::TYPE_INT64:
1127  case FieldDescriptor::TYPE_SFIXED64:
1128  case FieldDescriptor::TYPE_SINT64:
1129  m_execInterface.handleCommandReturn(cmd,
1130  PLEXIL::Value(
1131  (PLEXIL::Integer)refl->GetInt64(*msg, field)));
1132  break;
1133  case FieldDescriptor::TYPE_UINT64:
1134  case FieldDescriptor::TYPE_FIXED64:
1135  m_execInterface.handleCommandReturn(cmd,
1136  PLEXIL::Value(
1137  (PLEXIL::Integer)refl->GetUInt64(*msg, field)));
1138  break;
1139  case FieldDescriptor::TYPE_INT32:
1140  case FieldDescriptor::TYPE_SFIXED32:
1141  case FieldDescriptor::TYPE_SINT32:
1142  m_execInterface.handleCommandReturn(cmd,
1143  PLEXIL::Value(
1144  (PLEXIL::Integer)refl->GetInt32(*msg, field)));
1145  break;
1146  case FieldDescriptor::TYPE_UINT32:
1147  case FieldDescriptor::TYPE_FIXED32:
1148  m_execInterface.handleCommandReturn(cmd,
1149  PLEXIL::Value(
1150  (PLEXIL::Integer)refl->GetUInt32(*msg, field)));
1151  break;
1152  case FieldDescriptor::TYPE_BOOL:
1153  m_execInterface.handleCommandReturn(cmd, PLEXIL::Value(refl->GetBool(*msg, field)));
1154  break;
1155  case FieldDescriptor::TYPE_STRING:
1156  m_execInterface.handleCommandReturn(cmd, PLEXIL::Value(refl->GetString(*msg, field)));
1157  break;
1158 
1159  case FieldDescriptor::TYPE_ENUM:
1160  m_execInterface.handleCommandReturn(cmd, PLEXIL::Value(refl->GetEnum(*msg, field)->name()));
1161  break;
1162 
1163  default:
1164  warn("ProtobufCommAdapter:pb_get_value:" << m->GetTypeName() << " invalid value type for "
1165  << field_name);
1166  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
1167  m_execInterface.notifyOfExternalEvent();
1168  return;
1169  }
1170  }
1171 
1172  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_SUCCESS);
1173  m_execInterface.notifyOfExternalEvent();
1174 }
1175 
1176 void
1177 ProtobufCommPlexilAdapter::pb_get_length(PLEXIL::Command *cmd)
1178 {
1179  std::vector<PLEXIL::Value> const &args = cmd->getArgValues();
1180  if (!verify_args(args,
1181  "ProtobufCommAdapter:pb_get_length",
1182  {{"msg_id", PLEXIL::STRING_TYPE}, {"field", PLEXIL::STRING_TYPE}})) {
1183  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
1184  m_execInterface.notifyOfExternalEvent();
1185  return;
1186  }
1187 
1188  std::string msg_id;
1189  std::string field_name;
1190  args[0].getValue(msg_id);
1191  args[1].getValue(field_name);
1192 
1193  std::shared_ptr<google::protobuf::Message> m = get_message(msg_id);
1194  if (!m) {
1195  warn("ProtobufCommAdapter:pb_get_length:"
1196  << " Unknown message " << msg_id);
1197  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
1198  m_execInterface.notifyOfExternalEvent();
1199  return;
1200  }
1201 
1202  const FieldDescriptor * field = nullptr;
1203  google::protobuf::Message *msg = m.get();
1204 
1205  std::string partial_name;
1206  long int partial_index = -1;
1207 
1208  if (!traverse_field(msg, field_name, field, partial_name, partial_index, "pb_get_length")) {
1209  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
1210  m_execInterface.notifyOfExternalEvent();
1211  return;
1212  }
1213 
1214  if (!field) {
1215  warn("ProtobufCommAdapter:pb_get_length:"
1216  << " Unknown field " << field_name << " for message " << msg_id);
1217  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
1218  m_execInterface.notifyOfExternalEvent();
1219  return;
1220  }
1221 
1222  if (!field->is_repeated()) {
1223  warn("ProtobufCommAdapter:pb_get_length:"
1224  << " Field " << field_name << " is not a repeated field in " << msg_id);
1225  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
1226  m_execInterface.notifyOfExternalEvent();
1227  return;
1228  }
1229  const Reflection *refl = msg->GetReflection();
1230 
1231  m_execInterface.handleCommandReturn(cmd, PLEXIL::Value(refl->FieldSize(*msg, field)));
1232  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_SUCCESS);
1233  m_execInterface.notifyOfExternalEvent();
1234 }
1235 
1236 void
1237 ProtobufCommPlexilAdapter::pb_has_field(PLEXIL::Command *cmd)
1238 {
1239  std::vector<PLEXIL::Value> const &args = cmd->getArgValues();
1240  if (!verify_args(args,
1241  "ProtobufCommAdapter:pb_has_field",
1242  {{"msg_id", PLEXIL::STRING_TYPE}, {"field", PLEXIL::STRING_TYPE}})) {
1243  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
1244  m_execInterface.notifyOfExternalEvent();
1245  return;
1246  }
1247 
1248  std::string msg_id;
1249  std::string field_name;
1250  args[0].getValue(msg_id);
1251  args[1].getValue(field_name);
1252 
1253  std::shared_ptr<google::protobuf::Message> m = get_message(msg_id);
1254  if (!m) {
1255  warn("ProtobufCommAdapter:pb_has_field:"
1256  << " Unknown message " << msg_id);
1257  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
1258  m_execInterface.notifyOfExternalEvent();
1259  return;
1260  }
1261 
1262  const FieldDescriptor * field = nullptr;
1263  google::protobuf::Message *msg = m.get();
1264 
1265  std::string partial_name;
1266  long int partial_index = -1;
1267 
1268  if (!traverse_field(msg, field_name, field, partial_name, partial_index, "pb_has_field")) {
1269  m_execInterface.handleCommandReturn(cmd, PLEXIL::Value(false));
1270  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_SUCCESS);
1271  m_execInterface.notifyOfExternalEvent();
1272  return;
1273  }
1274 
1275  if (!field) {
1276  m_execInterface.handleCommandReturn(cmd, PLEXIL::Value(false));
1277  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_SUCCESS);
1278  m_execInterface.notifyOfExternalEvent();
1279  return;
1280  }
1281 
1282  const Reflection *refl = msg->GetReflection();
1283 
1284  m_execInterface.handleCommandReturn(cmd, PLEXIL::Value(refl->HasField(*msg, field)));
1285  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_SUCCESS);
1286  m_execInterface.notifyOfExternalEvent();
1287 }
1288 
1289 void
1290 ProtobufCommPlexilAdapter::pb_tostring(PLEXIL::Command *cmd)
1291 {
1292  std::vector<PLEXIL::Value> const &args = cmd->getArgValues();
1293  if (!verify_args(args, "ProtobufCommAdapter:pb_tostring", {{"msg_id", PLEXIL::STRING_TYPE}})) {
1294  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
1295  m_execInterface.notifyOfExternalEvent();
1296  return;
1297  }
1298 
1299  std::string msg_id;
1300  args[0].getValue(msg_id);
1301 
1302  std::shared_ptr<google::protobuf::Message> m = get_message(msg_id);
1303  if (!m) {
1304  warn("ProtobufCommAdapter:pb_tostring:"
1305  << " Unknown message " << msg_id);
1306  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
1307  m_execInterface.notifyOfExternalEvent();
1308  return;
1309  }
1310 
1311  m_execInterface.handleCommandReturn(cmd, PLEXIL::Value(m->DebugString()));
1312  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_SUCCESS);
1313  m_execInterface.notifyOfExternalEvent();
1314 }
1315 
1316 void
1317 ProtobufCommPlexilAdapter::pb_broadcast(PLEXIL::Command *cmd)
1318 {
1319  std::vector<PLEXIL::Value> const &args = cmd->getArgValues();
1320  if (!verify_args(args,
1321  "ProtobufCommAdapter:pb_broadcast",
1322  {{"peer_id", PLEXIL::INTEGER_TYPE}, {"msg_id", PLEXIL::STRING_TYPE}})) {
1323  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
1324  m_execInterface.notifyOfExternalEvent();
1325  return;
1326  }
1327 
1328  int peer_id;
1329  std::string msg_id;
1330  args[0].getValue(peer_id);
1331  args[1].getValue(msg_id);
1332 
1333  if (peers_.find(peer_id) == peers_.end()) {
1334  warn("ProtobufCommAdapter:pb_broadcast:"
1335  << " Unknown peer " << peer_id);
1336  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
1337  m_execInterface.notifyOfExternalEvent();
1338  return;
1339  }
1340 
1341  std::shared_ptr<google::protobuf::Message> m = get_message(msg_id);
1342  if (!m) {
1343  warn("ProtobufCommAdapter:pb_broadcast:"
1344  << " Unknown message " << msg_id);
1345  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
1346  m_execInterface.notifyOfExternalEvent();
1347  return;
1348  }
1349 
1350  std::lock_guard<std::mutex> lock(map_mutex_);
1351 
1352  //logger_->log_info("CLIPS-Protobuf", "Broadcasting %s", (*m)->GetTypeName().c_str());
1353  try {
1354  peers_[peer_id]->send(m);
1355  } catch (google::protobuf::FatalException &e) {
1356  warn("ProtobufCommAdapter:pb_broadcast:"
1357  << " Failed to send message " << msg_id << "(" << e.what() << ")");
1358  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
1359  m_execInterface.notifyOfExternalEvent();
1360  return;
1361  } catch (fawkes::Exception &e) {
1362  warn("ProtobufCommAdapter:pb_broadcast:"
1363  << " Failed to send message " << msg_id << "(" << e.what_no_backtrace() << ")");
1364  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
1365  m_execInterface.notifyOfExternalEvent();
1366  return;
1367  } catch (std::runtime_error &e) {
1368  warn("ProtobufCommAdapter:pb_broadcast:"
1369  << " Failed to message " << msg_id << "(" << e.what() << ")");
1370  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
1371  m_execInterface.notifyOfExternalEvent();
1372  return;
1373  }
1374 
1375  {
1376  std::lock_guard<std::mutex> lock(queue_mutex_);
1377  messages_.erase(msg_id);
1378  }
1379 
1380  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_SUCCESS);
1381  m_execInterface.notifyOfExternalEvent();
1382 }
1383 
1384 /** Enable protobuf peer.
1385  * @param address IP address to send messages to
1386  * @param send_port UDP port to send messages to
1387  * @param recv_port UDP port to receive messages on, 0 to use the same as the @p send_port
1388  * @param crypto_key encryption key
1389  * @param cipher cipher suite, see BufferEncryptor for supported types
1390  * @return peer identifier
1391  */
1392 void
1393 ProtobufCommPlexilAdapter::pb_peer_create_local_crypto(
1394  PLEXIL::Command * cmd,
1395  const std::vector<PLEXIL::Value> *override_args)
1396 {
1397  std::vector<PLEXIL::Value> const &args = override_args ? *override_args : cmd->getArgValues();
1398 
1399  if (!verify_args(args,
1400  "ProtobufCommAdapter:pb_peer_create_local_crypto",
1401  {{"address", PLEXIL::STRING_TYPE},
1402  {"send_port", PLEXIL::INTEGER_TYPE},
1403  {"recv_port", PLEXIL::INTEGER_TYPE},
1404  {"crypto_key", PLEXIL::STRING_TYPE},
1405  {"cipher", PLEXIL::STRING_TYPE}})) {
1406  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
1407  m_execInterface.notifyOfExternalEvent();
1408  return;
1409  }
1410 
1411  std::string address;
1412  int send_port, recv_port;
1413  std::string crypto_key, cipher;
1414 
1415  args[0].getValue(address);
1416  args[1].getValue(send_port);
1417  args[2].getValue(recv_port);
1418  args[3].getValue(crypto_key);
1419  args[4].getValue(cipher);
1420 
1421  if (recv_port <= 0)
1422  recv_port = send_port;
1423 
1424  if (send_port > 0) {
1425  std::shared_ptr<ProtobufBroadcastPeer> peer =
1426  std::make_shared<ProtobufBroadcastPeer>(address,
1427  (unsigned short)send_port,
1428  (unsigned short)recv_port,
1429  &*message_register_,
1430  crypto_key,
1431  cipher);
1432 
1433  int peer_id;
1434  {
1435  std::lock_guard<std::mutex> lock(map_mutex_);
1436  peer_id = ++next_client_id_;
1437  peers_[peer_id] = peer;
1438  }
1439 
1440  peer->signal_received().connect(
1441  boost::bind(&ProtobufCommPlexilAdapter::handle_peer_msg, this, peer_id, _1, _2, _3, _4));
1442  peer->signal_recv_error().connect(
1443  boost::bind(&ProtobufCommPlexilAdapter::handle_peer_recv_error, this, peer_id, _1, _2));
1444  peer->signal_send_error().connect(
1445  boost::bind(&ProtobufCommPlexilAdapter::handle_peer_send_error, this, peer_id, _1));
1446 
1447  m_execInterface.handleCommandReturn(cmd, PLEXIL::Value(peer_id));
1448  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_SUCCESS);
1449  m_execInterface.notifyOfExternalEvent();
1450  } else {
1451  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
1452  m_execInterface.notifyOfExternalEvent();
1453  }
1454 }
1455 
1456 /** Enable protobuf peer.
1457  * @param address IP address to send messages to
1458  * @param port UDP port to send and receive messages
1459  * @param crypto_key encryption key
1460  * @param cipher cipher suite, see BufferEncryptor for supported types
1461  * @return peer identifier
1462  */
1463 void
1464 ProtobufCommPlexilAdapter::pb_peer_create_crypto(PLEXIL::Command *cmd)
1465 {
1466  std::vector<PLEXIL::Value> const &in_args = cmd->getArgValues();
1467  if (!verify_args(in_args,
1468  "pb_peer_create_crypto",
1469  {{"address", PLEXIL::STRING_TYPE},
1470  {"port", PLEXIL::INTEGER_TYPE},
1471  {"crypto_key", PLEXIL::STRING_TYPE},
1472  {"cipher", PLEXIL::STRING_TYPE}})) {
1473  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
1474  m_execInterface.notifyOfExternalEvent();
1475  return;
1476  }
1477 
1478  std::vector<PLEXIL::Value> args{in_args[0], in_args[1], in_args[1], in_args[2], in_args[3]};
1479 
1480  pb_peer_create_local_crypto(cmd, &args);
1481 }
1482 
1483 /** Enable protobuf peer.
1484  * @param address IP address to send messages to
1485  * @param port UDP port to send and receive messages
1486  * @return peer identifier
1487  */
1488 void
1489 ProtobufCommPlexilAdapter::pb_peer_create(PLEXIL::Command *cmd)
1490 {
1491  std::vector<PLEXIL::Value> const &in_args = cmd->getArgValues();
1492  if (!verify_args(in_args,
1493  "pb_peer_create",
1494  {{"address", PLEXIL::STRING_TYPE}, {"port", PLEXIL::INTEGER_TYPE}})) {
1495  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
1496  m_execInterface.notifyOfExternalEvent();
1497  return;
1498  }
1499 
1500  std::vector<PLEXIL::Value> args{
1501  in_args[0], in_args[1], in_args[1], PLEXIL::Value(""), PLEXIL::Value("")};
1502 
1503  pb_peer_create_local_crypto(cmd, &args);
1504 }
1505 
1506 /** Enable protobuf peer.
1507  * @param address IP address to send messages to
1508  * @param send_port UDP port to send messages to
1509  * @param recv_port UDP port to receive messages on, 0 to use the same as the @p send_port
1510  * @return peer identifier
1511  */
1512 void
1513 ProtobufCommPlexilAdapter::pb_peer_create_local(PLEXIL::Command *cmd)
1514 {
1515  std::vector<PLEXIL::Value> const &in_args = cmd->getArgValues();
1516  if (!verify_args(in_args,
1517  "pb_peer_create_local",
1518  {{"address", PLEXIL::STRING_TYPE},
1519  {"send_port", PLEXIL::INTEGER_TYPE},
1520  {"recv_port", PLEXIL::INTEGER_TYPE}})) {
1521  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
1522  m_execInterface.notifyOfExternalEvent();
1523  return;
1524  }
1525 
1526  std::vector<PLEXIL::Value> args{
1527  in_args[0], in_args[1], in_args[2], PLEXIL::Value(""), PLEXIL::Value("")};
1528 
1529  pb_peer_create_local_crypto(cmd, &args);
1530 }
1531 
1532 /** Disable peer.
1533  * @param peer_id ID of the peer to destroy
1534  */
1535 void
1536 ProtobufCommPlexilAdapter::pb_peer_destroy(PLEXIL::Command *cmd)
1537 {
1538  std::vector<PLEXIL::Value> const &args = cmd->getArgValues();
1539  if (!verify_args(args,
1540  "ProtobufCommAdapter:pb_peer_destroy",
1541  {{"peer_id", PLEXIL::INTEGER_TYPE}})) {
1542  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
1543  m_execInterface.notifyOfExternalEvent();
1544  return;
1545  }
1546 
1547  int peer_id;
1548  args[0].getValue(peer_id);
1549 
1550  peers_.erase(peer_id);
1551 
1552  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_SUCCESS);
1553  m_execInterface.notifyOfExternalEvent();
1554 }
1555 
1556 /** Setup crypto for peer.
1557  * @param peer_id ID of the peer to destroy
1558  * @param crypto_key encryption key
1559  * @param cipher cipher suite, see BufferEncryptor for supported types
1560  */
1561 void
1562 ProtobufCommPlexilAdapter::pb_peer_setup_crypto(PLEXIL::Command *cmd)
1563 {
1564  std::vector<PLEXIL::Value> const &args = cmd->getArgValues();
1565  if (!verify_args(args,
1566  "ProtobufCommAdapter:pb_peer_setup_crypto",
1567  {{"peer_id", PLEXIL::INTEGER_TYPE},
1568  {"crypto_key", PLEXIL::STRING_TYPE},
1569  {"cipher", PLEXIL::STRING_TYPE}})) {
1570  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_FAILED);
1571  m_execInterface.notifyOfExternalEvent();
1572  return;
1573  }
1574 
1575  int peer_id;
1576  std::string crypto_key;
1577  std::string cipher;
1578 
1579  args[0].getValue(peer_id);
1580  args[1].getValue(crypto_key);
1581  args[2].getValue(cipher);
1582 
1583  if (peers_.find(peer_id) != peers_.end()) {
1584  peers_[peer_id]->setup_crypto(crypto_key, cipher);
1585  }
1586 
1587  m_execInterface.handleCommandAck(cmd, PLEXIL::COMMAND_SUCCESS);
1588  m_execInterface.notifyOfExternalEvent();
1589 }
1590 
1591 /** Handle message that came from a peer/robot
1592  * @param endpoint the endpoint from which the message was received
1593  * @param component_id component the message was addressed to
1594  * @param msg_type type of the message
1595  * @param msg the message
1596  */
1597 void
1598 ProtobufCommPlexilAdapter::handle_peer_msg(int peer_id,
1599  boost::asio::ip::udp::endpoint & endpoint,
1600  uint16_t component_id,
1601  uint16_t msg_type,
1602  std::shared_ptr<google::protobuf::Message> msg)
1603 {
1604  message_meta m{.time_received = fawkes::Time(clock_),
1605  .from_host = endpoint.address().to_string(),
1606  .from_port = endpoint.port(),
1607  .message = msg};
1608 
1609  add_message(msg->GetTypeName(), std::move(m));
1610  proc_queue(msg->GetTypeName());
1611 }
1612 
1613 /** Handle error during peer message processing.
1614  * @param endpoint endpoint of incoming message
1615  * @param msg error message
1616  */
1617 void
1618 ProtobufCommPlexilAdapter::handle_peer_recv_error(int peer_id,
1619  boost::asio::ip::udp::endpoint &endpoint,
1620  std::string msg)
1621 {
1622  if (logger_) {
1623  logger_->log_warn("PlexilProtobuf",
1624  "Failed to receive peer message from %s:%u: %s",
1625  endpoint.address().to_string().c_str(),
1626  endpoint.port(),
1627  msg.c_str());
1628  }
1629 }
1630 
1631 /** Handle error during peer message processing.
1632  * @param msg error message
1633  */
1634 void
1635 ProtobufCommPlexilAdapter::handle_peer_send_error(int peer_id, std::string msg)
1636 {
1637  if (logger_) {
1638  logger_->log_warn("PlexilProtobuf", "Failed to send peer message: %s", msg.c_str());
1639  }
1640 }
1641 
1642 extern "C" {
1643 void
1644 initProtobufCommAdapter()
1645 {
1646  REGISTER_ADAPTER(ProtobufCommPlexilAdapter, "ProtobufCommAdapter");
1647 }
1648 }
Interface adapter to provide logging facilities.
virtual bool initialize()
Initialize adapter.
virtual bool start()
Start adapter.
void executeCommand(PLEXIL::Command *cmd)
Perform given command.
void invokeAbort(PLEXIL::Command *cmd)
Abort currently running execution.
virtual bool shutdown()
Shut adapter down.
virtual ~ProtobufCommPlexilAdapter()
Destructor.
virtual bool reset()
Reset adapter.
ProtobufCommPlexilAdapter(PLEXIL::AdapterExecInterface &execInterface)
Constructor.
virtual bool stop()
Stop adapter.
This is supposed to be the central clock in Fawkes.
Definition: clock.h:35
Interface for configuration handling.
Definition: config.h:68
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_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.
A class for handling time.
Definition: time.h:93
Fawkes library namespace.