Goby Underwater Autonomy Project
Series: 1.1, revision: 163, released on 2013-02-06 14:23:27 -0500
|
00001 // copyright 2008, 2009 t. schneider tes@mit.edu 00002 // 00003 // this file is part of the Dynamic Compact Control Language (DCCL), 00004 // the goby-acomms codec. goby-acomms is a collection of libraries 00005 // for acoustic underwater networking 00006 // 00007 // This program is free software: you can redistribute it and/or modify 00008 // it under the terms of the GNU General Public License as published by 00009 // the Free Software Foundation, either version 3 of the License, or 00010 // (at your option) any later version. 00011 // 00012 // This software is distributed in the hope that it will be useful, 00013 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00015 // GNU General Public License for more details. 00016 // 00017 // You should have received a copy of the GNU General Public License 00018 // along with this software. If not, see <http://www.gnu.org/licenses/>. 00019 00020 #include <boost/foreach.hpp> 00021 00022 #include "goby/util/string.h" 00023 #include "message.h" 00024 #include "dccl_exception.h" 00025 00026 using goby::util::as; 00027 00028 00029 goby::acomms::DCCLMessage::DCCLMessage():size_(0), 00030 trigger_number_(1), 00031 body_bits_(0), 00032 id_(0), 00033 trigger_time_(0.0), 00034 repeat_enabled_(false), 00035 repeat_(1) 00036 { 00037 header_.resize(DCCL_NUM_HEADER_PARTS); 00038 header_[HEAD_CCL_ID] = 00039 boost::shared_ptr<DCCLMessageVar>(new DCCLMessageVarCCLID()); 00040 header_[HEAD_DCCL_ID] = 00041 boost::shared_ptr<DCCLMessageVar>(new DCCLMessageVarDCCLID()); 00042 header_[HEAD_TIME] = 00043 boost::shared_ptr<DCCLMessageVar>(new DCCLMessageVarTime()); 00044 header_[HEAD_SRC_ID] = 00045 boost::shared_ptr<DCCLMessageVar>(new DCCLMessageVarSrc()); 00046 header_[HEAD_DEST_ID] = 00047 boost::shared_ptr<DCCLMessageVar>(new DCCLMessageVarDest()); 00048 header_[HEAD_MULTIMESSAGE_FLAG] = 00049 boost::shared_ptr<DCCLMessageVar>(new DCCLMessageVarMultiMessageFlag()); 00050 header_[HEAD_BROADCAST_FLAG] = 00051 boost::shared_ptr<DCCLMessageVar>(new DCCLMessageVarBroadcastFlag()); 00052 header_[HEAD_UNUSED] = 00053 boost::shared_ptr<DCCLMessageVar>(new DCCLMessageVarUnused()); 00054 } 00055 00056 00057 // add a new message_var to the current messages vector 00058 void goby::acomms::DCCLMessage::add_message_var(const std::string& type) 00059 { 00060 if(type == "static") 00061 layout_.push_back(boost::shared_ptr<DCCLMessageVar>(new DCCLMessageVarStatic())); 00062 else if(type == "int") 00063 layout_.push_back(boost::shared_ptr<DCCLMessageVar>(new DCCLMessageVarInt())); 00064 else if(type == "string") 00065 layout_.push_back(boost::shared_ptr<DCCLMessageVar>(new DCCLMessageVarString())); 00066 else if(type == "float") 00067 layout_.push_back(boost::shared_ptr<DCCLMessageVar>(new DCCLMessageVarFloat())); 00068 else if(type == "enum") 00069 layout_.push_back(boost::shared_ptr<DCCLMessageVar>(new DCCLMessageVarEnum())); 00070 else if(type == "bool") 00071 layout_.push_back(boost::shared_ptr<DCCLMessageVar>(new DCCLMessageVarBool())); 00072 else if(type == "hex") 00073 layout_.push_back(boost::shared_ptr<DCCLMessageVar>(new DCCLMessageVarHex())); 00074 } 00075 00076 // add a new publish, i.e. a set of parameters to publish 00077 // upon receipt of an incoming (hex) message 00078 void goby::acomms::DCCLMessage::add_publish() 00079 { 00080 DCCLPublish p; 00081 publishes_.push_back(p); 00082 } 00083 00084 // a number of tasks to perform after reading in an entire <message> from 00085 // the xml file 00086 void goby::acomms::DCCLMessage::preprocess() 00087 { 00088 if(requested_bytes_total() <= bytes_head()) 00089 throw(DCCLException(std::string("<size> must be larger than the header size of " + as<std::string>(bytes_head())))); 00090 00091 // calculate number of repeated messages that will fit and put this in `repeat_`. 00092 if(repeat_enabled_) 00093 { 00094 BOOST_FOREACH(boost::shared_ptr<DCCLMessageVar> mv, layout_) 00095 { 00096 if(mv->array_length() != 1) 00097 throw(DCCLException("<repeat> is not allowed on messages with arrays (<array_length> not 1)")); 00098 } 00099 00100 // crank up the repeat until we go over 00101 while(calc_total_size() <= requested_bits_body()) 00102 { 00103 ++repeat_; 00104 set_repeat_array_length(); 00105 } 00106 00107 // back off one 00108 --repeat_; 00109 set_repeat_array_length(); 00110 } 00111 00112 body_bits_ = calc_total_size(); 00113 00114 // initialize header vars 00115 BOOST_FOREACH(boost::shared_ptr<DCCLMessageVar> mv, header_) 00116 mv->initialize(*this); 00117 00118 00119 if(body_bits_ > requested_bits_body() || repeat_ == 0) 00120 { 00121 throw DCCLException(std::string("DCCL: " + get_display() + "the message [" + name_ + "] will not fit within specified size. remove parameters, tighten bounds, or increase allowed size. details of the offending message are printed above.")); 00122 } 00123 00124 // iterate over publishes_ 00125 BOOST_FOREACH(DCCLPublish& p, publishes_) 00126 p.initialize(*this); 00127 00128 // set incoming_var / outgoing_var if not set 00129 if(in_var_ == "") 00130 in_var_ = "IN_" + boost::to_upper_copy(name_) + "_HEX_" + as<std::string>(size_) + "B"; 00131 if(out_var_ == "") 00132 out_var_ = "OUT_" + boost::to_upper_copy(name_) + "_HEX_" + as<std::string>(size_) + "B"; 00133 } 00134 00135 void goby::acomms::DCCLMessage::set_repeat_array_length() 00136 { 00137 // set array_length_ for repeated messages for all DCCLMessageVars 00138 BOOST_FOREACH(boost::shared_ptr<DCCLMessageVar> mv, layout_) 00139 mv->set_array_length(repeat_); 00140 } 00141 00142 unsigned goby::acomms::DCCLMessage::calc_total_size() 00143 { 00144 unsigned body_bits = 0; 00145 // iterate over layout_ 00146 BOOST_FOREACH(boost::shared_ptr<DCCLMessageVar> mv, layout_) 00147 { 00148 mv->initialize(*this); 00149 // calculate total bits for the message from the bits for each message_var 00150 body_bits += mv->calc_total_size(); 00151 } 00152 return body_bits; 00153 } 00154 00155 00156 00157 std::map<std::string, std::string> goby::acomms::DCCLMessage::message_var_names() const 00158 { 00159 std::map<std::string, std::string> s; 00160 BOOST_FOREACH(const boost::shared_ptr<DCCLMessageVar> mv, layout_) 00161 s.insert(std::pair<std::string, std::string>(mv->name(), type_to_string(mv->type()))); 00162 return s; 00163 } 00164 00165 std::set<std::string> goby::acomms::DCCLMessage::get_pubsub_encode_vars() 00166 { 00167 std::set<std::string> s = get_pubsub_src_vars(); 00168 if(trigger_type_ == "publish") 00169 s.insert(trigger_var_); 00170 return s; 00171 } 00172 00173 std::set<std::string> goby::acomms::DCCLMessage::get_pubsub_decode_vars() 00174 { 00175 std::set<std::string> s; 00176 s.insert(in_var_); 00177 return s; 00178 } 00179 00180 std::set<std::string> goby::acomms::DCCLMessage::get_pubsub_all_vars() 00181 { 00182 std::set<std::string> s_enc = get_pubsub_encode_vars(); 00183 std::set<std::string> s_dec = get_pubsub_decode_vars(); 00184 00185 std::set<std::string>& s = s_enc; 00186 s.insert(s_dec.begin(), s_dec.end()); 00187 00188 return s; 00189 } 00190 00191 std::set<std::string> goby::acomms::DCCLMessage::get_pubsub_src_vars() 00192 { 00193 std::set<std::string> s; 00194 00195 BOOST_FOREACH(boost::shared_ptr<DCCLMessageVar> mv, layout_) 00196 s.insert(mv->source_var()); 00197 BOOST_FOREACH(boost::shared_ptr<DCCLMessageVar> mv, header_) 00198 s.insert(mv->source_var()); 00199 00200 return s; 00201 } 00202 00203 00204 void goby::acomms::DCCLMessage::body_encode(std::string& body, std::map<std::string, std::vector<DCCLMessageVal> >& in) 00205 { 00206 boost::dynamic_bitset<unsigned char> body_bits(bytes2bits(used_bytes_body())); 00207 00208 // 1. encode each variable into the bitset 00209 for (std::vector< boost::shared_ptr<DCCLMessageVar> >::iterator it = layout_.begin(), 00210 n = layout_.end(); 00211 it != n; 00212 ++it) 00213 { 00214 (*it)->var_encode(in, body_bits); 00215 } 00216 00217 // 2. bitset to string 00218 bitset2string(body_bits, body); 00219 00220 // 3. strip all the ending zeros 00221 body.resize(body.find_last_not_of(char(0))+1); 00222 } 00223 00224 void goby::acomms::DCCLMessage::body_decode(std::string& body, std::map<std::string, std::vector<DCCLMessageVal> >& out) 00225 { 00226 boost::dynamic_bitset<unsigned char> body_bits(bytes2bits(used_bytes_body())); 00227 00228 // 3. resize the string to the proper size 00229 body.resize(used_bytes_body()); 00230 00231 // 2. convert string to bitset 00232 string2bitset(body_bits, body); 00233 00234 // 1. pull the bits off the message in the reverse that they were put on 00235 for (std::vector< boost::shared_ptr<DCCLMessageVar> >::reverse_iterator it = layout_.rbegin(), 00236 n = layout_.rend(); 00237 it != n; 00238 ++it) 00239 { 00240 (*it)->var_decode(out, body_bits); 00241 } 00242 } 00243 00244 void goby::acomms::DCCLMessage::set_head_defaults(std::map<std::string, std::vector<DCCLMessageVal> >& in, unsigned modem_id) 00245 { 00246 for (std::vector< boost::shared_ptr<DCCLMessageVar> >::iterator it = header_.begin(), 00247 n = header_.end(); 00248 it != n; 00249 ++it) 00250 { 00251 (*it)->set_defaults(in, modem_id, id_); 00252 } 00253 } 00254 00255 void goby::acomms::DCCLMessage::head_encode(std::string& head, std::map<std::string, std::vector<DCCLMessageVal> >& in) 00256 { 00257 boost::dynamic_bitset<unsigned char> head_bits(bytes2bits(DCCL_NUM_HEADER_BYTES)); 00258 00259 00260 for (std::vector< boost::shared_ptr<DCCLMessageVar> >::iterator it = header_.begin(), 00261 n = header_.end(); 00262 it != n; 00263 ++it) 00264 { 00265 (*it)->var_encode(in, head_bits); 00266 } 00267 00268 bitset2string(head_bits, head); 00269 } 00270 00271 void goby::acomms::DCCLMessage::head_decode(const std::string& head, std::map<std::string, std::vector<DCCLMessageVal> >& out) 00272 { 00273 boost::dynamic_bitset<unsigned char> head_bits(bytes2bits(DCCL_NUM_HEADER_BYTES)); 00274 string2bitset(head_bits, head); 00275 00276 for (std::vector< boost::shared_ptr<DCCLMessageVar> >::reverse_iterator it = header_.rbegin(), n = header_.rend(); 00277 it != n; 00278 ++it) 00279 { 00280 (*it)->var_decode(out, head_bits); 00281 } 00282 } 00283 00284 boost::shared_ptr<goby::acomms::DCCLMessageVar> goby::acomms::DCCLMessage::name2message_var(const std::string& name) const 00285 { 00286 BOOST_FOREACH(boost::shared_ptr<DCCLMessageVar> mv, layout_) 00287 { 00288 if(mv->name() == name) return mv; 00289 } 00290 BOOST_FOREACH(boost::shared_ptr<DCCLMessageVar> mv, header_) 00291 { 00292 if(mv->name() == name) return mv; 00293 } 00294 00295 throw DCCLException(std::string("DCCL: no such name \"" + name + "\" found in <layout> or <header>")); 00296 00297 return boost::shared_ptr<DCCLMessageVar>(); 00298 } 00299 00301 // VISUALIZATION 00303 00304 00305 // a long visual display of all the parameters for a DCCLMessage 00306 std::string goby::acomms::DCCLMessage::get_display() const 00307 { 00308 const unsigned int num_stars = 20; 00309 00310 bool is_moos = !trigger_type_.empty(); 00311 00312 std::stringstream ss; 00313 ss << std::string(num_stars, '*') << std::endl; 00314 ss << "message " << id_ << ": {" << name_ << "}" << std::endl; 00315 00316 if(is_moos) 00317 { 00318 ss << "trigger_type: {" << trigger_type_ << "}" << std::endl; 00319 00320 00321 if(trigger_type_ == "publish") 00322 { 00323 ss << "trigger_var: {" << trigger_var_ << "}"; 00324 if (trigger_mandatory_ != "") 00325 ss << " must contain string \"" << trigger_mandatory_ << "\""; 00326 ss << std::endl; 00327 } 00328 else if(trigger_type_ == "time") 00329 { 00330 ss << "trigger_time: {" << trigger_time_ << "}" << std::endl; 00331 } 00332 00333 ss << "outgoing_hex_var: {" << out_var_ << "}" << std::endl; 00334 ss << "incoming_hex_var: {" << in_var_ << "}" << std::endl; 00335 00336 if(repeat_enabled_) 00337 ss << "repeated " << repeat_ << " times." << std::endl; 00338 00339 } 00340 00341 ss << "requested size {bytes} [bits]: {" << requested_bytes_total() << "} [" << requested_bits_total() << "]" << std::endl; 00342 ss << "actual size {bytes} [bits]: {" << used_bytes_total() << "} [" << used_bits_total() << "]" << std::endl; 00343 00344 ss << ">>>> HEADER <<<<" << std::endl; 00345 BOOST_FOREACH(const boost::shared_ptr<DCCLMessageVar> mv, header_) 00346 ss << *mv; 00347 00348 ss << ">>>> LAYOUT (message_vars) <<<<" << std::endl; 00349 00350 BOOST_FOREACH(const boost::shared_ptr<DCCLMessageVar> mv, layout_) 00351 ss << *mv; 00352 00353 if(is_moos) 00354 { 00355 00356 ss << ">>>> PUBLISHES <<<<" << std::endl; 00357 00358 BOOST_FOREACH(const DCCLPublish& p, publishes_) 00359 ss << p; 00360 } 00361 00362 ss << std::string(num_stars, '*') << std::endl; 00363 00364 00365 return ss.str(); 00366 } 00367 00368 // a much shorter rundown of the Message parameters 00369 std::string goby::acomms::DCCLMessage::get_short_display() const 00370 { 00371 std::stringstream ss; 00372 00373 ss << name_ << ": "; 00374 00375 bool is_moos = !trigger_type_.empty(); 00376 if(is_moos) 00377 { 00378 ss << "trig: "; 00379 00380 if(trigger_type_ == "publish") 00381 ss << trigger_var_; 00382 else if(trigger_type_ == "time") 00383 ss << trigger_time_ << "s"; 00384 ss << " | out: " << out_var_; 00385 ss << " | in: " << in_var_ << " | "; 00386 } 00387 00388 ss << "size: {" << used_bytes_total() << "/" << requested_bytes_total() << "B} [" << used_bits_total() << "/" << requested_bits_total() << "b] | message var N: " << layout_.size() << std::endl; 00389 00390 return ss.str(); 00391 } 00392 00393 // overloaded << 00394 std::ostream& goby::acomms::operator<< (std::ostream& out, const DCCLMessage& message) 00395 { 00396 out << message.get_display(); 00397 return out; 00398 }