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
37using namespace fawkes;
38using namespace protobuf_comm;
39using namespace google::protobuf;
40using 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 */
50ProtobufCommPlexilAdapter::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 */
60ProtobufCommPlexilAdapter::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 */
74bool
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 */
135bool
137{
138 return true;
139}
140
141/** Stop adapter.
142 * @return true if successful, false otherwise.
143 */
144bool
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 */
157bool
159{
160 return true;
161}
162
163/** Shut adapter down.
164 * @return true if successful, false otherwise.
165 */
166bool
168{
169 return true;
170}
171
172/** Perform given command.
173 * @param cmd command to execute
174 */
175void
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
194void
195ProtobufCommPlexilAdapter::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
230void
231ProtobufCommPlexilAdapter::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
312void
313ProtobufCommPlexilAdapter::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 */
350void
352{
353 m_execInterface.handleCommandAbortAck(cmd, false);
354 m_execInterface.notifyOfExternalEvent();
355}
356
357void
358ProtobufCommPlexilAdapter::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
365void
366ProtobufCommPlexilAdapter::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
374void
375ProtobufCommPlexilAdapter::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
386void
387ProtobufCommPlexilAdapter::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
401std::shared_ptr<google::protobuf::Message>
402ProtobufCommPlexilAdapter::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
414std::string
415ProtobufCommPlexilAdapter::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
421void
422ProtobufCommPlexilAdapter::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
439ProtobufCommPlexilAdapter::queue_entry &
440ProtobufCommPlexilAdapter::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 */
453void
454ProtobufCommPlexilAdapter::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 */
494void
495ProtobufCommPlexilAdapter::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
511static std::pair<std::string, long int>
512parse_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
538static bool
539traverse_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
619void
620ProtobufCommPlexilAdapter::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
931void
932ProtobufCommPlexilAdapter::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
1176void
1177ProtobufCommPlexilAdapter::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
1236void
1237ProtobufCommPlexilAdapter::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
1289void
1290ProtobufCommPlexilAdapter::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
1316void
1317ProtobufCommPlexilAdapter::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 */
1392void
1393ProtobufCommPlexilAdapter::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 */
1463void
1464ProtobufCommPlexilAdapter::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 */
1488void
1489ProtobufCommPlexilAdapter::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 */
1512void
1513ProtobufCommPlexilAdapter::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 */
1535void
1536ProtobufCommPlexilAdapter::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 */
1561void
1562ProtobufCommPlexilAdapter::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 */
1597void
1598ProtobufCommPlexilAdapter::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 */
1617void
1618ProtobufCommPlexilAdapter::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 */
1634void
1635ProtobufCommPlexilAdapter::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
1642extern "C" {
1643void
1644initProtobufCommAdapter()
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.