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