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