Note: Goby version 1 (shown here) is now considered obsolete. Please use version 2 for new projects, and consider upgrading old projects.

Goby Underwater Autonomy Project  Series: 1.1, revision: 163, released on 2013-02-06 14:23:27 -0500
acomms/libdccl/message.cpp
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 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends