]> git.tdb.fi Git - r2c2.git/blob - source/libr2c2/timetable.cpp
Add support for named zones
[r2c2.git] / source / libr2c2 / timetable.cpp
1 /* $Id$
2
3 This file is part of R²C²
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 R2C2 {
21
22 Timetable::Timetable(Train &t):
23         train(t),
24         enabled(false),
25         current_row(0),
26         executing(true),
27         pending_block(0),
28         pending_train(0)
29 {
30         train.signal_arrived.connect(sigc::mem_fun(this, &Timetable::train_arrived));
31         train.get_layout().get_driver().signal_sensor.connect(sigc::mem_fun(this, &Timetable::sensor_event));
32 }
33
34 void Timetable::set_enabled(bool e)
35 {
36         enabled = e;
37 }
38
39 void Timetable::reset()
40 {
41         current_row = 0;
42         wait_timeout = Time::TimeStamp();
43         pending_block = 0;
44         executing = true;
45 }
46
47 void Timetable::clear()
48 {
49         rows.clear();
50         reset();
51 }
52
53 void Timetable::append(const Row &row)
54 {
55         rows.push_back(row);
56 }
57
58 void Timetable::insert(unsigned i, const Row &row)
59 {
60         if(i>rows.size())
61                 throw InvalidParameterValue("Insert position out of range");
62
63         rows.insert(rows.begin()+i, row);
64         if(i<=current_row)
65                 ++current_row;
66 }
67
68 const Timetable::Row &Timetable::get_row(unsigned i) const
69 {
70         if(i>=rows.size())
71                 throw InvalidParameterValue("Row index out of range");
72         return rows[i];
73 }
74
75 void Timetable::tick(const Time::TimeStamp &t)
76 {
77         if(rows.empty() || !enabled)
78                 return;
79
80         if(wait_timeout && t>=wait_timeout)
81         {
82                 wait_timeout = Time::TimeStamp();
83                 current_row = (current_row+1)%rows.size();
84                 executing = true;
85         }
86
87         if(executing)
88         {
89                 Row &row = rows[current_row];
90                 switch(row.type)
91                 {
92                 case GOTO_SENSOR:
93                         if(!train.go_to(get_sensor(row.get_param<unsigned>(0))))
94                                 set_enabled(false);
95                         break;
96                 case GOTO_ZONE:
97                         if(!train.go_to(get_zone(row.get_param<string>(0))))
98                                 set_enabled(false);
99                         break;
100                 case TRAVEL:
101                         pending_block = &get_sensor(row.get_param<unsigned>(0)).get_block();
102                         pending_train = &train;
103                         executing = false;
104                         break;
105                 case WAIT_TIME:
106                         wait_timeout = t+row.get_param<unsigned>(0)*Time::sec;
107                         executing = false;
108                         break;
109                 case WAIT_TRAIN:
110                         pending_train = &train.get_layout().get_train(row.get_param<unsigned>(0));
111                         pending_block = &get_sensor(row.get_param<unsigned>(1)).get_block();
112                         executing = false;
113                         break;
114                 case ARRIVE:
115                         executing = false;
116                         break;
117                 case SPEED:
118                         train.set_control("speed", row.get_param<unsigned>(0)/3.6*train.get_layout().get_catalogue().get_scale());
119                         break;
120                 case REVERSE:
121                         train.set_control("reverse", !train.get_control("reverse"));
122                         break;
123                 case ROUTE:
124                         if(!train.set_route(&train.get_layout().get_route(row.get_param<string>(0))))
125                                 set_enabled(false);
126                         break;
127                 }
128
129                 if(executing)
130                         current_row = (current_row+1)%rows.size();
131         }
132 }
133
134 void Timetable::save(list<DataFile::Statement> &st) const
135 {
136         for(vector<Row>::const_iterator i=rows.begin(); i!=rows.end(); ++i)
137                 st.push_back(i->save());
138 }
139
140 Track &Timetable::get_sensor(unsigned id)
141 {
142         Block &block = train.get_layout().get_block(id|0x1000);
143         return **block.get_tracks().begin();
144 }
145
146 Zone &Timetable::get_zone(const string &name)
147 {
148         string::size_type space = name.rfind(' ');
149         if(space==string::npos || space==0)
150                 throw InvalidParameterValue("Invalid zone name");
151         unsigned number = lexical_cast<unsigned>(name.substr(space+1));
152         return train.get_layout().get_zone(name.substr(0, space), number);
153 }
154
155 void Timetable::sensor_event(unsigned addr, bool state)
156 {
157         if(pending_block && pending_block->get_train()==pending_train && addr==pending_block->get_sensor_id() && state)
158         {
159                 pending_block = 0;
160                 current_row = (current_row+1)%rows.size();
161                 executing = true;
162         }
163 }
164
165 void Timetable::train_arrived()
166 {
167         Row &row = rows[current_row];
168         if(row.type==ARRIVE)
169         {
170                 current_row = (current_row+1)%rows.size();
171                 executing = true;
172         }
173 }
174
175
176 Timetable::Row::Row(RowType t):
177         type(t)
178 { }
179
180 template<typename T>
181 Timetable::Row::Row(RowType t, const T &p):
182         type(t)
183 {
184         params.push_back(p);
185 }
186
187 template<typename T>
188 const T &Timetable::Row::get_param(unsigned i) const
189 {
190         if(i>=params.size())
191                 throw InvalidParameterValue("Parameter index out of range");
192         return params[i].value<T>();
193 }
194
195 string Timetable::Row::str() const
196 {
197         switch(type)
198         {
199         case GOTO_SENSOR:
200                 return "set route to sensor "+get_param<unsigned>(0);
201         case GOTO_ZONE:
202                 return "set route to "+get_param<string>(0);
203         case TRAVEL:
204                 return "travel to "+get_param<string>(0);
205         case WAIT_TIME:
206                 return format("wait for %d seconds", get_param<unsigned>(0));
207         case WAIT_TRAIN:
208                 return format("wait for train %d at %s", get_param<unsigned>(0), get_param<string>(1));
209         case ARRIVE:
210                 return "travel until arrival";
211         case SPEED:
212                 return format("set speed %d km/h", get_param<unsigned>(0));
213         case REVERSE:
214                 return "reverse";
215         case ROUTE:
216                 return "set route "+get_param<string>(0);
217         default:
218                 return "invalid row";
219         }
220 }
221
222 DataFile::Statement Timetable::Row::save() const
223 {
224         switch(type)
225         {
226         case GOTO_SENSOR:
227                 return DataFile::Statement("goto_sensor"), get_param<unsigned>(0);
228         case GOTO_ZONE:
229                 return DataFile::Statement("goto_zone"), get_param<string>(0);
230         case TRAVEL:
231                 return DataFile::Statement("travel"), get_param<string>(0);
232         case WAIT_TIME:
233                 return DataFile::Statement("wait"), get_param<unsigned>(0);
234         case WAIT_TRAIN:
235                 return DataFile::Statement("wait_train"), get_param<unsigned>(0), get_param<unsigned>(1);
236         case ARRIVE:
237                 return DataFile::Statement("arrive");
238         case SPEED:
239                 return DataFile::Statement("speed"), get_param<unsigned>(0);
240         case REVERSE:
241                 return DataFile::Statement("reverse");
242         case ROUTE:
243                 return DataFile::Statement("route"), get_param<string>(0);
244         default:
245                 return DataFile::Statement();
246         }
247 }
248
249 Timetable::Row Timetable::Row::parse(const string &s)
250 {
251         if(!s.compare(0, 7, "travel "))
252         {
253                 if(!s.compare(7, 10, "to sensor "))
254                         return Row(TRAVEL, lexical_cast<unsigned>(s.substr(17)));
255                 else if(!s.compare(7, string::npos, "until arrival"))
256                         return Row(ARRIVE);
257         }
258         else if(!s.compare(0, 9, "wait for "))
259         {
260                 if(isdigit(s[9]))
261                 {
262                         unsigned nondigit = 10;
263                         while(nondigit<s.size() && isdigit(s[nondigit]))
264                                 ++nondigit;
265                         return Row(WAIT_TIME, lexical_cast<unsigned>(s.substr(9, nondigit-9)));
266                 }
267                 else if(!s.compare(9, 6, "train "))
268                 {
269                         string::size_type at = s.find(" at sensor ", 15);
270                         if(at!=string::npos)
271                         {
272                                 Row row(WAIT_TRAIN, lexical_cast<unsigned>(s.substr(15, at-15)));
273                                 row.params.push_back(lexical_cast<unsigned>(s.substr(at+11)));
274                                 return row;
275                         }
276                 }
277         }
278         else if(!s.compare(0, 10, "set speed "))
279         {
280                 unsigned nondigit = 11;
281                 while(nondigit<s.size() && (isdigit(s[nondigit]) || s[nondigit]=='-'))
282                         ++nondigit;
283                 return Row(SPEED, lexical_cast<unsigned>(s.substr(10, nondigit-10)));
284         }
285         else if(s=="reverse")
286                 return Row(REVERSE);
287         else if(!s.compare(0, 10, "set route "))
288         {
289                 if(!s.compare(10, 3, "to "))
290                 {
291                         if(!s.compare(13, 7, "sensor "))
292                                 return Row(GOTO_SENSOR, lexical_cast<unsigned>(s.substr(20)));
293                         else
294                                 return Row(GOTO_ZONE, s.substr(13));
295                 }
296                 return Row(ROUTE, s.substr(10));
297         }
298
299         throw InvalidParameterValue("Invalid row");
300 }
301
302
303 Timetable::Loader::Loader(Timetable &tt):
304         DataFile::ObjectLoader<Timetable>(tt)
305 {
306         add("arrive",      &Loader::arrive);
307         add("goto_sensor", static_cast<void (Loader::*)(unsigned)>(&Loader::goto_sensor));
308         add("goto_zone",   &Loader::goto_zone);
309         add("route",       &Loader::route);
310         add("speed",       &Loader::speed);
311         add("reverse",     &Loader::reverse);
312         add("travel",      &Loader::travel);
313         add("wait",        &Loader::wait);
314         add("wait_train",  &Loader::wait_train);
315
316         // Deprecated alias
317         add("goto",        static_cast<void (Loader::*)(const string &)>(&Loader::goto_sensor));
318 }
319
320 void Timetable::Loader::arrive()
321 {
322         obj.rows.push_back(Row(ARRIVE));
323 }
324
325 void Timetable::Loader::goto_sensor(unsigned s)
326 {
327         obj.rows.push_back(Row(GOTO_SENSOR, s));
328 }
329
330 void Timetable::Loader::goto_sensor(const string &s)
331 {
332         if(!s.compare(0, 7, "sensor "))
333                 obj.rows.push_back(Row(GOTO_SENSOR, lexical_cast<unsigned>(s.substr(7))));
334 }
335
336 void Timetable::Loader::goto_zone(const string &z)
337 {
338         obj.rows.push_back(Row(GOTO_ZONE, z));
339 }
340
341 void Timetable::Loader::route(const string &r)
342 {
343         obj.rows.push_back(Row(ROUTE, r));
344 }
345
346 void Timetable::Loader::reverse()
347 {
348         obj.rows.push_back(Row(REVERSE));
349 }
350
351 void Timetable::Loader::speed(unsigned s)
352 {
353         obj.rows.push_back(Row(SPEED, s));
354 }
355
356 void Timetable::Loader::travel(const string &t)
357 {
358         obj.rows.push_back(Row(TRAVEL, t));
359 }
360
361 void Timetable::Loader::wait(unsigned t)
362 {
363         obj.rows.push_back(Row(WAIT_TIME, t));
364 }
365
366 void Timetable::Loader::wait_train(unsigned t, unsigned s)
367 {
368         Row row(WAIT_TRAIN, t);
369         row.params.push_back(s);
370         obj.rows.push_back(row);
371 }
372
373 } // namespace R2C2