]> git.tdb.fi Git - r2c2.git/blob - source/libmarklin/timetable.cpp
Place vehicles on the track when attached
[r2c2.git] / source / libmarklin / timetable.cpp
1 /* $Id$
2
3 This file is part of the MSP Märklin suite
4 Copyright © 2010  Mikkosoft Productions, Mikko Rasa
5 Distributed under the GPL
6 */
7
8 #include <msp/strings/formatter.h>
9 #include <msp/time/units.h>
10 #include "block.h"
11 #include "catalogue.h"
12 #include "driver.h"
13 #include "layout.h"
14 #include "timetable.h"
15 #include "train.h"
16
17 using namespace std;
18 using namespace Msp;
19
20 namespace Marklin {
21
22 Timetable::Timetable(Train &t):
23         train(t),
24         enabled(false),
25         current_row(0),
26         executing(true),
27         pending_block(0)
28 {
29         train.signal_arrived.connect(sigc::mem_fun(this, &Timetable::train_arrived));
30         train.get_layout().get_driver().signal_sensor.connect(sigc::mem_fun(this, &Timetable::sensor_event));
31 }
32
33 void Timetable::set_enabled(bool e)
34 {
35         enabled = e;
36 }
37
38 void Timetable::reset()
39 {
40         current_row = 0;
41         wait_timeout = Time::TimeStamp();
42         pending_block = 0;
43         executing = true;
44 }
45
46 void Timetable::clear()
47 {
48         rows.clear();
49         reset();
50 }
51
52 void Timetable::append(const Row &row)
53 {
54         rows.push_back(row);
55 }
56
57 void Timetable::insert(unsigned i, const Row &row)
58 {
59         if(i>rows.size())
60                 throw InvalidParameterValue("Insert position out of range");
61
62         rows.insert(rows.begin()+i, row);
63         if(i<=current_row)
64                 ++current_row;
65 }
66
67 const Timetable::Row &Timetable::get_row(unsigned i) const
68 {
69         if(i>=rows.size())
70                 throw InvalidParameterValue("Row index out of range");
71         return rows[i];
72 }
73
74 void Timetable::tick(const Time::TimeStamp &t)
75 {
76         if(rows.empty() || !enabled)
77                 return;
78
79         if(wait_timeout && t>=wait_timeout)
80         {
81                 wait_timeout = Time::TimeStamp();
82                 current_row = (current_row+1)%rows.size();
83                 executing = true;
84         }
85
86         while(executing)
87         {
88                 Row &row = rows[current_row];
89                 switch(row.type)
90                 {
91                 case GOTO:
92                         train.go_to(**parse_location(row.strparam).get_tracks().begin());
93                         break;
94                 case TRAVEL:
95                         pending_block = &parse_location(row.strparam);
96                         executing = false;
97                         break;
98                 case WAIT:
99                         wait_timeout = t+row.intparam*Time::sec;
100                         executing = false;
101                         break;
102                 case ARRIVE:
103                         executing = false;
104                         break;
105                 case SPEED:
106                         train.set_control("speed", row.intparam/3.6*train.get_layout().get_catalogue().get_scale());
107                         break;
108                 case ROUTE:
109                         train.set_route(&train.get_layout().get_route(row.strparam));
110                         break;
111                 }
112
113                 if(executing)
114                         current_row = (current_row+1)%rows.size();
115         }
116 }
117
118 void Timetable::save(list<DataFile::Statement> &st) const
119 {
120         for(vector<Row>::const_iterator i=rows.begin(); i!=rows.end(); ++i)
121         {
122                 switch(i->type)
123                 {
124                 case GOTO:
125                         st.push_back((DataFile::Statement("goto"), i->strparam));
126                         break;
127                 case TRAVEL:
128                         st.push_back((DataFile::Statement("travel"), i->strparam));
129                         break;
130                 case WAIT:
131                         st.push_back((DataFile::Statement("wait"), i->intparam));
132                         break;
133                 case ARRIVE:
134                         st.push_back(DataFile::Statement("arrive"));
135                         break;
136                 case SPEED:
137                         st.push_back((DataFile::Statement("speed"), i->intparam));
138                         break;
139                 case ROUTE:
140                         st.push_back((DataFile::Statement("route"), i->strparam));
141                         break;
142                 }
143         }
144 }
145
146 Block &Timetable::parse_location(const string &loc)
147 {
148         if(!loc.compare(0, 7, "sensor "))
149                 return train.get_layout().get_block(lexical_cast<unsigned>(loc.substr(7))|0x1000);
150         throw Exception("Named blocks are not supported yet");
151 }
152
153 void Timetable::sensor_event(unsigned addr, bool state)
154 {
155         if(pending_block && pending_block->get_train()==&train && addr==pending_block->get_sensor_id() && state)
156         {
157                 pending_block = 0;
158                 current_row = (current_row+1)%rows.size();
159                 executing = true;
160         }
161 }
162
163 void Timetable::train_arrived()
164 {
165         Row &row = rows[current_row];
166         if(row.type==ARRIVE)
167         {
168                 current_row = (current_row+1)%rows.size();
169                 executing = true;
170         }
171 }
172
173
174 Timetable::Row::Row(RowType t, int p):
175         type(t),
176         intparam(p)
177 { }
178
179 Timetable::Row::Row(RowType t, const string &p):
180         type(t),
181         intparam(0),
182         strparam(p)
183 { }
184
185 string Timetable::Row::str() const
186 {
187         switch(type)
188         {
189         case GOTO:
190                 return "go to "+strparam;
191         case TRAVEL:
192                 return "travel to "+strparam;
193         case WAIT:
194                 return format("wait for %d seconds", intparam);
195         case ARRIVE:
196                 return "wait for arrival";
197         case SPEED:
198                 return format("set speed %d km/h", intparam);
199         case ROUTE:
200                 return "set route "+strparam;
201         default:
202                 return "invalid row";
203         }
204 }
205
206 Timetable::Row Timetable::Row::parse(const string &s)
207 {
208         if(!s.compare(0, 6, "go to "))
209                 return Row(GOTO, s.substr(6));
210         else if(!s.compare(0, 10, "travel to "))
211                 return Row(TRAVEL, s.substr(10));
212         else if(!s.compare(0, 9, "wait for ") && isdigit(s[9]))
213         {
214                 unsigned nondigit = 10;
215                 while(nondigit<s.size() && isdigit(s[nondigit]))
216                         ++nondigit;
217                 return Row(WAIT, lexical_cast<unsigned>(s.substr(9, nondigit-9)));
218         }
219         else if(s=="wait for arrival")
220                 return Row(ARRIVE, 0);
221         else if(!s.compare(0, 10, "set speed "))
222         {
223                 unsigned nondigit = 11;
224                 while(nondigit<s.size() && (isdigit(s[nondigit]) || s[nondigit]=='-'))
225                         ++nondigit;
226                 return Row(SPEED, lexical_cast<int>(s.substr(10, nondigit-10)));
227         }
228         else if(!s.compare(0, 10, "set route "))
229                 return Row(ROUTE, s.substr(10));
230
231         throw InvalidParameterValue("Invalid row");
232 }
233
234
235 Timetable::Loader::Loader(Timetable &tt):
236         DataFile::ObjectLoader<Timetable>(tt)
237 {
238         add("arrive", &Loader::arrive);
239         add("goto",   &Loader::go_to);
240         add("route",  &Loader::route);
241         add("speed",  &Loader::speed);
242         add("travel", &Loader::travel);
243         add("wait",   &Loader::wait);
244 }
245
246 void Timetable::Loader::arrive()
247 {
248         obj.rows.push_back(Row(ARRIVE, 0));
249 }
250
251 void Timetable::Loader::go_to(const string &t)
252 {
253         obj.rows.push_back(Row(GOTO, t));
254 }
255
256 void Timetable::Loader::route(const string &r)
257 {
258         obj.rows.push_back(Row(ROUTE, r));
259 }
260
261 void Timetable::Loader::speed(int s)
262 {
263         obj.rows.push_back(Row(SPEED, s));
264 }
265
266 void Timetable::Loader::travel(const string &t)
267 {
268         obj.rows.push_back(Row(TRAVEL, t));
269 }
270
271 void Timetable::Loader::wait(unsigned t)
272 {
273         obj.rows.push_back(Row(WAIT, t));
274 }
275
276 } // namespace Marklin