23 #include <boost/filesystem.hpp> 24 #include <boost/foreach.hpp> 26 #include "dccl_transitional.h" 27 #include "goby/common/logger.h" 28 #include "goby/util/as.h" 29 #include "goby/util/dynamic_protobuf_manager.h" 30 #include "message_xml_callbacks.h" 31 #include "queue_xml_callbacks.h" 32 #include <boost/regex.hpp> 33 #include <google/protobuf/descriptor.pb.h> 37 using goby::acomms::operator<<;
44 : log_(0), dccl_(
goby::acomms::DCCLCodec::get()), start_time_(
goby_time())
46 goby::util::DynamicProtobufManager::enable_compilation();
49 void goby::transitional::DCCLTransitionalCodec::convert_to_v2_representation(
52 cfg_ = cfg->transitional_cfg();
54 for (
int i = 0, n = cfg->transitional_cfg().message_file_size(); i < n; ++i)
56 convert_xml_message_file(cfg->transitional_cfg().message_file(i),
57 cfg->add_load_proto_file(), cfg->mutable_translator_entry(),
58 cfg->mutable_queue_cfg());
62 void goby::transitional::DCCLTransitionalCodec::convert_xml_message_file(
64 google::protobuf::RepeatedPtrField<goby::moos::protobuf::TranslatorEntry>* translator_entries,
67 const std::string& xml_file = message_file.path();
69 size_t begin_size = messages_.size();
75 XMLParser message_parser(message_content, message_error);
77 std::vector<goby::transitional::protobuf::QueueConfig> old_queue_cfg;
81 XMLParser queue_parser(queue_content, queue_error);
83 message_parser.parse(xml_file);
84 queue_parser.parse(xml_file);
86 size_t end_size = messages_.size();
90 std::vector<unsigned> added_ids;
91 std::set<unsigned> set_added_ids;
93 boost::filesystem::path xml_file_path(xml_file);
95 std::string generated_proto_dir = cfg_.generated_proto_dir();
96 if (!generated_proto_dir.empty() && generated_proto_dir[generated_proto_dir.size() - 1] !=
'/')
97 generated_proto_dir +=
"/";
99 #if BOOST_FILESYSTEM_VERSION == 3 100 std::string xml_path_stem = xml_file_path.stem().string();
101 boost::filesystem::path proto_file_path =
102 boost::filesystem::absolute(generated_proto_dir + xml_path_stem +
".proto");
104 std::string xml_path_stem = xml_file_path.stem();
105 boost::filesystem::path proto_file_path =
106 boost::filesystem::complete(generated_proto_dir + xml_path_stem +
".proto");
109 proto_file_path.normalize();
111 for (
size_t i = 0, n = end_size - begin_size; i < n; ++i)
114 size_t new_index = messages_.size() - (n - i);
115 name2messages_.insert(
116 std::pair<std::string, size_t>(messages_[new_index].name(), new_index));
117 id2messages_.insert(std::pair<unsigned, size_t>(messages_[new_index].
id(), new_index));
119 set_added_ids.insert(messages_[new_index].
id());
120 added_ids.push_back(messages_[new_index].
id());
123 *proto_file = proto_file_path.string();
125 std::ofstream fout(proto_file->c_str(), std::ios::out);
128 throw(goby::acomms::DCCLException(
"Could not open " + *proto_file +
" for writing"));
130 fout <<
"import \"dccl/option_extensions.proto\";" << std::endl;
131 fout <<
"import \"goby/common/protobuf/option_extensions.proto\";" << std::endl;
133 for (
int i = 0, n = added_ids.size(); i < n; ++i)
135 to_iterator(added_ids[i])->write_schema_to_dccl2(&fout);
138 queue_entry->set_protobuf_name(to_iterator(added_ids[i])->name());
139 if (old_queue_cfg[i].has_ack())
140 queue_entry->set_ack(old_queue_cfg[i].ack());
141 if (old_queue_cfg[i].has_blackout_time())
142 queue_entry->set_blackout_time(old_queue_cfg[i].blackout_time());
143 if (old_queue_cfg[i].has_max_queue())
144 queue_entry->set_max_queue(old_queue_cfg[i].max_queue());
145 if (old_queue_cfg[i].has_newest_first())
146 queue_entry->set_newest_first(old_queue_cfg[i].newest_first());
147 if (old_queue_cfg[i].has_value_base())
148 queue_entry->set_value_base(old_queue_cfg[i].value_base());
149 if (old_queue_cfg[i].has_ttl())
150 queue_entry->set_ttl(old_queue_cfg[i].ttl());
153 src_role->set_type(goby::acomms::protobuf::QueuedMessageEntry::SOURCE_ID);
154 src_role->set_field(to_iterator(added_ids[i])->header_var(HEAD_SRC_ID).name());
157 dest_role->set_type(goby::acomms::protobuf::QueuedMessageEntry::DESTINATION_ID);
158 dest_role->set_field(to_iterator(added_ids[i])->header_var(HEAD_DEST_ID).name());
161 time_role->set_type(goby::acomms::protobuf::QueuedMessageEntry::TIMESTAMP);
162 time_role->set_field(to_iterator(added_ids[i])->header_var(HEAD_TIME).name());
164 for (
int i = 0, n = message_file.manipulator_size(); i < n; ++i)
165 queue_entry->add_manipulator(message_file.manipulator(i));
170 const google::protobuf::FileDescriptor* file_desc =
171 goby::util::DynamicProtobufManager::user_descriptor_pool().FindFileByName(*proto_file);
175 glog.is(DEBUG2) &&
glog << file_desc->DebugString() << std::flush;
177 BOOST_FOREACH (
unsigned id, added_ids)
179 const google::protobuf::Descriptor* desc =
180 file_desc->FindMessageTypeByName(to_iterator(
id)->name());
183 dccl_->validate(desc);
186 glog << die <<
"No descriptor with name " << to_iterator(
id)->name() <<
" found!" 190 to_iterator(
id)->set_descriptor(desc);
195 glog << die <<
"No file_descriptor with name " << *proto_file <<
" found!" << std::endl;
198 BOOST_FOREACH (
unsigned id, added_ids)
201 entry->set_use_short_enum(
true);
202 std::vector<DCCLMessage>::const_iterator msg_it = to_iterator(
id);
203 entry->set_protobuf_name(msg_it->name());
205 if (msg_it->trigger_type() ==
"time")
207 entry->mutable_trigger()->set_type(
208 goby::moos::protobuf::TranslatorEntry::Trigger::TRIGGER_TIME);
209 entry->mutable_trigger()->set_period(msg_it->trigger_time());
211 else if (msg_it->trigger_type() ==
"publish")
213 entry->mutable_trigger()->set_type(
214 goby::moos::protobuf::TranslatorEntry::Trigger::TRIGGER_PUBLISH);
215 entry->mutable_trigger()->set_moos_var(msg_it->trigger_var());
216 if (!msg_it->trigger_mandatory().empty())
217 entry->mutable_trigger()->set_mandatory_content(msg_it->trigger_mandatory());
220 std::map<std::string, goby::moos::protobuf::TranslatorEntry::CreateParser*> parser_map;
221 std::map<std::string, int> sequence_map;
222 int max_sequence = 0;
223 BOOST_FOREACH (boost::shared_ptr<DCCLMessageVar> var, msg_it->header_const())
225 sequence_map[var->name()] = var->sequence_number();
227 (var->sequence_number() > max_sequence) ? var->sequence_number() : max_sequence;
229 fill_create(var, &parser_map, entry);
231 BOOST_FOREACH (boost::shared_ptr<DCCLMessageVar> var, msg_it->layout_const())
233 sequence_map[var->name()] = var->sequence_number();
235 (var->sequence_number() > max_sequence) ? var->sequence_number() : max_sequence;
237 fill_create(var, &parser_map, entry);
240 max_sequence = ((max_sequence + 100) / 100) * 100;
242 for (
int i = 0, n = msg_it->publishes_const().size(); i < n; ++i)
244 const DCCLPublish& publish = msg_it->publishes_const()[i];
247 entry->add_publish();
248 serializer->set_technique(goby::moos::protobuf::TranslatorEntry::TECHNIQUE_FORMAT);
251 boost::regex pattern(
"%([0-9]+)",
252 boost::regex_constants::icase | boost::regex_constants::perl);
253 std::string replace(
"%_GOBY1TEMP_\\1_");
255 std::string new_moos_var = boost::regex_replace(publish.var(), pattern, replace);
257 std::string old_format = publish.format();
258 std::string new_format = boost::regex_replace(old_format, pattern, replace);
261 for (
int j = 0, m = publish.message_vars().size(); j < m; ++j)
263 for (
int k = 0, o = publish.message_vars()[j]->array_length(); k < o; ++k)
265 int replacement_field = sequence_map[publish.message_vars()[j]->name()];
268 bool added_algorithms =
false;
269 BOOST_FOREACH (
const std::string& algorithm, publish.algorithms()[j])
271 added_algorithms =
true;
273 new_alg = serializer->add_algorithm();
274 std::vector<std::string> algorithm_parts;
275 boost::split(algorithm_parts, algorithm, boost::is_any_of(
":"));
277 new_alg->set_name(algorithm_parts[0]);
278 new_alg->set_primary_field(sequence_map[publish.message_vars()[j]->name()]);
280 for (
int l = 1, p = algorithm_parts.size(); l < p; ++l)
281 new_alg->add_reference_field(sequence_map[algorithm_parts[l]]);
283 new_alg->set_output_virtual_field(max_sequence);
286 if (added_algorithms)
288 replacement_field = max_sequence;
292 std::string v2_index = goby::util::as<std::string>(replacement_field);
294 v2_index +=
"." + goby::util::as<std::string>(k);
296 boost::algorithm::replace_all(
297 new_moos_var,
"%_GOBY1TEMP_" + goby::util::as<std::string>(v1_index) +
"_",
299 boost::algorithm::replace_all(
300 new_format,
"%_GOBY1TEMP_" + goby::util::as<std::string>(v1_index) +
"_",
306 serializer->set_moos_var(new_moos_var);
307 serializer->set_format(new_format);
312 void goby::transitional::DCCLTransitionalCodec::fill_create(
313 boost::shared_ptr<DCCLMessageVar> var,
314 std::map<std::string, goby::moos::protobuf::TranslatorEntry::CreateParser*>* parser_map,
318 if ((*parser_map).count(var->source_var()))
321 (*parser_map)[var->source_var()]->set_technique(
323 TECHNIQUE_COMMA_SEPARATED_KEY_EQUALS_VALUE_PAIRS);
324 (*parser_map)[var->source_var()]->clear_format();
326 else if (!var->source_var().empty())
328 (*parser_map)[var->source_var()] = entry->add_create();
329 (*parser_map)[var->source_var()]->set_format(
330 "%" + goby::util::as<std::string>(var->sequence_number()) +
"%");
331 (*parser_map)[var->source_var()]->set_technique(
332 goby::moos::protobuf::TranslatorEntry::TECHNIQUE_FORMAT);
333 (*parser_map)[var->source_var()]->set_moos_var(var->source_var());
337 BOOST_FOREACH (
const std::string& algorithm, var->algorithms())
339 if (var->source_var().empty())
340 throw(std::runtime_error(
"Algorithms without a corresponding source variable are not " 341 "permitted in Goby v2 --> v1 backwards compatibility as the " 342 "MOOS Translator does all algorithm conversions in Goby2"));
345 (*parser_map)[var->source_var()]->add_algorithm();
347 if (algorithm.find(
':') != std::string::npos)
349 throw(std::runtime_error(
350 "Algorithms with reference fields (e.g. subtract:timestamp) are not permitted in " 351 "Goby v2 --> v1 backwards compatibility. Please redesign the XML message to remove " 352 "such algorithms. Offending algorithm: " +
353 algorithm +
" used in variable: " + var->name()));
356 new_alg->set_name(algorithm);
357 new_alg->set_primary_field(var->sequence_number());
361 std::ostream& goby::transitional::operator<<(std::ostream& out, const std::set<std::string>& s)
363 out <<
"std::set<std::string>:" << std::endl;
364 for (std::set<std::string>::const_iterator it = s.begin(), n = s.end(); it != n; ++it)
365 out << (*it) << std::endl;
369 std::ostream& goby::transitional::operator<<(std::ostream& out, const std::set<unsigned>& s)
371 out <<
"std::set<unsigned>:" << std::endl;
372 for (std::set<unsigned>::const_iterator it = s.begin(), n = s.end(); it != n; ++it)
373 out << (*it) << std::endl;
381 void goby::transitional::DCCLTransitionalCodec::check_duplicates()
383 std::map<unsigned, std::vector<DCCLMessage>::iterator> all_ids;
384 for (std::vector<DCCLMessage>::iterator it = messages_.begin(), n = messages_.end(); it != n;
387 unsigned id = it->id();
389 std::map<unsigned, std::vector<DCCLMessage>::iterator>::const_iterator id_it =
391 if (id_it != all_ids.end())
392 throw goby::acomms::DCCLException(
393 std::string(
"DCCL: duplicate variable id " + as<std::string>(
id) +
394 " specified for " + it->name() +
" and " + id_it->second->name()));
396 all_ids.insert(std::pair<
unsigned, std::vector<DCCLMessage>::iterator>(
id, it));
400 std::vector<goby::transitional::DCCLMessage>::const_iterator
401 goby::transitional::DCCLTransitionalCodec::to_iterator(
const std::string& message_name)
const 403 if (name2messages_.count(message_name))
404 return messages_.begin() + name2messages_.find(message_name)->second;
406 throw goby::acomms::DCCLException(std::string(
"DCCL: attempted an operation on message [" +
407 message_name +
"] which is not loaded"));
409 std::vector<goby::transitional::DCCLMessage>::iterator
410 goby::transitional::DCCLTransitionalCodec::to_iterator(
const std::string& message_name)
412 if (name2messages_.count(message_name))
413 return messages_.begin() + name2messages_.find(message_name)->second;
415 throw goby::acomms::DCCLException(std::string(
"DCCL: attempted an operation on message [" +
416 message_name +
"] which is not loaded"));
418 std::vector<goby::transitional::DCCLMessage>::const_iterator
419 goby::transitional::DCCLTransitionalCodec::to_iterator(
const unsigned&
id)
const 421 if (id2messages_.count(
id))
422 return messages_.begin() + id2messages_.find(
id)->second;
424 throw goby::acomms::DCCLException(std::string(
"DCCL: attempted an operation on message [" +
425 as<std::string>(
id) +
426 "] which is not loaded"));
429 std::vector<goby::transitional::DCCLMessage>::iterator
430 goby::transitional::DCCLTransitionalCodec::to_iterator(
const unsigned&
id)
432 if (id2messages_.count(
id))
433 return messages_.begin() + id2messages_.find(
id)->second;
435 throw goby::acomms::DCCLException(std::string(
"DCCL: attempted an operation on message [" +
436 as<std::string>(
id) +
437 "] which is not loaded"));
ReturnType goby_time()
Returns current UTC time as a boost::posix_time::ptime.
DCCLTransitionalCodec()
Instantiate optionally with a ostream logger (for human readable output)
common::FlexOstream glog
Access the Goby logger through this object.
The global namespace for the Goby project.