Goby Underwater Autonomy Project
Series: 1.1, revision: 163, released on 2013-02-06 14:23:27 -0500
|
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 }