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