X-Git-Url: http://git.tdb.fi/?a=blobdiff_plain;ds=sidebyside;f=source%2Flibr2c2%2Ftrain.cpp;h=599f2dcdf28b27d92a1401b9fe025d701c4949d1;hb=4086edbfae9a04da491cdb349a3bef6af5c132d9;hp=dd7139f864824c7aac2e80b1eea4f8c40691c6ab;hpb=1ff06c5bc46a677fa389ef86c6b26664368f1653;p=r2c2.git diff --git a/source/libr2c2/train.cpp b/source/libr2c2/train.cpp index dd7139f..599f2dc 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,12 +10,14 @@ 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" #include "train.h" #include "vehicle.h" #include "vehicletype.h" +#include "zone.h" using namespace std; using namespace Msp; @@ -47,39 +44,42 @@ Train::Train(Layout &l, const VehicleType &t, unsigned a, const string &p): protocol(p), priority(0), yielding_to(0), + preceding_train(0), cur_blocks_end(blocks.end()), clear_blocks_end(blocks.end()), 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)); layout.add_train(*this); - layout.get_driver().add_loco(address, protocol); + layout.get_driver().add_loco(address, protocol, loco_type); layout.get_driver().signal_loco_speed.connect(sigc::mem_fun(this, &Train::loco_speed_event)); 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)); @@ -94,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); @@ -127,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]; } @@ -165,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) @@ -174,20 +173,14 @@ 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"); - if(func<5) - layout.get_driver().set_loco_function(address, func, state); - else - layout.get_driver().set_loco_function(address+1, func-4, state); + throw invalid_argument("Train::set_function"); + layout.get_driver().set_loco_function(address, func, state); } float Train::get_control(const string &ctrl) const @@ -200,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) @@ -266,10 +289,39 @@ bool Train::go_to(Track &to) return set_route(route); } +bool Train::go_to(const Zone &to) +{ + set tracks; + for(BlockList::const_iterator i=blocks.begin(); i!=blocks.end(); ++i) + tracks.insert((*i)->get_tracks().begin(), (*i)->get_tracks().end()); + + const Zone::TrackSet &ztracks = to.get_tracks(); + unsigned union_size = 0; + for(Zone::TrackSet::const_iterator i=ztracks.begin(); i!=ztracks.end(); ++i) + union_size += tracks.count(*i); + + if(union_size==tracks.size() || union_size==ztracks.size()) + { + signal_arrived.emit(); + return set_route(0); + } + + free_noncritical_blocks(); + + TrackIter next = blocks.back().next().track_iter(); + + Route *route = Route::find(next, to); + if(!route) + return false; + create_lead_route(route, route); + route->add_tracks(ztracks); + return set_route(route); +} + 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; @@ -343,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)); } @@ -371,20 +423,20 @@ 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(); set_active(false); accurate_position = false; + blocks.push_back(BlockIter(&block, entry)); if(!block.reserve(this)) { - set_status("Unplaced"); + blocks.pop_back(); return; } - blocks.push_back(BlockIter(&block, entry)); if(reverse) { TrackIter track = BlockIter(&block, entry).reverse().track_iter(); @@ -400,7 +452,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(); @@ -409,14 +461,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; @@ -496,7 +545,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) @@ -506,7 +555,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) @@ -519,36 +576,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); @@ -560,11 +622,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); @@ -593,8 +651,9 @@ void Train::tick(const Time::TimeStamp &t, const Time::TimeDelta &dt) if(dist>10*layout.get_catalogue().get_scale()) { - blocks.front()->reserve(0); + Block &block = *blocks.front(); blocks.pop_front(); + block.reserve(0); } } } @@ -609,9 +668,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()) { @@ -634,12 +696,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) @@ -647,11 +711,16 @@ void Train::control_changed(const Controller::Control &ctrl) signal_control_changed.emit(ctrl.name, ctrl.value); } -void Train::loco_speed_event(unsigned addr, unsigned speed, bool) +void Train::loco_speed_event(unsigned addr, unsigned speed, bool rev) { if(addr==address) { current_speed_step = speed; + 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; } @@ -659,10 +728,8 @@ void Train::loco_speed_event(unsigned addr, unsigned speed, bool) void Train::loco_func_event(unsigned addr, unsigned func, bool state) { - if(addr==address || (addr==address+1 && loco_type.get_max_function()>4)) + if(addr==address) { - if(addr==address+1) - func += 4; if(state) functions |= 1<get_sensor_id()) { - if((*end)->get_sensor_id()!=addr) + if(&**end!=&block) { if(result==0) result = 2; @@ -700,22 +767,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) @@ -747,6 +807,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 @@ -756,7 +818,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()); @@ -811,6 +873,7 @@ void Train::reserve_more() BlockIter start = blocks.back(); pending_block = 0; + preceding_train = 0; // See how many sensor blocks and how much track we already have unsigned nsens = 0; @@ -845,7 +908,10 @@ void Train::reserve_more() if(!block || block->get_endpoints().size()<2) { if(!blocking_train) + { good_end = blocks.end(); + end_of_route = true; + } break; } @@ -905,9 +971,11 @@ void Train::reserve_more() } } + blocks.push_back(block); bool reserved = block->reserve(this); if(!reserved) { + blocks.pop_back(); /* We've found another train. If it wants to exit the block from the same endpoint we're trying to enter from or the other way around, treat it as coming towards us. Otherwise treat it as going in the @@ -915,17 +983,22 @@ 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(); bool entry_conflict = (block.entry()==other_exit); bool exit_conflict = (exit==static_cast(other_entry)); if(!entry_conflict && !last->get_turnout_id()) + { /* The other train is not coming to the blocks we're holding, so we can keep them. */ good_end = blocks.end(); + if(static_cast(other_entry)==block.entry()) + preceding_train = other_train; + } + int other_prio = other_train->get_priority(); if(!entry_conflict && !exit_conflict && other_priofree_block(*block)) - reserved = block->reserve(this); + { + blocks.push_back(block); + if(!(reserved = block->reserve(this))) + blocks.pop_back(); + } } else if(other_train!=yielding_to && (other_priois_active())) // We are blocked, but there's a diversion possibility try_divert = true; @@ -969,8 +1046,6 @@ void Train::reserve_more() if(!contested_blocks.empty() && contested_blocks.front()==block) contested_blocks.pop_front(); - blocks.push_back(block); - if(cur_blocks_end==blocks.end()) --cur_blocks_end; if(clear_blocks_end==blocks.end()) @@ -1038,6 +1113,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; } } @@ -1097,87 +1174,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(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()); @@ -1257,7 +1253,7 @@ bool Train::is_valid_diversion(const Route &diversion, const TrackIter &from) track1 = track1.next(path); - if(track1.looped()) + if(!track1 || track1.looped()) return false; } @@ -1275,6 +1271,8 @@ bool Train::is_valid_diversion(const Route &diversion, const TrackIter &from) bool ok = (track2!=from && diversion.has_track(*track2)); track2 = track2.next(path); + if(!track2) + return false; if(ok) break; @@ -1300,20 +1298,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) { @@ -1321,7 +1307,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); @@ -1334,8 +1320,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"); } } @@ -1349,7 +1333,7 @@ void Train::Loader::block(unsigned id) { blk = &obj.layout.get_block(id); } - catch(const KeyError &) + catch(const key_error &) { blocks_valid = false; return; @@ -1361,8 +1345,8 @@ void Train::Loader::block(unsigned id) if(entry<0) entry = 0; - blk->reserve(&obj); obj.blocks.push_back(BlockIter(blk, entry)); + blk->reserve(&obj); if(blk->get_sensor_id()) obj.layout.get_driver().set_sensor(blk->get_sensor_id(), true); @@ -1376,7 +1360,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; } @@ -1387,12 +1371,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) @@ -1402,11 +1384,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)