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