]> git.tdb.fi Git - r2c2.git/blob - source/libmarklin/timetable.cpp
Add a timetable row type to wait for another train
[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         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:
93                         train.go_to(**parse_location(row.get_param<string>(0)).get_tracks().begin());
94                         break;
95                 case TRAVEL:
96                         pending_block = &parse_location(row.get_param<string>(0));
97                         pending_train = &train;
98                         executing = false;
99                         break;
100                 case WAIT_TIME:
101                         wait_timeout = t+row.get_param<unsigned>(0)*Time::sec;
102                         executing = false;
103                         break;
104                 case WAIT_TRAIN:
105                         pending_train = &train.get_layout().get_train(row.get_param<unsigned>(0));
106                         pending_block = &parse_location(row.get_param<string>(1));
107                         executing = false;
108                         break;
109                 case ARRIVE:
110                         executing = false;
111                         break;
112                 case SPEED:
113                         train.set_control("speed", row.get_param<unsigned>(0)/3.6*train.get_layout().get_catalogue().get_scale());
114                         break;
115                 case REVERSE:
116                         train.set_control("reverse", !train.get_control("reverse"));
117                         break;
118                 case ROUTE:
119                         train.set_route(&train.get_layout().get_route(row.get_param<string>(0)));
120                         break;
121                 }
122
123                 if(executing)
124                         current_row = (current_row+1)%rows.size();
125         }
126 }
127
128 void Timetable::save(list<DataFile::Statement> &st) const
129 {
130         for(vector<Row>::const_iterator i=rows.begin(); i!=rows.end(); ++i)
131                 st.push_back(i->save());
132 }
133
134 Block &Timetable::parse_location(const string &loc)
135 {
136         if(!loc.compare(0, 7, "sensor "))
137                 return train.get_layout().get_block(lexical_cast<unsigned>(loc.substr(7))|0x1000);
138         throw Exception("Named blocks are not supported yet");
139 }
140
141 void Timetable::sensor_event(unsigned addr, bool state)
142 {
143         if(pending_block && pending_block->get_train()==pending_train && addr==pending_block->get_sensor_id() && state)
144         {
145                 pending_block = 0;
146                 current_row = (current_row+1)%rows.size();
147                 executing = true;
148         }
149 }
150
151 void Timetable::train_arrived()
152 {
153         Row &row = rows[current_row];
154         if(row.type==ARRIVE)
155         {
156                 current_row = (current_row+1)%rows.size();
157                 executing = true;
158         }
159 }
160
161
162 Timetable::Row::Row(RowType t):
163         type(t)
164 { }
165
166 template<typename T>
167 Timetable::Row::Row(RowType t, const T &p):
168         type(t)
169 {
170         params.push_back(p);
171 }
172
173 template<typename T>
174 const T &Timetable::Row::get_param(unsigned i) const
175 {
176         if(i>=params.size())
177                 throw InvalidParameterValue("Parameter index out of range");
178         return params[i].value<T>();
179 }
180
181 string Timetable::Row::str() const
182 {
183         switch(type)
184         {
185         case GOTO:
186                 return "set route to "+get_param<string>(0);
187         case TRAVEL:
188                 return "travel to "+get_param<string>(0);
189         case WAIT_TIME:
190                 return format("wait for %d seconds", get_param<unsigned>(0));
191         case WAIT_TRAIN:
192                 return format("wait for train %d at %s", get_param<unsigned>(0), get_param<string>(1));
193         case ARRIVE:
194                 return "travel until arrival";
195         case SPEED:
196                 return format("set speed %d km/h", get_param<unsigned>(0));
197         case REVERSE:
198                 return "reverse";
199         case ROUTE:
200                 return "set route "+get_param<string>(0);
201         default:
202                 return "invalid row";
203         }
204 }
205
206 DataFile::Statement Timetable::Row::save() const
207 {
208         switch(type)
209         {
210         case GOTO:
211                 return DataFile::Statement("goto"), get_param<string>(0);
212         case TRAVEL:
213                 return DataFile::Statement("travel"), get_param<string>(0);
214         case WAIT_TIME:
215                 return DataFile::Statement("wait"), get_param<unsigned>(0);
216         case WAIT_TRAIN:
217                 return DataFile::Statement("wait_train"), get_param<unsigned>(0), get_param<string>(1);
218         case ARRIVE:
219                 return DataFile::Statement("arrive");
220         case SPEED:
221                 return DataFile::Statement("speed"), get_param<unsigned>(0);
222         case REVERSE:
223                 return DataFile::Statement("reverse");
224         case ROUTE:
225                 return DataFile::Statement("route"), get_param<string>(0);
226         default:
227                 return DataFile::Statement();
228         }
229 }
230
231 Timetable::Row Timetable::Row::parse(const string &s)
232 {
233         if(!s.compare(0, 7, "travel "))
234         {
235                 if(!s.compare(7, 3, "to "))
236                         return Row(TRAVEL, s.substr(10));
237                 else if(!s.compare(7, string::npos, "until arrival"))
238                         return Row(ARRIVE);
239         }
240         else if(!s.compare(0, 9, "wait for "))
241         {
242                 if(isdigit(s[9]))
243                 {
244                         unsigned nondigit = 10;
245                         while(nondigit<s.size() && isdigit(s[nondigit]))
246                                 ++nondigit;
247                         return Row(WAIT_TIME, lexical_cast<unsigned>(s.substr(9, nondigit-9)));
248                 }
249                 else if(!s.compare(9, 6, "train "))
250                 {
251                         string::size_type at = s.find(" at ", 15);
252                         if(at!=string::npos)
253                         {
254                                 Row row(WAIT_TRAIN, lexical_cast<unsigned>(s.substr(15, at-15)));
255                                 row.params.push_back(s.substr(at+4));
256                                 return row;
257                         }
258                 }
259         }
260         else if(!s.compare(0, 10, "set speed "))
261         {
262                 unsigned nondigit = 11;
263                 while(nondigit<s.size() && (isdigit(s[nondigit]) || s[nondigit]=='-'))
264                         ++nondigit;
265                 return Row(SPEED, lexical_cast<unsigned>(s.substr(10, nondigit-10)));
266         }
267         else if(s=="reverse")
268                 return Row(REVERSE);
269         else if(!s.compare(0, 10, "set route "))
270         {
271                 if(!s.compare(10, 3, "to "))
272                         return Row(GOTO, s.substr(13));
273                 return Row(ROUTE, s.substr(10));
274         }
275
276         throw InvalidParameterValue("Invalid row");
277 }
278
279
280 Timetable::Loader::Loader(Timetable &tt):
281         DataFile::ObjectLoader<Timetable>(tt)
282 {
283         add("arrive",  &Loader::arrive);
284         add("goto",    &Loader::go_to);
285         add("route",   &Loader::route);
286         add("speed",   &Loader::speed);
287         add("reverse", &Loader::reverse);
288         add("travel",  &Loader::travel);
289         add("wait",    &Loader::wait);
290         add("wait_train", &Loader::wait_train);
291 }
292
293 void Timetable::Loader::arrive()
294 {
295         obj.rows.push_back(Row(ARRIVE));
296 }
297
298 void Timetable::Loader::go_to(const string &t)
299 {
300         obj.rows.push_back(Row(GOTO, t));
301 }
302
303 void Timetable::Loader::route(const string &r)
304 {
305         obj.rows.push_back(Row(ROUTE, r));
306 }
307
308 void Timetable::Loader::reverse()
309 {
310         obj.rows.push_back(Row(REVERSE));
311 }
312
313 void Timetable::Loader::speed(unsigned s)
314 {
315         obj.rows.push_back(Row(SPEED, s));
316 }
317
318 void Timetable::Loader::travel(const string &t)
319 {
320         obj.rows.push_back(Row(TRAVEL, t));
321 }
322
323 void Timetable::Loader::wait(unsigned t)
324 {
325         obj.rows.push_back(Row(WAIT_TIME, t));
326 }
327
328 void Timetable::Loader::wait_train(unsigned t, const string &b)
329 {
330         Row row(WAIT_TRAIN, t);
331         row.params.push_back(b);
332         obj.rows.push_back(row);
333 }
334
335 } // namespace Marklin