X-Git-Url: http://git.tdb.fi/?p=r2c2.git;a=blobdiff_plain;f=source%2Flibr2c2%2Ftimetable.cpp;h=5fa36395825d0afdb6dfb92df83e76f97d3cd6cb;hp=4a78bcafadab289f5e2f88a4004d449b4e32fe06;hb=d990c03a06a494ce3596862ce61e2a5684dea7e1;hpb=2c0a478641a10a739557d055f80354a28936ced1 diff --git a/source/libr2c2/timetable.cpp b/source/libr2c2/timetable.cpp index 4a78bca..5fa3639 100644 --- a/source/libr2c2/timetable.cpp +++ b/source/libr2c2/timetable.cpp @@ -1,18 +1,12 @@ -/* $Id$ - -This file is part of R²C² -Copyright © 2010-2011 Mikkosoft Productions, Mikko Rasa -Distributed under the GPL -*/ - -#include -#include -#include "block.h" -#include "catalogue.h" -#include "driver.h" +#include +#include +#include "aicontrol.h" +#include "clock.h" #include "layout.h" #include "timetable.h" #include "train.h" +#include "trainrouter.h" +#include "zone.h" using namespace std; using namespace Msp; @@ -20,452 +14,308 @@ using namespace Msp; namespace R2C2 { Timetable::Timetable(Train &t): - train(t), - enabled(false), - current_row(0), - executing(true), - pending_block(0), - pending_train(0), - arrived(false) + TrainAI(t), + current_row(rows.end()), + update_pending(false), + sync_to_clock(true) { - train.signal_advanced.connect(sigc::mem_fun(this, &Timetable::train_advanced)); - train.signal_arrived.connect(sigc::mem_fun(this, &Timetable::train_arrived)); - Layout &layout = train.get_layout(); - layout.get_driver().signal_sensor.connect(sigc::mem_fun(this, &Timetable::sensor_event)); - layout.signal_block_reserved.connect(sigc::mem_fun(this, &Timetable::block_reserved)); -} - -void Timetable::set_enabled(bool e) -{ - enabled = e; -} + if(!train.get_ai_of_type()) + new AIControl(train); + if(!train.get_ai_of_type()) + new TrainRouter(train); -void Timetable::reset() -{ - current_row = 0; - wait_timeout = Time::TimeStamp(); - pending_block = 0; - executing = true; + train.signal_ai_event.connect(sigc::mem_fun(this, &Timetable::event)); } -void Timetable::clear() +void Timetable::append_row(const Row &r) { - rows.clear(); - reset(); + insert_row(rows.size(), r); } -void Timetable::append(const Row &row) -{ - rows.push_back(row); -} - -void Timetable::insert(unsigned i, const Row &row) +void Timetable::insert_row(unsigned i, const Row &r) { if(i>rows.size()) - throw InvalidParameterValue("Insert position out of range"); + throw out_of_range("Timetable::insert_row"); + + list::iterator j = rows.begin(); + advance(j, i); + j = rows.insert(j, r); + signal_row_added.emit(i, *j); - rows.insert(rows.begin()+i, row); - if(i<=current_row) - ++current_row; + check_update(j); } -const Timetable::Row &Timetable::get_row(unsigned i) const +void Timetable::modify_row(unsigned i, const Row &r) { if(i>=rows.size()) - throw InvalidParameterValue("Row index out of range"); - return rows[i]; + throw out_of_range("Timetable::remove_row"); + + list::iterator j = rows.begin(); + advance(j, i); + *j = r; + signal_row_modified.emit(i, r); + + check_update(j); } -void Timetable::tick(const Time::TimeStamp &t) +void Timetable::remove_row(unsigned i) { - if(rows.empty() || !enabled) - return; - - if(wait_timeout && t>=wait_timeout) - { - wait_timeout = Time::TimeStamp(); - current_row = (current_row+1)%rows.size(); - executing = true; - } + if(i>=rows.size()) + throw out_of_range("Timetable::remove_row"); - if(executing) - { - Row &row = rows[current_row]; - switch(row.type) - { - case GOTO_SENSOR: - arrived = false; - if(!train.go_to(get_sensor(row.get_param(0)))) - set_enabled(false); - break; - case GOTO_ZONE: - arrived = false; - if(!train.go_to(get_zone(row.get_param(0)))) - set_enabled(false); - break; - case TRAVEL_TO: - pending_block = &get_sensor(row.get_param(0)).get_block(); - pending_train = &train; - executing = false; - break; - case TRAVEL_PAST: - pending_block = &get_turnout(row.get_param(0)).get_block(); - pending_train = (pending_block->get_train()==&train ? 0 : &train); - executing = false; - break; - case WAIT_TIME: - wait_timeout = t+row.get_param(0)*Time::sec; - executing = false; - break; - case WAIT_UNTIL: - { - unsigned unixtime = t.to_unixtime(); - unsigned mod = row.get_param(1); - unsigned secs = ((mod+row.get_param(0))-(unixtime%mod))%mod; - wait_timeout = t+secs*Time::sec; - executing = false; - } - break; - case WAIT_TRAIN: - pending_train = &train.get_layout().get_train(row.get_param(0)); - pending_block = &get_sensor(row.get_param(1)).get_block(); - executing = false; - break; - case ARRIVE: - if(!arrived) - executing = false; - arrived = false; - break; - case SPEED: - if(!arrived) - train.set_control("speed", row.get_param(0)/3.6*train.get_layout().get_catalogue().get_scale()); - break; - case REVERSE: - train.set_control("reverse", !train.get_control("reverse")); - break; - case ROUTE: - if(!train.set_route(&train.get_layout().get_route(row.get_param(0)))) - set_enabled(false); - break; - } + list::iterator j = rows.begin(); + advance(j, i); - if(executing) - current_row = (current_row+1)%rows.size(); - } -} + check_update(j); + if(j==current_row) + --current_row; -void Timetable::save(list &st) const -{ - for(vector::const_iterator i=rows.begin(); i!=rows.end(); ++i) - st.push_back(i->save()); + rows.erase(j); + signal_row_removed.emit(i); } -Track &Timetable::get_sensor(unsigned id) +const Timetable::Row &Timetable::get_row(unsigned i) const { - Block &block = train.get_layout().get_block(id|0x1000); - return **block.get_tracks().begin(); -} + if(i>=rows.size()) + throw out_of_range("Timetable::get_row"); -Track &Timetable::get_turnout(unsigned id) -{ - Block &block = train.get_layout().get_block(id|0x2000); - return **block.get_tracks().begin(); + list::const_iterator j = rows.begin(); + advance(j, i); + return *j; } -Zone &Timetable::get_zone(const string &name) +void Timetable::tick(const Time::TimeDelta &dt) { - string::size_type space = name.rfind(' '); - if(space==string::npos || space==0) - throw InvalidParameterValue("Invalid zone name"); - unsigned number = lexical_cast(name.substr(space+1)); - return train.get_layout().get_zone(name.substr(0, space), number); -} + if(update_pending && !train.get_block_allocator().is_active()) + update_route(); -void Timetable::sensor_event(unsigned addr, bool state) -{ - if(pending_block && pending_block->get_train()==pending_train && addr==pending_block->get_sensor_id() && state) + if(current_row->type==DEPART) { - pending_block = 0; - current_row = (current_row+1)%rows.size(); - executing = true; - } -} + const Clock &clock = train.get_layout().get_clock(); -void Timetable::block_reserved(Block &block, Train *trn) -{ - if(&block==pending_block && trn==pending_train) - { - Row &row = rows[current_row]; - if(row.type==TRAVEL_PAST && !pending_train) + Time::TimeDelta t = clock.get_current_time(); + if(ttime) + t += Time::day; + + Time::TimeDelta b = t-dt*clock.get_rate(); + if(btime) { - pending_block = 0; - current_row = (current_row+1)%rows.size(); - executing = true; + train.ai_message(Message("set-target-speed", train.get_maximum_speed())); + ++current_row; } } } -void Timetable::train_advanced(Block &block) -{ - Row &row = rows[current_row]; - if(row.type==TRAVEL_PAST && &block==pending_block && pending_train) - pending_train = 0; -} - -void Timetable::train_arrived() +void Timetable::save(list &st) const { - Row &row = rows[current_row]; - if(row.type==ARRIVE) + for(list::const_iterator i=rows.begin(); i!=rows.end(); ++i) { - current_row = (current_row+1)%rows.size(); - executing = true; + DataFile::Statement ss("row"); + i->save(ss.sub); + st.push_back(ss); } - else - arrived = true; } - -Timetable::Row::Row(RowType t): - type(t) -{ } - -template -Timetable::Row::Row(RowType t, const T &p): - type(t) +void Timetable::check_update(const list::const_iterator &i) { - params.push_back(p); + for(list::const_iterator j=current_row; (j!=rows.end() && j!=i); ++j) + if(j->type==ARRIVE) + return; + update_pending = true; } -template -const T &Timetable::Row::get_param(unsigned i) const +list::iterator Timetable::find_trip(const list::iterator &begin, list::iterator *arrive) { - if(i>=params.size()) - throw InvalidParameterValue("Parameter index out of range"); - return params[i].value(); -} + list::iterator i = find_if(begin, rows.end(), RowTypeMatch(DEPART)); + if(i==rows.end()) + return i; -string Timetable::Row::str() const -{ - switch(type) - { - case GOTO_SENSOR: - return format("set route to sensor %d", get_param(0)); - case GOTO_ZONE: - return "set route to "+get_param(0); - case TRAVEL_TO: - return format("travel to sensor %d", get_param(0)); - case TRAVEL_PAST: - return format("travel past turnout %d", get_param(0)); - case WAIT_TIME: - return format("wait for %d seconds", get_param(0)); - case WAIT_UNTIL: - return format("wait until %d mod %d seconds", get_param(0), get_param(1)); - case WAIT_TRAIN: - return format("wait for train %d at %s", get_param(0), get_param(1)); - case ARRIVE: - return "travel until arrival"; - case SPEED: - return format("set speed %d km/h", get_param(0)); - case REVERSE: - return "reverse"; - case ROUTE: - return "set route "+get_param(0); - default: - return "invalid row"; - } -} + list::iterator j = find_if(i, rows.end(), RowTypeMatch(ARRIVE)); + if(j==rows.end()) + return j; -DataFile::Statement Timetable::Row::save() const -{ - switch(type) - { - case GOTO_SENSOR: - return DataFile::Statement("goto_sensor"), get_param(0); - case GOTO_ZONE: - return DataFile::Statement("goto_zone"), get_param(0); - case TRAVEL_TO: - return DataFile::Statement("travel_to"), get_param(0); - case TRAVEL_PAST: - return DataFile::Statement("travel_past"), get_param(0); - case WAIT_TIME: - return DataFile::Statement("wait"), get_param(0); - case WAIT_UNTIL: - return DataFile::Statement("wait_until"), get_param(0), get_param(1); - case WAIT_TRAIN: - return DataFile::Statement("wait_train"), get_param(0), get_param(1); - case ARRIVE: - return DataFile::Statement("arrive"); - case SPEED: - return DataFile::Statement("speed"), get_param(0); - case REVERSE: - return DataFile::Statement("reverse"); - case ROUTE: - return DataFile::Statement("route"), get_param(0); - default: - return DataFile::Statement(); - } + if(arrive) + *arrive = j; + return i; } -Timetable::Row Timetable::Row::parse(const string &s) +void Timetable::update_route() { - if(!s.compare(0, 7, "travel ")) + update_pending = false; + if(rows.empty()) + return; + + const Clock &clock = train.get_layout().get_clock(); + + if(sync_to_clock) { - if(!s.compare(7, 10, "to sensor ")) - return Row(TRAVEL_TO, lexical_cast(s.substr(17))); - else if(!s.compare(7, 13, "past turnout ")) - return Row(TRAVEL_PAST, lexical_cast(s.substr(20))); - else if(!s.compare(7, string::npos, "until arrival")) - return Row(ARRIVE); + sync_to_clock = false; + current_row = rows.begin(); + for(list::iterator i=rows.begin(); i!=rows.end(); ++i) + if(i->type==DEPART && i->time>=clock.get_current_time()) + { + current_row = i; + break; + } } - else if(!s.compare(0, 9, "wait for ")) + + list::iterator arrive; + list::iterator depart = find_trip(current_row, &arrive); + if(depart==rows.end()) { - if(isdigit(s[9])) - { - unsigned nondigit = 10; - while(nondigit(s.substr(9, nondigit-9))); - } - else if(!s.compare(9, 6, "train ")) + depart = find_trip(rows.begin(), &arrive); + if(depart==rows.end()) { - string::size_type at = s.find(" at sensor ", 15); - if(at!=string::npos) - { - Row row(WAIT_TRAIN, lexical_cast(s.substr(15, at-15))); - row.params.push_back(lexical_cast(s.substr(at+11))); - return row; - } + current_row = rows.end(); + return; } } - else if(!s.compare(0, 11, "wait until ")) + + train.ai_message(Message("clear-route")); + + current_row = depart; + for(list::const_iterator i=depart; i!=rows.end(); ++i) { - string::size_type mod = s.find(" mod ", 11); - unsigned nondigit = (mod!=string::npos ? mod+5 : 11); - while(nondigittype==DEPART) { - unsigned time = lexical_cast(s.substr(11, mod-11)); - Row row(WAIT_UNTIL, time); - row.params.push_back(lexical_cast(s.substr(mod+5, nondigit-mod-5))); - return row; + Time::TimeDelta dt = i->time-clock.get_current_time(); + while(dt(s.substr(11, nondigit-11)); - Row row(WAIT_UNTIL, time); - row.params.push_back(3600); - return row; + train.ai_message(Message("add-waypoint", TrainRouter::Waypoint(*i->target, i->direction))); + if(i->type==ARRIVE) + break; } } - else if(!s.compare(0, 10, "set speed ")) + + list::iterator next_depart = find_trip(arrive, 0); + if(next_depart==rows.end()) + next_depart = find_trip(rows.begin(), 0); + if(next_depart!=rows.end()) { - unsigned nondigit = 11; - while(nondigit(s.substr(10, nondigit-10))); + Time::TimeDelta dt = next_depart->time-depart->time; + while(dt<=Time::zero) + dt += Time::day; + train.ai_message(Message("set-trip-duration", dt/clock.get_rate())); } - else if(s=="reverse") - return Row(REVERSE); - else if(!s.compare(0, 10, "set route ")) +} + +void Timetable::event(TrainAI &, const Message &msg) +{ + if(msg.type=="arrived") { - if(!s.compare(10, 3, "to ")) + if(current_row->type==ARRIVE) + record_time(); + update_pending = true; + } + else if(msg.type=="waypoint-reached") + { + const TrackChain *wp = msg.value.value(); + if(current_row->type==THROUGH && current_row->target==wp) { - if(!s.compare(13, 7, "sensor ")) - return Row(GOTO_SENSOR, lexical_cast(s.substr(20))); - else - return Row(GOTO_ZONE, s.substr(13)); + record_time(); + ++current_row; } - return Row(ROUTE, s.substr(10)); } - - throw InvalidParameterValue("Invalid row"); } - -Timetable::Loader::Loader(Timetable &tt): - DataFile::ObjectLoader(tt) +void Timetable::record_time() { - add("arrive", &Loader::arrive); - add("goto_sensor", &Loader::goto_sensor); - add("goto_zone", &Loader::goto_zone); - add("route", &Loader::route); - add("speed", &Loader::speed); - add("reverse", &Loader::reverse); - add("travel_to", &Loader::travel_to); - add("travel_past", &Loader::travel_past); - add("wait", &Loader::wait); - add("wait_train", &Loader::wait_train); - add("wait_until", &Loader::wait_until); - - // Deprecated alias - add("goto", &Loader::goto_sensor_str); - add("travel", &Loader::travel_to); + current_row->time = train.get_layout().get_clock().get_current_time(); + unsigned i = distance(rows.begin(), current_row); + signal_row_modified.emit(i, *current_row); } -void Timetable::Loader::arrive() -{ - obj.rows.push_back(Row(ARRIVE)); -} -void Timetable::Loader::goto_sensor(unsigned s) -{ - obj.rows.push_back(Row(GOTO_SENSOR, s)); -} +Timetable::Row::Row(): + type(ARRIVE), + target(0), + direction(TrackChain::UNSPECIFIED) +{ } -void Timetable::Loader::goto_sensor_str(const string &s) +void Timetable::Row::save(list &st) const { - if(!s.compare(0, 7, "sensor ")) - obj.rows.push_back(Row(GOTO_SENSOR, lexical_cast(s.substr(7)))); + st.push_back((DataFile::Statement("type"), type)); + st.push_back((DataFile::Statement("time"), time.raw())); + st.push_back(target->save_reference()); + if(direction) + st.push_back((DataFile::Statement("direction"), direction)); } -void Timetable::Loader::goto_zone(const string &z) + +Timetable::Loader::Loader(Timetable &t, Layout &l): + DataFile::ObjectLoader(t), + layout(l) { - obj.rows.push_back(Row(GOTO_ZONE, z)); + add("row", &Loader::row); } -void Timetable::Loader::route(const string &r) +void Timetable::Loader::row() { - obj.rows.push_back(Row(ROUTE, r)); + Row r; + load_sub(r, layout); + obj.rows.push_back(r); + obj.update_pending = true; } -void Timetable::Loader::reverse() + +Timetable::Row::Loader::Loader(Row &r, Layout &l): + DataFile::ObjectLoader(r), + layout(l) { - obj.rows.push_back(Row(REVERSE)); + add("block", &Loader::block); + add("direction", &Row::direction); + add("time", &Loader::time); + add("type", &Row::type); + add("zone", &Loader::zone); + add("zone", &Loader::zone_numbered); } -void Timetable::Loader::speed(unsigned s) +void Timetable::Row::Loader::block(unsigned id) { - obj.rows.push_back(Row(SPEED, s)); + obj.target = &layout.get_block(id); } -void Timetable::Loader::travel_to(unsigned s) +void Timetable::Row::Loader::time(Time::RawTime t) { - obj.rows.push_back(Row(TRAVEL_TO, s)); + obj.time = Time::TimeDelta(t); } -void Timetable::Loader::travel_past(unsigned s) +void Timetable::Row::Loader::zone(const string &name) { - obj.rows.push_back(Row(TRAVEL_PAST, s)); + zone_numbered(name, 0); } -void Timetable::Loader::wait(unsigned t) +void Timetable::Row::Loader::zone_numbered(const string &name, unsigned number) { - obj.rows.push_back(Row(WAIT_TIME, t)); + obj.target = &layout.get_zone(name, number); } -void Timetable::Loader::wait_train(unsigned t, unsigned s) + +void operator<<(LexicalConverter &conv, Timetable::RowType rt) { - Row row(WAIT_TRAIN, t); - row.params.push_back(s); - obj.rows.push_back(row); + switch(rt) + { + case Timetable::ARRIVE: conv.result("ARRIVE"); return; + case Timetable::DEPART: conv.result("DEPART"); return; + case Timetable::THROUGH: conv.result("THROUGH"); return; + default: throw lexical_error(format("conversion of RowType(%d) to string", rt)); + } } -void Timetable::Loader::wait_until(unsigned t, unsigned m) +void operator>>(const LexicalConverter &conv, Timetable::RowType &rt) { - Row row(WAIT_UNTIL, t); - row.params.push_back(m); - obj.rows.push_back(row); + if(conv.get()=="ARRIVE") + rt = Timetable::ARRIVE; + else if(conv.get()=="DEPART") + rt = Timetable::DEPART; + else if(conv.get()=="THROUGH") + rt = Timetable::THROUGH; + else + throw lexical_error(format("conversion of '%s' to RowType", conv.get())); } } // namespace R2C2