+/* $Id$
+
+This file is part of the MSP Märklin suite
+Copyright © 2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#include <msp/time/units.h>
+#include "block.h"
+#include "catalogue.h"
+#include "driver.h"
+#include "layout.h"
+#include "timetable.h"
+#include "train.h"
+
+using namespace std;
+using namespace Msp;
+
+namespace Marklin {
+
+Timetable::Timetable(Train &t):
+ train(t),
+ current_row(0),
+ executing(true),
+ pending_block(0)
+{
+ 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));
+}
+
+void Timetable::tick(const Time::TimeStamp &t)
+{
+ if(wait_timeout && t>=wait_timeout)
+ {
+ wait_timeout = Time::TimeStamp();
+ current_row = (current_row+1)%rows.size();
+ executing = true;
+ }
+
+ while(executing)
+ {
+ Row &row = rows[current_row];
+ switch(row.type)
+ {
+ case GOTO:
+ train.go_to(**parse_location(row.strparam).get_tracks().begin());
+ executing = false;
+ break;
+ case TRAVEL:
+ pending_block = &parse_location(row.strparam);
+ executing = false;
+ break;
+ case WAIT:
+ wait_timeout = t+row.intparam*Time::sec;
+ executing = false;
+ break;
+ case SPEED:
+ train.set_control("speed", row.intparam/3.6*train.get_layout().get_catalogue().get_scale());
+ break;
+ case ROUTE:
+ train.set_route(&train.get_layout().get_route(row.strparam));
+ break;
+ }
+
+ if(executing)
+ current_row = (current_row+1)%rows.size();
+ }
+}
+
+void Timetable::save(list<DataFile::Statement> &st) const
+{
+ for(vector<Row>::const_iterator i=rows.begin(); i!=rows.end(); ++i)
+ {
+ switch(i->type)
+ {
+ case GOTO:
+ st.push_back((DataFile::Statement("goto"), i->strparam));
+ break;
+ case TRAVEL:
+ st.push_back((DataFile::Statement("travel"), i->strparam));
+ break;
+ case WAIT:
+ st.push_back((DataFile::Statement("wait"), i->intparam));
+ break;
+ case SPEED:
+ st.push_back((DataFile::Statement("speed"), i->intparam));
+ break;
+ case ROUTE:
+ st.push_back((DataFile::Statement("route"), i->strparam));
+ break;
+ }
+ }
+}
+
+Block &Timetable::parse_location(const string &loc)
+{
+ if(!loc.compare(0, 7, "sensor "))
+ return train.get_layout().get_block(lexical_cast<unsigned>(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()==&train && addr==pending_block->get_sensor_id() && state)
+ {
+ pending_block = 0;
+ current_row = (current_row+1)%rows.size();
+ executing = true;
+ }
+}
+
+void Timetable::train_arrived()
+{
+ Row &row = rows[current_row];
+ if(row.type==GOTO)
+ {
+ current_row = (current_row+1)%rows.size();
+ executing = true;
+ }
+}
+
+
+Timetable::Row::Row(RowType t, int p):
+ type(t),
+ intparam(p)
+{ }
+
+Timetable::Row::Row(RowType t, const string &p):
+ type(t),
+ intparam(0),
+ strparam(p)
+{ }
+
+
+Timetable::Loader::Loader(Timetable &tt):
+ DataFile::ObjectLoader<Timetable>(tt)
+{
+ add("goto", &Loader::go_to);
+ add("route", &Loader::route);
+ add("speed", &Loader::speed);
+ add("travel", &Loader::travel);
+ add("wait", &Loader::wait);
+}
+
+void Timetable::Loader::go_to(const string &t)
+{
+ obj.rows.push_back(Row(GOTO, t));
+}
+
+void Timetable::Loader::route(const string &r)
+{
+ obj.rows.push_back(Row(ROUTE, r));
+}
+
+void Timetable::Loader::speed(int s)
+{
+ obj.rows.push_back(Row(SPEED, s));
+}
+
+void Timetable::Loader::travel(const string &t)
+{
+ obj.rows.push_back(Row(TRAVEL, t));
+}
+
+void Timetable::Loader::wait(unsigned t)
+{
+ obj.rows.push_back(Row(WAIT, t));
+}
+
+} // namespace Marklin