X-Git-Url: http://git.tdb.fi/?p=r2c2.git;a=blobdiff_plain;f=source%2Flibr2c2%2Ftimetable.cpp;h=5fa36395825d0afdb6dfb92df83e76f97d3cd6cb;hp=bc8ebe6c43012d628b695ff1ba036f5022489c7f;hb=d990c03a06a494ce3596862ce61e2a5684dea7e1;hpb=1ff06c5bc46a677fa389ef86c6b26664368f1653 diff --git a/source/libr2c2/timetable.cpp b/source/libr2c2/timetable.cpp index bc8ebe6..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 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,318 +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) + TrainAI(t), + current_row(rows.end()), + update_pending(false), + sync_to_clock(true) { - train.signal_arrived.connect(sigc::mem_fun(this, &Timetable::train_arrived)); - train.get_layout().get_driver().signal_sensor.connect(sigc::mem_fun(this, &Timetable::sensor_event)); -} + if(!train.get_ai_of_type()) + new AIControl(train); + if(!train.get_ai_of_type()) + new TrainRouter(train); -void Timetable::set_enabled(bool e) -{ - enabled = e; + train.signal_ai_event.connect(sigc::mem_fun(this, &Timetable::event)); } -void Timetable::reset() +void Timetable::append_row(const Row &r) { - current_row = 0; - wait_timeout = Time::TimeStamp(); - pending_block = 0; - executing = true; + insert_row(rows.size(), r); } -void Timetable::clear() +void Timetable::insert_row(unsigned i, const Row &r) { - rows.clear(); - reset(); + if(i>rows.size()) + 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); + + check_update(j); } -void Timetable::append(const Row &row) +void Timetable::modify_row(unsigned i, const Row &r) { - rows.push_back(row); + if(i>=rows.size()) + 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::insert(unsigned i, const Row &row) +void Timetable::remove_row(unsigned i) { - if(i>rows.size()) - throw InvalidParameterValue("Insert position out of range"); + if(i>=rows.size()) + throw out_of_range("Timetable::remove_row"); + + list::iterator j = rows.begin(); + advance(j, i); - rows.insert(rows.begin()+i, row); - if(i<=current_row) - ++current_row; + check_update(j); + if(j==current_row) + --current_row; + + rows.erase(j); + signal_row_removed.emit(i); } const Timetable::Row &Timetable::get_row(unsigned i) const { if(i>=rows.size()) - throw InvalidParameterValue("Row index out of range"); - return rows[i]; + throw out_of_range("Timetable::get_row"); + + list::const_iterator j = rows.begin(); + advance(j, i); + return *j; } -void Timetable::tick(const Time::TimeStamp &t) +void Timetable::tick(const Time::TimeDelta &dt) { - if(rows.empty() || !enabled) - return; + if(update_pending && !train.get_block_allocator().is_active()) + update_route(); - if(wait_timeout && t>=wait_timeout) + if(current_row->type==DEPART) { - wait_timeout = Time::TimeStamp(); - current_row = (current_row+1)%rows.size(); - executing = true; - } + const Clock &clock = train.get_layout().get_clock(); - if(executing) - { - Row &row = rows[current_row]; - switch(row.type) + Time::TimeDelta t = clock.get_current_time(); + if(ttime) + t += Time::day; + + Time::TimeDelta b = t-dt*clock.get_rate(); + if(btime) { - case GOTO: - if(!train.go_to(**parse_location(row.get_param(0)).get_tracks().begin())) - set_enabled(false); - break; - case TRAVEL: - pending_block = &parse_location(row.get_param(0)); - pending_train = &train; - executing = false; - break; - case WAIT_TIME: - wait_timeout = t+row.get_param(0)*Time::sec; - executing = false; - break; - case WAIT_TRAIN: - pending_train = &train.get_layout().get_train(row.get_param(0)); - pending_block = &parse_location(row.get_param(1)); - executing = false; - break; - case ARRIVE: - executing = false; - break; - case SPEED: - 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; + train.ai_message(Message("set-target-speed", train.get_maximum_speed())); + ++current_row; } - - if(executing) - current_row = (current_row+1)%rows.size(); } } void Timetable::save(list &st) const { - for(vector::const_iterator i=rows.begin(); i!=rows.end(); ++i) - st.push_back(i->save()); -} - -Block &Timetable::parse_location(const string &loc) -{ - if(!loc.compare(0, 7, "sensor ")) - return train.get_layout().get_block(lexical_cast(loc.substr(7))|0x1000); - throw Exception("Named blocks are not supported yet"); -} - -void Timetable::sensor_event(unsigned addr, bool state) -{ - if(pending_block && pending_block->get_train()==pending_train && addr==pending_block->get_sensor_id() && state) + for(list::const_iterator i=rows.begin(); i!=rows.end(); ++i) { - pending_block = 0; - current_row = (current_row+1)%rows.size(); - executing = true; + DataFile::Statement ss("row"); + i->save(ss.sub); + st.push_back(ss); } } -void Timetable::train_arrived() +void Timetable::check_update(const list::const_iterator &i) { - Row &row = rows[current_row]; - if(row.type==ARRIVE) - { - current_row = (current_row+1)%rows.size(); - executing = true; - } + for(list::const_iterator j=current_row; (j!=rows.end() && j!=i); ++j) + if(j->type==ARRIVE) + return; + update_pending = true; } +list::iterator Timetable::find_trip(const list::iterator &begin, list::iterator *arrive) +{ + list::iterator i = find_if(begin, rows.end(), RowTypeMatch(DEPART)); + if(i==rows.end()) + return i; -Timetable::Row::Row(RowType t): - type(t) -{ } + list::iterator j = find_if(i, rows.end(), RowTypeMatch(ARRIVE)); + if(j==rows.end()) + return j; -template -Timetable::Row::Row(RowType t, const T &p): - type(t) -{ - params.push_back(p); + if(arrive) + *arrive = j; + return i; } -template -const T &Timetable::Row::get_param(unsigned i) const +void Timetable::update_route() { - if(i>=params.size()) - throw InvalidParameterValue("Parameter index out of range"); - return params[i].value(); -} + update_pending = false; + if(rows.empty()) + return; -string Timetable::Row::str() const -{ - switch(type) - { - case GOTO: - return "set route to "+get_param(0); - case TRAVEL: - return "travel to "+get_param(0); - case WAIT_TIME: - return format("wait for %d seconds", get_param(0)); - 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"; - } -} + const Clock &clock = train.get_layout().get_clock(); -DataFile::Statement Timetable::Row::save() const -{ - switch(type) + if(sync_to_clock) { - case GOTO: - return DataFile::Statement("goto"), get_param(0); - case TRAVEL: - return DataFile::Statement("travel"), get_param(0); - case WAIT_TIME: - return DataFile::Statement("wait"), get_param(0); - 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(); + 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; + } } -} -Timetable::Row Timetable::Row::parse(const string &s) -{ - if(!s.compare(0, 7, "travel ")) + list::iterator arrive; + list::iterator depart = find_trip(current_row, &arrive); + if(depart==rows.end()) { - if(!s.compare(7, 3, "to ")) - return Row(TRAVEL, s.substr(10)); - else if(!s.compare(7, string::npos, "until arrival")) - return Row(ARRIVE); + depart = find_trip(rows.begin(), &arrive); + if(depart==rows.end()) + { + current_row = rows.end(); + return; + } } - else if(!s.compare(0, 9, "wait for ")) + + train.ai_message(Message("clear-route")); + + current_row = depart; + for(list::const_iterator i=depart; i!=rows.end(); ++i) { - if(isdigit(s[9])) + if(i->type==DEPART) { - unsigned nondigit = 10; - while(nondigit(s.substr(9, nondigit-9))); + Time::TimeDelta dt = i->time-clock.get_current_time(); + while(dt(s.substr(15, at-15))); - row.params.push_back(s.substr(at+4)); - 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 ")) - return Row(GOTO, s.substr(13)); - return Row(ROUTE, s.substr(10)); + 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) + { + record_time(); + ++current_row; + } + } +} + +void Timetable::record_time() +{ + 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); +} - throw InvalidParameterValue("Invalid row"); + +Timetable::Row::Row(): + type(ARRIVE), + target(0), + direction(TrackChain::UNSPECIFIED) +{ } + +void Timetable::Row::save(list &st) const +{ + 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)); } -Timetable::Loader::Loader(Timetable &tt): - DataFile::ObjectLoader(tt) +Timetable::Loader::Loader(Timetable &t, Layout &l): + DataFile::ObjectLoader(t), + layout(l) { - add("arrive", &Loader::arrive); - add("goto", &Loader::go_to); - add("route", &Loader::route); - add("speed", &Loader::speed); - add("reverse", &Loader::reverse); - add("travel", &Loader::travel); - add("wait", &Loader::wait); - add("wait_train", &Loader::wait_train); + add("row", &Loader::row); } -void Timetable::Loader::arrive() +void Timetable::Loader::row() { - obj.rows.push_back(Row(ARRIVE)); + Row r; + load_sub(r, layout); + obj.rows.push_back(r); + obj.update_pending = true; } -void Timetable::Loader::go_to(const string &t) + +Timetable::Row::Loader::Loader(Row &r, Layout &l): + DataFile::ObjectLoader(r), + layout(l) { - obj.rows.push_back(Row(GOTO, t)); + 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::route(const string &r) +void Timetable::Row::Loader::block(unsigned id) { - obj.rows.push_back(Row(ROUTE, r)); + obj.target = &layout.get_block(id); } -void Timetable::Loader::reverse() +void Timetable::Row::Loader::time(Time::RawTime t) { - obj.rows.push_back(Row(REVERSE)); + obj.time = Time::TimeDelta(t); } -void Timetable::Loader::speed(unsigned s) +void Timetable::Row::Loader::zone(const string &name) { - obj.rows.push_back(Row(SPEED, s)); + zone_numbered(name, 0); } -void Timetable::Loader::travel(const string &t) +void Timetable::Row::Loader::zone_numbered(const string &name, unsigned number) { - obj.rows.push_back(Row(TRAVEL, t)); + obj.target = &layout.get_zone(name, number); } -void Timetable::Loader::wait(unsigned t) + +void operator<<(LexicalConverter &conv, Timetable::RowType rt) { - obj.rows.push_back(Row(WAIT_TIME, t)); + 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_train(unsigned t, const string &b) +void operator>>(const LexicalConverter &conv, Timetable::RowType &rt) { - Row row(WAIT_TRAIN, t); - row.params.push_back(b); - 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