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/libamac/mac_manager.cpp
00001 // copyright 2009, 2010 t. schneider tes@mit.edu
00002 // 
00003 // this file is part of libamac, a medium access control for
00004 // acoustic networks. 
00005 //
00006 // see the readme file within this directory for information
00007 // pertaining to usage and purpose of this script.
00008 //
00009 // This program is free software: you can redistribute it and/or modify
00010 // it under the terms of the GNU General Public License as published by
00011 // the Free Software Foundation, either version 3 of the License, or
00012 // (at your option) any later version.
00013 //
00014 // This software is distributed in the hope that it will be useful,
00015 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00016 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017 // GNU General Public License for more details.
00018 //
00019 // You should have received a copy of the GNU General Public License
00020 // along with this software.  If not, see <http://www.gnu.org/licenses/>.
00021 
00022 #include <iostream>
00023 #include <cmath>
00024 
00025 #include <boost/date_time/gregorian/gregorian_types.hpp>
00026 #include <boost/thread.hpp>
00027 #include <boost/bind.hpp>
00028 #include <boost/foreach.hpp>
00029 
00030 #include "goby/acomms/libdccl/dccl_constants.h"
00031 #include "goby/util/logger.h"
00032 #include "goby/acomms/acomms_helpers.h"
00033 
00034 #include "mac_manager.h"
00035 
00036 using goby::util::goby_time;
00037 using goby::util::as;
00038 using namespace goby::util::tcolor;
00039 
00040 goby::acomms::MACManager::MACManager(std::ostream* log /* =0 */)
00041     : log_(log),
00042       timer_(io_),
00043       timer_is_running_(false),
00044       current_slot_(slot_order_.begin()),
00045       startup_done_(false)
00046 { }
00047 
00048 goby::acomms::MACManager::~MACManager()
00049 { }
00050 
00051 void goby::acomms::MACManager::do_work()
00052 {    
00053     // let the io service execute ready handlers (in this case, is the timer up?)
00054     if(timer_is_running_) io_.poll();
00055 }
00056 
00057 void goby::acomms::MACManager::restart_timer()
00058 {    
00059     // cancel any old timer jobs waiting
00060     timer_.cancel();
00061     timer_.expires_at(next_slot_t_);
00062     timer_.async_wait(boost::bind(&MACManager::send_poll, this, _1));
00063     timer_is_running_ = true;
00064 }
00065 
00066 void goby::acomms::MACManager::stop_timer()
00067 {
00068     timer_is_running_ = false;
00069     timer_.cancel();
00070 }
00071 
00072 void goby::acomms::MACManager::startup(const protobuf::MACConfig& cfg)
00073 {
00074     if(startup_done_)
00075     {
00076         if(log_) *log_ << warn << group("mm_out") << "startup() called but driver is already started." << std::endl;
00077         return;
00078     }
00079     
00080     // create a copy for us
00081     cfg_ = cfg;
00082     
00083     switch(cfg_.type())
00084     {
00085         case protobuf::MAC_AUTO_DECENTRALIZED:
00086         {
00087             if(log_) *log_ << group("mac")
00088                            << "Using the Decentralized Slotted TDMA MAC scheme with autodiscovery"
00089                            << std::endl;
00090 
00091             
00092             protobuf::Slot blank_slot;
00093             blank_slot.set_src(acomms::BROADCAST_ID);
00094             blank_slot.set_dest(acomms::QUERY_DESTINATION_ID);
00095             blank_slot.set_rate(cfg_.rate());
00096             blank_slot.set_type(protobuf::SLOT_DATA);
00097             blank_slot.set_slot_seconds(cfg_.slot_seconds());
00098             blank_slot.set_last_heard_time(as<std::string>(goby_time()));
00099             blank_it_ = add_slot(blank_slot);
00100             
00101 
00102             protobuf::Slot our_slot;
00103             our_slot.set_src(cfg_.modem_id());
00104             our_slot.set_dest(acomms::QUERY_DESTINATION_ID);
00105             our_slot.set_rate(cfg_.rate());
00106             our_slot.set_type(protobuf::SLOT_DATA);
00107             our_slot.set_slot_seconds(cfg_.slot_seconds());
00108             our_slot.set_last_heard_time(as<std::string>(goby_time()));
00109 
00110             add_slot(our_slot);
00111             
00112             slot_order_.sort();
00113 
00114             next_slot_t_ = next_cycle_time();
00115             position_blank();
00116             
00117             break;
00118         }
00119         
00120         case protobuf::MAC_POLLED:
00121         case protobuf::MAC_FIXED_DECENTRALIZED:
00122             for(int i = 0, n = cfg_.slot_size(); i < n; ++i)
00123                 add_slot(cfg_.slot(i));
00124             
00125             if(log_ && cfg_.type() == protobuf::MAC_POLLED)
00126                 *log_ << group("mac") << "Using the Centralized Polling MAC scheme" << std::endl;
00127             else if(log_ && cfg_.type() == protobuf::MAC_FIXED_DECENTRALIZED)
00128                 *log_ << group("mac") << "Using the Decentralized (Fixed) Slotted TDMA MAC scheme" << std::endl;
00129 
00130             break;
00131 
00132         default:
00133             return;
00134     }
00135 
00136     if(log_) *log_ << group("mac")
00137                  << "the MAC TDMA first cycle begins at time: "
00138                  << next_slot_t_ << std::endl;
00139     
00140     
00141     if(!slot_order_.empty())
00142         restart_timer();
00143 
00144     startup_done_ = true;
00145 }
00146 
00147 void goby::acomms::MACManager::shutdown()
00148 {
00149     stop_timer();
00150     
00151     slot_order_.clear();
00152     id2slot_.clear();
00153     current_slot_ = slot_order_.begin();
00154     startup_done_ = false;
00155 }
00156 
00157 
00158 void goby::acomms::MACManager::send_poll(const boost::system::error_code& e)
00159 {    
00160     // canceled the last timer
00161     if(e == boost::asio::error::operation_aborted) return;   
00162     
00163     const protobuf::Slot& s = (*current_slot_)->second;
00164     
00165     bool send_poll = true;    
00166     switch(cfg_.type())
00167     {
00168         case protobuf::MAC_FIXED_DECENTRALIZED:
00169         case protobuf::MAC_AUTO_DECENTRALIZED:
00170             send_poll = (s.src() == cfg_.modem_id());
00171             break;
00172 
00173         case protobuf::MAC_POLLED:
00174             // be quiet in the case where src = 0
00175             send_poll = (s.src() != BROADCAST_ID);
00176             break;
00177 
00178         default:
00179             break;
00180     }
00181 
00182     if(log_)
00183     {
00184         *log_ << group("mac") << "cycle order: [";
00185     
00186         BOOST_FOREACH(id2slot_it it, slot_order_)
00187         {
00188             if(it==(*current_slot_))
00189                 *log_ << " " << green;
00190             
00191             switch(it->second.type())
00192             {
00193                 case protobuf::SLOT_DATA: *log_ << "d"; break;
00194                 case protobuf::SLOT_PING: *log_ << "p"; break;
00195                 case protobuf::SLOT_REMUS_LBL: *log_ << "r"; break; 
00196             }
00197 
00198             *log_ << it->second.src() << "/" << it->second.dest() << "@" << it->second.rate() << " " << nocolor;
00199         }
00200 
00201         *log_ << " ]" << std::endl;
00202 
00203         *log_ << group("mac") << "starting slot: " << s << std::endl;
00204     }
00205 
00206     
00207     if(send_poll)
00208     {
00209         switch(s.type())
00210         {
00211             case protobuf::SLOT_DATA:
00212             {
00213                 protobuf::ModemMsgBase m;
00214                 m.set_src(s.src());
00215                 m.set_dest(s.dest());
00216                 m.set_rate(s.rate());
00217                 signal_initiate_transmission(&m);
00218                 break;
00219             }
00220             
00221             case protobuf::SLOT_REMUS_LBL:
00222             case protobuf::SLOT_PING:
00223             {
00224                 protobuf::ModemRangingRequest m;
00225                 m.mutable_base()->set_src(s.src());
00226                 m.mutable_base()->set_dest(s.dest());
00227 
00228                 if(s.type() == protobuf::SLOT_REMUS_LBL)
00229                     m.set_type(protobuf::REMUS_LBL_RANGING);
00230                 else if(s.type() == protobuf::SLOT_PING)
00231                     m.set_type(protobuf::MODEM_TWO_WAY_PING);
00232                 
00233                 signal_initiate_ranging(&m);
00234                 break;
00235             }            
00236             default:
00237                 break;
00238         }
00239     }
00240     
00241     ++current_slot_;
00242     
00243     switch(cfg_.type())
00244     {
00245         case protobuf::MAC_AUTO_DECENTRALIZED:
00246             expire_ids();
00247 
00248             if (current_slot_ == slot_order_.end())
00249             {
00250                 ++cycles_since_day_start_;
00251                 if(log_) *log_ << group("mac") << "cycles since day start: "
00252                              << cycles_since_day_start_ << std::endl;    
00253                 position_blank();
00254             }
00255             next_slot_t_ += boost::posix_time::seconds(cfg_.slot_seconds());
00256             break;
00257             
00258         case protobuf::MAC_FIXED_DECENTRALIZED:
00259         case protobuf::MAC_POLLED:
00260             if (current_slot_ == slot_order_.end()) current_slot_ = slot_order_.begin();
00261             next_slot_t_ += boost::posix_time::seconds(s.slot_seconds());
00262             break;
00263 
00264         default:
00265             break;
00266  
00267     }
00268     
00269     restart_timer();
00270 }
00271 
00272 boost::posix_time::ptime goby::acomms::MACManager::next_cycle_time()
00273 {
00274     using namespace boost::gregorian;
00275     using namespace boost::posix_time;
00276 
00277     int since_day_start = goby_time().time_of_day().total_seconds();
00278     cycles_since_day_start_ = (floor(since_day_start/cycle_length()) + 1);
00279     
00280     if(log_) *log_ << group("mac") << "cycles since day start: "
00281                  << cycles_since_day_start_ << std::endl;
00282     
00283     unsigned secs_to_next = cycles_since_day_start_*cycle_length();
00284 
00285     
00286     // day start plus the next cycle starting from now
00287     return ptime(day_clock::universal_day(), seconds(secs_to_next));
00288 }
00289 
00290 void goby::acomms::MACManager::handle_modem_all_incoming(const protobuf::ModemMsgBase& m)
00291 {
00292     unsigned id = m.src();
00293     
00294     if(cfg_.type() != protobuf::MAC_AUTO_DECENTRALIZED)
00295         return;
00296     
00297 
00298 // if we haven't heard from this id before we have to reset (since the cycle changed
00299     bool new_id = !id2slot_.count(id);
00300     
00301     if(new_id)
00302     {
00303         if(log_) *log_ << group("mac") << "discovered id " << id << std::endl;
00304         
00305         protobuf::Slot new_slot;
00306         
00307         new_slot.set_src(id);
00308         new_slot.set_dest(acomms::QUERY_DESTINATION_ID);
00309         new_slot.set_rate(cfg_.rate());
00310         new_slot.set_type(protobuf::SLOT_DATA);
00311         new_slot.set_slot_seconds(cfg_.slot_seconds());
00312         new_slot.set_last_heard_time(as<std::string>(goby_time()));
00313 
00314         slot_order_.push_back(id2slot_.insert(std::make_pair(id, new_slot)));
00315 
00316         slot_order_.sort();
00317 
00318         process_cycle_size_change();
00319     }
00320     else
00321     {
00322         std::pair<id2slot_it, id2slot_it> p = id2slot_.equal_range(id);
00323         for(id2slot_it it = p.first; it != p.second; ++it)
00324             it->second.set_last_heard_time(as<std::string>(goby_time()));
00325     }
00326 }
00327 
00328 void goby::acomms::MACManager::expire_ids()
00329 {
00330     bool reset = false;
00331     
00332     for(id2slot_it it = id2slot_.begin(), n = id2slot_.end(); it != n; ++it)
00333     {
00334         if(as<boost::posix_time::ptime>(it->second.last_heard_time()) <
00335            goby_time()-boost::posix_time::seconds(cycle_length()*cfg_.expire_cycles())
00336            && it->first != cfg_.modem_id()
00337            && it->first != BROADCAST_ID)
00338         {
00339             if(log_) *log_ << group("mac") << "removed id " << it->first
00340                            << " after not hearing for " << cfg_.expire_cycles()
00341                            << " cycles." << std::endl;
00342 
00343             id2slot_.erase(it);
00344             slot_order_.remove(it);
00345             reset = true;
00346         }
00347     }
00348 
00349     if(reset) process_cycle_size_change();
00350 }
00351 
00352 void goby::acomms::MACManager::process_cycle_size_change()
00353 {
00354     next_slot_t_ = next_cycle_time();
00355     if(log_) *log_ << group("mac") << "the MAC TDMA next cycle begins at time: "
00356                  << next_slot_t_ << std::endl;
00357     
00358     
00359     if(cfg_.type() == protobuf::MAC_AUTO_DECENTRALIZED && slot_order_.size() > 1)
00360         position_blank();
00361   
00362     restart_timer();
00363 }
00364 
00365 
00366 unsigned goby::acomms::MACManager::cycle_sum()
00367 {
00368     unsigned s = 0;
00369     BOOST_FOREACH(id2slot_it it, slot_order_)
00370         s += it->second.src();
00371     return s;
00372 }
00373 
00374 unsigned goby::acomms::MACManager::cycle_length()
00375 {
00376     unsigned length = 0;
00377     BOOST_FOREACH(const id2slot_it& it, slot_order_)
00378         length += it->second.slot_seconds();
00379 
00380     
00381     return length;
00382 }
00383  
00384 
00385 void goby::acomms::MACManager::position_blank()
00386 {
00387     unsigned blank_pos = cycle_length() - ((cycles_since_day_start_ % ENTROPY) == (cycle_sum() % ENTROPY)) - 1;
00388     
00389     slot_order_.remove(blank_it_);
00390     
00391     std::list<id2slot_it>::iterator id_it = slot_order_.begin();
00392     for(unsigned i = 0; i < blank_pos; ++i)
00393         ++id_it;
00394 
00395     slot_order_.insert(id_it, blank_it_);
00396     
00397     current_slot_ = slot_order_.begin();
00398 }
00399 
00400 std::map<int, goby::acomms::protobuf::Slot>::iterator goby::acomms::MACManager::add_slot(const protobuf::Slot& s)    
00401 {
00402     if(!s.IsInitialized())
00403     {
00404         if(log_) *log_ << group("mac") << warn << "ignoring invalid Slot: " << s << std::endl;
00405         return id2slot_.begin();
00406     }
00407         
00408     std::map<int, protobuf::Slot>::iterator it =
00409         id2slot_.insert(std::pair<int, protobuf::Slot>(s.src(), s));
00410 
00411     slot_order_.push_back(it);
00412     current_slot_ = slot_order_.begin();
00413     
00414     if(log_) *log_ << group("mac") << "added new slot " << s << std::endl;
00415     process_cycle_size_change();
00416     
00417     return it;
00418 }
00419 
00420 void goby::acomms::MACManager::add_flex_groups(util::FlexOstream* tout)
00421 {
00422     tout->add_group("mac", util::Colors::blue, "MAC related messages (goby_amac)");
00423 }
00424 
00425 
00426 bool goby::acomms::MACManager::remove_slot(const protobuf::Slot& s)    
00427 {
00428     if(!s.IsInitialized())
00429     {
00430         if(log_) *log_ << group("mac") << warn << "ignoring invalid Slot: " << s << std::endl;
00431         return false; 
00432     }
00433 
00434     bool removed_a_slot = false;
00435     
00436     for(id2slot_it it = id2slot_.begin(), n = id2slot_.end(); it != n; ++it)
00437     {
00438         if(s == it->second)
00439         {
00440             if(log_) *log_ << group("mac") << "removed slot " << it->second << std::endl;
00441             slot_order_.remove(it);
00442             id2slot_.erase(it);
00443             removed_a_slot = true;
00444             break;
00445         }
00446     }
00447 
00448     
00449     if(slot_order_.empty())
00450         stop_timer();
00451     else
00452         process_cycle_size_change();
00453 
00454     if(removed_a_slot)
00455         current_slot_ = slot_order_.begin();
00456         
00457     return removed_a_slot;
00458 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends