X-Git-Url: http://git.tdb.fi/?a=blobdiff_plain;f=source%2Flibr2c2%2Ftrain.cpp;h=936f74aa82d6d522bb74a7d500a59eeecea0f2d2;hb=e6b49ce4c5833ab90733aee8e42413f7c75d9da7;hp=218b6861da183b0d24c3279046c4dc122aaae9ba;hpb=b41c7409da959420aed91e8a2e1fdab95ef473c4;p=r2c2.git diff --git a/source/libr2c2/train.cpp b/source/libr2c2/train.cpp index 218b686..936f74a 100644 --- a/source/libr2c2/train.cpp +++ b/source/libr2c2/train.cpp @@ -1,12 +1,7 @@ -/* $Id$ - -This file is part of R²C² -Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa -Distributed under the GPL -*/ - +#include #include -#include +#include +#include #include #include #include "aicontrol.h" @@ -15,6 +10,7 @@ Distributed under the GPL #include "layout.h" #include "route.h" #include "simplecontroller.h" +#include "speedquantizer.h" #include "timetable.h" #include "trackiter.h" #include "tracktype.h" @@ -54,23 +50,25 @@ Train::Train(Layout &l, const VehicleType &t, unsigned a, const string &p): pending_block(0), reserving(false), advancing(false), - controller(new AIControl(*this, new SimpleController)), - timetable(0), + controller(new SimpleController), active(false), current_speed_step(0), speed_changing(false), reverse(false), functions(0), end_of_route(false), - status("Unplaced"), travel_dist(0), pure_speed(false), - real_speed(layout.get_driver().get_protocol_speed_steps(protocol)+1), + speed_quantizer(0), accurate_position(false), overshoot_dist(false) { if(!loco_type.is_locomotive()) - throw InvalidParameterValue("Initial vehicle must be a locomotive"); + throw invalid_argument("Train::Train"); + + unsigned speed_steps = layout.get_driver().get_protocol_speed_steps(protocol); + if(speed_steps) + speed_quantizer = new SpeedQuantizer(speed_steps); vehicles.push_back(new Vehicle(layout, loco_type)); @@ -81,7 +79,7 @@ Train::Train(Layout &l, const VehicleType &t, unsigned a, const string &p): layout.get_driver().signal_loco_function.connect(sigc::mem_fun(this, &Train::loco_func_event)); layout.signal_block_reserved.connect(sigc::mem_fun(this, &Train::block_reserved)); - layout.get_driver().signal_sensor.connect(sigc::mem_fun(this, &Train::sensor_event)); + layout.signal_block_state_changed.connect(sigc::mem_fun(this, &Train::block_state_changed)); layout.get_driver().signal_halt.connect(sigc::mem_fun(this, &Train::halt_event)); @@ -96,7 +94,6 @@ Train::Train(Layout &l, const VehicleType &t, unsigned a, const string &p): Train::~Train() { delete controller; - delete timetable; for(vector::iterator i=vehicles.begin(); i!=vehicles.end(); ++i) delete *i; layout.remove_train(*this); @@ -129,9 +126,9 @@ void Train::add_vehicle(const VehicleType &vt) void Train::remove_vehicle(unsigned i) { if(i>=vehicles.size()) - throw InvalidParameterValue("Vehicle index out of range"); + throw out_of_range("Train::remove_vehicle"); if(i==0) - throw InvalidParameterValue("Can't remove the locomotive"); + throw logic_error("can't remove locomotive"); delete vehicles[i]; vehicles.erase(vehicles.begin()+i); if(i=vehicles.size()) - throw InvalidParameterValue("Vehicle index out of range"); + throw out_of_range("Train::get_vehicle"); return *vehicles[i]; } const Vehicle &Train::get_vehicle(unsigned i) const { if(i>=vehicles.size()) - throw InvalidParameterValue("Vehicle index out of range"); + throw out_of_range("Train::get_vehicle"); return *vehicles[i]; } @@ -167,7 +164,7 @@ void Train::set_active(bool a) if(a==active) return; if(!a && controller->get_speed()) - throw InvalidState("Can't deactivate while moving"); + throw logic_error("moving"); active = a; if(active) @@ -176,16 +173,13 @@ void Train::set_active(bool a) reserve_more(); } else - { stop_timeout = Time::now()+2*Time::sec; - set_status("Stopped"); - } } void Train::set_function(unsigned func, bool state) { if(!loco_type.get_functions().count(func)) - throw InvalidParameterValue("Invalid function"); + throw invalid_argument("Train::set_function"); layout.get_driver().set_loco_function(address, func, state); } @@ -199,15 +193,45 @@ float Train::get_speed() const return controller->get_speed(); } +float Train::get_quantized_speed() const +{ + if(speed_quantizer) + return speed_quantizer->quantize_speed(controller->get_speed()); + else + return controller->get_speed(); +} + bool Train::get_function(unsigned func) const { return (functions>>func)&1; } -void Train::set_timetable(Timetable *tt) +void Train::add_ai(TrainAI &ai) +{ + ais.push_back(&ai); + ai.signal_event.connect(sigc::bind<0>(signal_ai_event, sigc::ref(ai))); +} + +void Train::remove_ai(TrainAI &ai) { - delete timetable; - timetable = tt; + list::iterator i = find(ais.begin(), ais.end(), &ai); + if(i!=ais.end()) + ais.erase(i); +} + +TrainAI *Train::get_tagged_ai(const string &tag) const +{ + for(list::const_iterator i=ais.begin(); i!=ais.end(); ++i) + if((*i)->get_tag()==tag) + return *i; + + return 0; +} + +void Train::ai_message(const TrainAI::Message &msg) +{ + for(list::iterator i=ais.begin(); i!=ais.end(); ++i) + (*i)->message(msg); } bool Train::set_route(const Route *r) @@ -297,7 +321,7 @@ bool Train::go_to(const Zone &to) bool Train::divert(Track &from) { if(!from.get_turnout_id()) - throw InvalidParameterValue("Can't divert from a non-turnout"); + throw invalid_argument("Train::divert"); if(routes.empty()) return false; @@ -371,7 +395,7 @@ bool Train::divert(Track &from) if(end!=routes.end()) break; else if(!diversion->has_track(*track)) - throw LogicError("Pathfinder returned a bad route"); + throw logic_error("bad diversion"); track = track.next(diversion->get_path(*track)); } @@ -399,7 +423,7 @@ const Route *Train::get_route() const void Train::place(Block &block, unsigned entry) { if(controller->get_speed()) - throw InvalidState("Must be stopped before placing"); + throw logic_error("moving"); release_blocks(); @@ -407,10 +431,7 @@ void Train::place(Block &block, unsigned entry) accurate_position = false; if(!block.reserve(this)) - { - set_status("Unplaced"); return; - } blocks.push_back(BlockIter(&block, entry)); if(reverse) @@ -428,7 +449,7 @@ void Train::place(Block &block, unsigned entry) void Train::unplace() { if(controller->get_speed()) - throw InvalidState("Must be stopped before unplacing"); + throw logic_error("moving"); release_blocks(); @@ -437,14 +458,11 @@ void Train::unplace() for(vector::iterator i=vehicles.begin(); i!=vehicles.end(); ++i) (*i)->unplace(); - - set_status("Unplaced"); } bool Train::free_block(Block &block) { - float margin = 10*layout.get_catalogue().get_scale(); - if(get_reserved_distance_until(&block, false)get_braking_distance()*1.3+margin) + if(get_reserved_distance_until(&block, false)get_braking_distance()*1.3) return false; unsigned nsens = 0; @@ -524,7 +542,7 @@ void Train::free_noncritical_blocks() } } -int Train::get_entry_to_block(Block &block) const +int Train::get_entry_to_block(const Block &block) const { for(BlockList::const_iterator i=blocks.begin(); i!=blocks.end(); ++i) if(i->block()==&block) @@ -534,7 +552,15 @@ int Train::get_entry_to_block(Block &block) const float Train::get_reserved_distance() const { - return get_reserved_distance_until(0, false); + if(blocks.empty()) + return 0; + + float margin = 0; + TrackIter next = blocks.back().next().track_iter(); + if(next && next->get_type().is_turnout()) + margin = 15*layout.get_catalogue().get_scale(); + + return max(get_reserved_distance_until(0, false)-margin, 0.0f); } void Train::tick(const Time::TimeStamp &t, const Time::TimeDelta &dt) @@ -547,36 +573,41 @@ void Train::tick(const Time::TimeStamp &t, const Time::TimeDelta &dt) Driver &driver = layout.get_driver(); - if(timetable) - timetable->tick(t); + for(list::iterator i=ais.begin(); i!=ais.end(); ++i) + (*i)->tick(t, dt); controller->tick(dt); float speed = controller->get_speed(); - unsigned speed_step = find_speed_step(speed); + bool moving = speed>0; if(controller->get_reverse()!=reverse) { reverse = controller->get_reverse(); - driver.set_loco_reverse(address, reverse); + bool r = reverse; + if(loco_type.get_swap_direction()) + r = !r; + driver.set_loco_reverse(address, r); release_blocks(cur_blocks_end, blocks.end()); reverse_blocks(blocks); reserve_more(); } - if(speed_step!=current_speed_step && !speed_changing && !driver.is_halted() && driver.get_power()) + + if(speed_quantizer) { - speed_changing = true; - driver.set_loco_speed(address, speed_step); + unsigned speed_step = speed_quantizer->find_speed_step(speed); + if(speed_step!=current_speed_step && !speed_changing && !driver.is_halted() && driver.get_power()) + { + speed_changing = true; + driver.set_loco_speed(address, speed_step); - pure_speed = false; + pure_speed = false; + } - if(speed_step) - set_status(format("Traveling %d kmh", get_travel_speed())); - else - set_status("Waiting"); + speed = speed_quantizer->get_speed(current_speed_step); } - if(speed) + if(moving) { if(!active) set_active(true); @@ -588,11 +619,7 @@ void Train::tick(const Time::TimeStamp &t, const Time::TimeDelta &dt) for(BlockList::const_iterator i=blocks.begin(); (!ok && i!=cur_blocks_end); ++i) ok = (*i)->has_track(*track); - float d; - if(real_speed.size()>1) - d = get_real_speed(current_speed_step)*(dt/Time::sec); - else - d = speed*(dt/Time::sec); + float d = speed*(dt/Time::sec); if(ok) { SetFlag setf(advancing); @@ -637,9 +664,12 @@ void Train::save(list &st) const if(i!=vehicles.begin()) st.push_back((DataFile::Statement("vehicle"), (*i)->get_type().get_article_number())); - for(unsigned i=0; isave(ss.sub); + st.push_back(ss); + } if(!blocks.empty() && cur_blocks_end!=blocks.begin()) { @@ -662,12 +692,14 @@ void Train::save(list &st) const st.push_back((DataFile::Statement("route"), i->route->get_name())); } - if(timetable) - { - DataFile::Statement ss("timetable"); - timetable->save(ss.sub); - st.push_back(ss); - } + // XXX Need more generic way of saving AI state + for(list::const_iterator i=ais.begin(); i!=ais.end(); ++i) + if(Timetable *timetable = dynamic_cast(*i)) + { + DataFile::Statement ss("timetable"); + timetable->save(ss.sub); + st.push_back(ss); + } } void Train::control_changed(const Controller::Control &ctrl) @@ -680,8 +712,11 @@ void Train::loco_speed_event(unsigned addr, unsigned speed, bool rev) if(addr==address) { current_speed_step = speed; - if(rev!=reverse) - layout.get_driver().set_loco_reverse(address, reverse); + bool r = reverse; + if(loco_type.get_swap_direction()) + r = !r; + if(rev!=r) + layout.get_driver().set_loco_reverse(address, r); speed_changing = false; pure_speed = false; } @@ -700,9 +735,9 @@ void Train::loco_func_event(unsigned addr, unsigned func, bool state) } } -void Train::sensor_event(unsigned addr, bool state) +void Train::block_state_changed(Block &block, Block::State state) { - if(state) + if(state==Block::MAYBE_ACTIVE) { // Find the first sensor block from our reserved blocks that isn't this sensor BlockList::iterator end; @@ -710,7 +745,7 @@ void Train::sensor_event(unsigned addr, bool state) for(end=cur_blocks_end; end!=blocks.end(); ++end) if((*end)->get_sensor_id()) { - if((*end)->get_sensor_id()!=addr) + if(&**end!=&block) { if(result==0) result = 2; @@ -728,22 +763,15 @@ void Train::sensor_event(unsigned addr, bool state) // Compute speed and update related state float travel_time_secs = (Time::now()-last_entry_time)/Time::sec; - if(pure_speed) - { - if(current_speed_step>0) - { - RealSpeed &rs = real_speed[current_speed_step]; - rs.add(travel_dist/travel_time_secs, travel_time_secs); - } - set_status(format("Traveling %d kmh", get_travel_speed())); - } + if(pure_speed && speed_quantizer && current_speed_step>0 && travel_time_secs>=2) + speed_quantizer->learn(current_speed_step, travel_dist/travel_time_secs, travel_time_secs); travel_dist = 0; for(BlockList::iterator j=cur_blocks_end; j!=end; ++j) { travel_dist += (*j)->get_path_length(j->entry()); - if((*j)->get_sensor_id()==addr && !advancing) + if(&**j==&block && !advancing) { TrackIter track = j->track_iter(); if(reverse) @@ -775,6 +803,8 @@ void Train::sensor_event(unsigned addr, bool state) } // Move blocks up to the next sensor to our current blocks + for(BlockList::iterator j=cur_blocks_end; j!=end; ++j) + signal_advanced.emit(**j); cur_blocks_end = end; // Try to get more blocks if we're moving @@ -784,7 +814,7 @@ void Train::sensor_event(unsigned addr, bool state) else if(result==3) layout.emergency("Sensor for "+name+" triggered out of order"); } - else + else if(state==Block::INACTIVE) { const Vehicle &veh = *(reverse ? vehicles.front() : vehicles.back()); @@ -947,7 +977,7 @@ void Train::reserve_more() Train *other_train = block->get_train(); int other_entry = other_train->get_entry_to_block(*block); if(other_entry<0) - throw LogicError("Block reservation inconsistency"); + throw logic_error("block reservation inconsistency"); unsigned exit = block.reverse().entry(); unsigned other_exit = BlockIter(block.block(), other_entry).reverse().entry(); @@ -1075,6 +1105,8 @@ void Train::check_turnout_paths(bool set) if(i==clear_blocks_end) ++clear_blocks_end; + if(i==cur_blocks_end && !(*i)->get_sensor_id()) + ++cur_blocks_end; } } @@ -1134,87 +1166,6 @@ float Train::get_reserved_distance_until(const Block *until_block, bool back) co return result; } -float Train::get_real_speed(unsigned i) const -{ - if(i==0) - return 0; - if(real_speed[i].weight) - return real_speed[i].speed; - - unsigned low; - unsigned high; - for(low=i; low>0; --low) - if(real_speed[low].weight) - break; - for(high=i; high+1=real) - high = i; - else if(real_speed[i].speed>real_speed[low].speed) - low = i; - } - if(!high) - { - unsigned limit = real_speed.size()/5; - if(!low) - { - if(real) - return limit; - else - return 0; - } - return min(min(static_cast(low*real/real_speed[low].speed), real_speed.size()-1), last+limit); - } - - float f = (real-real_speed[low].speed)/(real_speed[high].speed-real_speed[low].speed); - return static_cast(low*(1-f)+high*f+0.5); -} - -float Train::get_travel_speed() const -{ - float speed = get_real_speed(current_speed_step); - float scale = layout.get_catalogue().get_scale(); - return static_cast(round(speed/scale*3.6/5))*5; -} - -void Train::set_status(const string &s) -{ - status = s; - signal_status_changed.emit(s); -} - void Train::release_blocks() { release_blocks(blocks.begin(), blocks.end()); @@ -1339,20 +1290,8 @@ Train::RouteRef::RouteRef(const Route *r, unsigned d): { } -Train::RealSpeed::RealSpeed(): - speed(0), - weight(0) -{ } - -void Train::RealSpeed::add(float s, float w) -{ - speed = (speed*weight+s*w)/(weight+w); - weight = min(weight+w, 300.0f); -} - - Train::Loader::Loader(Train &t): - DataFile::BasicLoader(t), + DataFile::ObjectLoader(t), prev_block(0), blocks_valid(true) { @@ -1360,7 +1299,7 @@ Train::Loader::Loader(Train &t): add("block_hint", &Loader::block_hint); add("name", &Loader::name); add("priority", &Train::priority); - add("real_speed", &Loader::real_speed); + add("quantized_speed", &Loader::quantized_speed); add("route", &Loader::route); add("timetable", &Loader::timetable); add("vehicle", &Loader::vehicle); @@ -1373,8 +1312,6 @@ void Train::Loader::finish() TrackIter track = obj.blocks.front().track_iter(); float offset = 2*obj.layout.get_catalogue().get_scale(); obj.vehicles.back()->place(*track, track.entry(), offset, Vehicle::BACK_BUFFER); - - obj.set_status("Stopped"); } } @@ -1388,7 +1325,7 @@ void Train::Loader::block(unsigned id) { blk = &obj.layout.get_block(id); } - catch(const KeyError &) + catch(const key_error &) { blocks_valid = false; return; @@ -1415,7 +1352,7 @@ void Train::Loader::block_hint(unsigned id) { prev_block = &obj.layout.get_block(id); } - catch(const KeyError &) + catch(const key_error &) { blocks_valid = false; } @@ -1426,12 +1363,10 @@ void Train::Loader::name(const string &n) obj.set_name(n); } -void Train::Loader::real_speed(unsigned i, float speed, float weight) +void Train::Loader::quantized_speed() { - if(i>=obj.real_speed.size()) - return; - obj.real_speed[i].speed = speed; - obj.real_speed[i].weight = weight; + if(obj.speed_quantizer) + load_sub(*obj.speed_quantizer); } void Train::Loader::route(const string &n) @@ -1441,11 +1376,8 @@ void Train::Loader::route(const string &n) void Train::Loader::timetable() { - if(obj.timetable) - throw InvalidState("A timetable has already been loaded"); - - obj.timetable = new Timetable(obj); - load_sub(*obj.timetable); + Timetable *ttbl = new Timetable(obj); + load_sub(*ttbl); } void Train::Loader::vehicle(ArticleNumber art_nr)