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