]> git.tdb.fi Git - r2c2.git/blob - source/libr2c2/timetable.cpp
Handle sensors in a separate class
[r2c2.git] / source / libr2c2 / timetable.cpp
1 #include <msp/strings/format.h>
2 #include <msp/time/units.h>
3 #include "block.h"
4 #include "catalogue.h"
5 #include "driver.h"
6 #include "layout.h"
7 #include "timetable.h"
8 #include "trackcircuit.h"
9 #include "train.h"
10
11 using namespace std;
12 using namespace Msp;
13
14 namespace R2C2 {
15
16 Timetable::Timetable(Train &t):
17         TrainAI(t),
18         enabled(false),
19         current_row(0),
20         executing(true),
21         pending_block(0),
22         pending_train(0),
23         arrived(false)
24 {
25         train.signal_advanced.connect(sigc::mem_fun(this, &Timetable::train_advanced));
26         train.signal_ai_event.connect(sigc::mem_fun(this, &Timetable::event));
27         Layout &layout = train.get_layout();
28         layout.signal_sensor_state_changed.connect(sigc::mem_fun(this, &Timetable::sensor_state_changed));
29         layout.signal_block_reserved.connect(sigc::mem_fun(this, &Timetable::block_reserved));
30 }
31
32 void Timetable::set_enabled(bool e)
33 {
34         enabled = e;
35 }
36
37 void Timetable::reset()
38 {
39         current_row = 0;
40         wait_timeout = Time::TimeStamp();
41         pending_block = 0;
42         executing = true;
43 }
44
45 void Timetable::clear()
46 {
47         rows.clear();
48         reset();
49 }
50
51 void Timetable::append(const Row &row)
52 {
53         rows.push_back(row);
54 }
55
56 void Timetable::insert(unsigned i, const Row &row)
57 {
58         if(i>rows.size())
59                 throw out_of_range("Timetable::insert");
60
61         rows.insert(rows.begin()+i, row);
62         if(i<=current_row)
63                 ++current_row;
64 }
65
66 const Timetable::Row &Timetable::get_row(unsigned i) const
67 {
68         if(i>=rows.size())
69                 throw out_of_range("Timetable::get_row");
70         return rows[i];
71 }
72
73 void Timetable::tick(const Time::TimeStamp &t, const Time::TimeDelta &)
74 {
75         if(rows.empty() || !enabled)
76                 return;
77
78         if(wait_timeout && t>=wait_timeout)
79         {
80                 wait_timeout = Time::TimeStamp();
81                 current_row = (current_row+1)%rows.size();
82                 executing = true;
83         }
84
85         if(executing)
86         {
87                 Row &row = rows[current_row];
88                 switch(row.type)
89                 {
90                 case GOTO_SENSOR:
91                         arrived = false;
92                         train.ai_message(Message("set-destination-block", &get_sensor(row.get_param<unsigned>(0))));
93                         break;
94                 case GOTO_ZONE:
95                         arrived = false;
96                         train.ai_message(Message("set-destination-zone", &get_zone(row.get_param<string>(0))));
97                         break;
98                 case TRAVEL_TO:
99                         {
100                                 Block *block = &get_sensor(row.get_param<unsigned>(0));
101                                 if(block->get_train()!=&train || block->get_sensor().get_state()<Sensor::MAYBE_ACTIVE)
102                                 {
103                                         pending_block = block;
104                                         pending_train = &train;
105                                         executing = false;
106                                 }
107                         }
108                         break;
109                 case TRAVEL_PAST:
110                         pending_block = &get_turnout(row.get_param<unsigned>(0)).get_block();
111                         pending_train = (pending_block->get_train()==&train ? 0 : &train);
112                         executing = false;
113                         break;
114                 case WAIT_TIME:
115                         wait_timeout = t+row.get_param<unsigned>(0)*Time::sec;
116                         executing = false;
117                         break;
118                 case WAIT_UNTIL:
119                         {
120                                 unsigned unixtime = t.to_unixtime();
121                                 unsigned mod = row.get_param<unsigned>(1);
122                                 unsigned secs = ((mod+row.get_param<unsigned>(0))-(unixtime%mod))%mod;
123                                 wait_timeout = t+secs*Time::sec;
124                                 executing = false;
125                         }
126                         break;
127                 case WAIT_TRAIN:
128                         {
129                                 Train *other_train = &train.get_layout().get_train(row.get_param<unsigned>(0));
130                                 Block *block = &get_sensor(row.get_param<unsigned>(1));
131                                 if(block->get_train()!=other_train || block->get_sensor().get_state()<Sensor::MAYBE_ACTIVE)
132                                 {
133                                         pending_train = other_train;
134                                         pending_block = block;
135                                         executing = false;
136                                 }
137                         }
138                         break;
139                 case ARRIVE:
140                         if(!arrived)
141                                 executing = false;
142                         arrived = false;
143                         break;
144                 case SPEED:
145                         if(!arrived)
146                         {
147                                 float speed = row.get_param<unsigned>(0)/3.6*train.get_layout().get_catalogue().get_scale();
148                                 train.ai_message(Message("set-target-speed", speed));
149                         }
150                         break;
151                 case REVERSE:
152                         train.ai_message(Message("toggle-reverse"));
153                         break;
154                 case ROUTE:
155                         train.ai_message(Message("set-route", &train.get_layout().get_route(row.get_param<string>(0))));
156                         break;
157                 }
158
159                 if(executing)
160                         current_row = (current_row+1)%rows.size();
161         }
162 }
163
164 void Timetable::save(list<DataFile::Statement> &st) const
165 {
166         for(vector<Row>::const_iterator i=rows.begin(); i!=rows.end(); ++i)
167                 st.push_back(i->save());
168 }
169
170 Block &Timetable::get_sensor(unsigned id)
171 {
172         return train.get_layout().get_block(id|0x1000);
173 }
174
175 Track &Timetable::get_turnout(unsigned id)
176 {
177         Block &block = train.get_layout().get_block(id|0x2000);
178         return **block.get_tracks().begin();
179 }
180
181 Zone &Timetable::get_zone(const string &name)
182 {
183         string::size_type space = name.rfind(' ');
184         if(space==string::npos || space==0)
185                 throw invalid_argument("Timetable::get_zone");
186         unsigned number = lexical_cast<unsigned>(name.substr(space+1));
187         return train.get_layout().get_zone(name.substr(0, space), number);
188 }
189
190 void Timetable::sensor_state_changed(Sensor &sensor, Sensor::State state)
191 {
192         if(rows.empty() || !enabled)
193                 return;
194
195         Block *block = 0;
196         if(TrackCircuit *tc = dynamic_cast<TrackCircuit *>(&sensor))
197                 block = &tc->get_block();
198         else
199                 return;
200
201         if(block==pending_block && block->get_train()==pending_train && state>=Sensor::MAYBE_ACTIVE)
202         {
203                 pending_block = 0;
204                 current_row = (current_row+1)%rows.size();
205                 executing = true;
206         }
207 }
208
209 void Timetable::block_reserved(Block &block, Train *trn)
210 {
211         if(rows.empty() || !enabled)
212                 return;
213
214         if(&block==pending_block && trn==pending_train)
215         {
216                 Row &row = rows[current_row];
217                 if(row.type==TRAVEL_PAST && !pending_train)
218                 {
219                         pending_block = 0;
220                         current_row = (current_row+1)%rows.size();
221                         executing = true;
222                 }
223         }
224 }
225
226 void Timetable::train_advanced(Block &block)
227 {
228         if(rows.empty() || !enabled)
229                 return;
230
231         Row &row = rows[current_row];
232         if(row.type==TRAVEL_PAST && &block==pending_block && pending_train)
233                 pending_train = 0;
234 }
235
236 void Timetable::event(TrainAI &, const Message &ev)
237 {
238         if(ev.type=="arrived")
239         {
240                 if(rows.empty() || !enabled)
241                         return;
242
243                 Row &row = rows[current_row];
244                 if(row.type==ARRIVE)
245                 {
246                         current_row = (current_row+1)%rows.size();
247                         executing = true;
248                 }
249                 else
250                         arrived = true;
251         }
252 }
253
254
255 Timetable::Row::Row(RowType t):
256         type(t)
257 { }
258
259 template<typename T>
260 Timetable::Row::Row(RowType t, const T &p):
261         type(t)
262 {
263         params.push_back(p);
264 }
265
266 template<typename T>
267 const T &Timetable::Row::get_param(unsigned i) const
268 {
269         if(i>=params.size())
270                 throw out_of_range("Timetable::Row::get_param");
271         return params[i].value<T>();
272 }
273
274 string Timetable::Row::str() const
275 {
276         switch(type)
277         {
278         case GOTO_SENSOR:
279                 return format("set route to sensor %d", get_param<unsigned>(0));
280         case GOTO_ZONE:
281                 return "set route to "+get_param<string>(0);
282         case TRAVEL_TO:
283                 return format("travel to sensor %d", get_param<unsigned>(0));
284         case TRAVEL_PAST:
285                 return format("travel past turnout %d", get_param<unsigned>(0));
286         case WAIT_TIME:
287                 return format("wait for %d seconds", get_param<unsigned>(0));
288         case WAIT_UNTIL:
289                 return format("wait until %d mod %d seconds", get_param<unsigned>(0), get_param<unsigned>(1));
290         case WAIT_TRAIN:
291                 return format("wait for train %d at sensor %d", get_param<unsigned>(0), get_param<unsigned>(1));
292         case ARRIVE:
293                 return "travel until arrival";
294         case SPEED:
295                 return format("set speed %d km/h", get_param<unsigned>(0));
296         case REVERSE:
297                 return "reverse";
298         case ROUTE:
299                 return "set route "+get_param<string>(0);
300         default:
301                 return "invalid row";
302         }
303 }
304
305 DataFile::Statement Timetable::Row::save() const
306 {
307         switch(type)
308         {
309         case GOTO_SENSOR:
310                 return DataFile::Statement("goto_sensor"), get_param<unsigned>(0);
311         case GOTO_ZONE:
312                 return DataFile::Statement("goto_zone"), get_param<string>(0);
313         case TRAVEL_TO:
314                 return DataFile::Statement("travel_to"), get_param<unsigned>(0);
315         case TRAVEL_PAST:
316                 return DataFile::Statement("travel_past"), get_param<unsigned>(0);
317         case WAIT_TIME:
318                 return DataFile::Statement("wait"), get_param<unsigned>(0);
319         case WAIT_UNTIL:
320                 return DataFile::Statement("wait_until"), get_param<unsigned>(0), get_param<unsigned>(1);
321         case WAIT_TRAIN:
322                 return DataFile::Statement("wait_train"), get_param<unsigned>(0), get_param<unsigned>(1);
323         case ARRIVE:
324                 return DataFile::Statement("arrive");
325         case SPEED:
326                 return DataFile::Statement("speed"), get_param<unsigned>(0);
327         case REVERSE:
328                 return DataFile::Statement("reverse");
329         case ROUTE:
330                 return DataFile::Statement("route"), get_param<string>(0);
331         default:
332                 return DataFile::Statement();
333         }
334 }
335
336 Timetable::Row Timetable::Row::parse(const string &s)
337 {
338         if(!s.compare(0, 7, "travel "))
339         {
340                 if(!s.compare(7, 10, "to sensor "))
341                         return Row(TRAVEL_TO, lexical_cast<unsigned>(s.substr(17)));
342                 else if(!s.compare(7, 13, "past turnout "))
343                         return Row(TRAVEL_PAST, lexical_cast<unsigned>(s.substr(20)));
344                 else if(!s.compare(7, string::npos, "until arrival"))
345                         return Row(ARRIVE);
346         }
347         else if(!s.compare(0, 9, "wait for "))
348         {
349                 if(isdigit(s[9]))
350                 {
351                         unsigned nondigit = 10;
352                         while(nondigit<s.size() && isdigit(s[nondigit]))
353                                 ++nondigit;
354                         return Row(WAIT_TIME, lexical_cast<unsigned>(s.substr(9, nondigit-9)));
355                 }
356                 else if(!s.compare(9, 6, "train "))
357                 {
358                         string::size_type at = s.find(" at sensor ", 15);
359                         if(at!=string::npos)
360                         {
361                                 Row row(WAIT_TRAIN, lexical_cast<unsigned>(s.substr(15, at-15)));
362                                 row.params.push_back(lexical_cast<unsigned>(s.substr(at+11)));
363                                 return row;
364                         }
365                 }
366         }
367         else if(!s.compare(0, 11, "wait until "))
368         {
369                 string::size_type mod = s.find(" mod ", 11);
370                 unsigned nondigit = (mod!=string::npos ? mod+5 : 11);
371                 while(nondigit<s.size() && isdigit(s[nondigit]))
372                         ++nondigit;
373                 if(mod!=string::npos)
374                 {
375                         unsigned time = lexical_cast<unsigned>(s.substr(11, mod-11));
376                         Row row(WAIT_UNTIL, time);
377                         row.params.push_back(lexical_cast<unsigned>(s.substr(mod+5, nondigit-mod-5)));
378                         return row;
379                 }
380                 else
381                 {
382                         unsigned time = lexical_cast<unsigned>(s.substr(11, nondigit-11));
383                         Row row(WAIT_UNTIL, time);
384                         row.params.push_back(3600);
385                         return row;
386                 }
387         }
388         else if(!s.compare(0, 10, "set speed "))
389         {
390                 unsigned nondigit = 11;
391                 while(nondigit<s.size() && (isdigit(s[nondigit]) || s[nondigit]=='-'))
392                         ++nondigit;
393                 return Row(SPEED, lexical_cast<unsigned>(s.substr(10, nondigit-10)));
394         }
395         else if(s=="reverse")
396                 return Row(REVERSE);
397         else if(!s.compare(0, 10, "set route "))
398         {
399                 if(!s.compare(10, 3, "to "))
400                 {
401                         if(!s.compare(13, 7, "sensor "))
402                                 return Row(GOTO_SENSOR, lexical_cast<unsigned>(s.substr(20)));
403                         else
404                                 return Row(GOTO_ZONE, s.substr(13));
405                 }
406                 return Row(ROUTE, s.substr(10));
407         }
408
409         throw invalid_argument("Timetable::Row::parse");
410 }
411
412
413 Timetable::Loader::Loader(Timetable &tt):
414         DataFile::ObjectLoader<Timetable>(tt)
415 {
416         add("arrive",      &Loader::arrive);
417         add("goto_sensor", &Loader::goto_sensor);
418         add("goto_zone",   &Loader::goto_zone);
419         add("route",       &Loader::route);
420         add("speed",       &Loader::speed);
421         add("reverse",     &Loader::reverse);
422         add("travel_to",   &Loader::travel_to);
423         add("travel_past", &Loader::travel_past);
424         add("wait",        &Loader::wait);
425         add("wait_train",  &Loader::wait_train);
426         add("wait_until",  &Loader::wait_until);
427
428         // Deprecated alias
429         add("goto",        &Loader::goto_sensor_str);
430         add("travel",      &Loader::travel_to);
431 }
432
433 void Timetable::Loader::arrive()
434 {
435         obj.rows.push_back(Row(ARRIVE));
436 }
437
438 void Timetable::Loader::goto_sensor(unsigned s)
439 {
440         obj.rows.push_back(Row(GOTO_SENSOR, s));
441 }
442
443 void Timetable::Loader::goto_sensor_str(const string &s)
444 {
445         if(!s.compare(0, 7, "sensor "))
446                 obj.rows.push_back(Row(GOTO_SENSOR, lexical_cast<unsigned>(s.substr(7))));
447 }
448
449 void Timetable::Loader::goto_zone(const string &z)
450 {
451         obj.rows.push_back(Row(GOTO_ZONE, z));
452 }
453
454 void Timetable::Loader::route(const string &r)
455 {
456         obj.rows.push_back(Row(ROUTE, r));
457 }
458
459 void Timetable::Loader::reverse()
460 {
461         obj.rows.push_back(Row(REVERSE));
462 }
463
464 void Timetable::Loader::speed(unsigned s)
465 {
466         obj.rows.push_back(Row(SPEED, s));
467 }
468
469 void Timetable::Loader::travel_to(unsigned s)
470 {
471         obj.rows.push_back(Row(TRAVEL_TO, s));
472 }
473
474 void Timetable::Loader::travel_past(unsigned s)
475 {
476         obj.rows.push_back(Row(TRAVEL_PAST, s));
477 }
478
479 void Timetable::Loader::wait(unsigned t)
480 {
481         obj.rows.push_back(Row(WAIT_TIME, t));
482 }
483
484 void Timetable::Loader::wait_train(unsigned t, unsigned s)
485 {
486         Row row(WAIT_TRAIN, t);
487         row.params.push_back(s);
488         obj.rows.push_back(row);
489 }
490
491 void Timetable::Loader::wait_until(unsigned t, unsigned m)
492 {
493         Row row(WAIT_UNTIL, t);
494         row.params.push_back(m);
495         obj.rows.push_back(row);
496 }
497
498 } // namespace R2C2