]> git.tdb.fi Git - r2c2.git/blob - source/libr2c2/timetable.cpp
Handle the case of a train being late
[r2c2.git] / source / libr2c2 / timetable.cpp
1 #include <algorithm>
2 #include <msp/strings/format.h>
3 #include "aicontrol.h"
4 #include "clock.h"
5 #include "layout.h"
6 #include "timetable.h"
7 #include "train.h"
8 #include "trainrouter.h"
9 #include "zone.h"
10
11 using namespace std;
12 using namespace Msp;
13
14 namespace R2C2 {
15
16 Timetable::Timetable(Train &t):
17         TrainAI(t),
18         current_row(rows.end()),
19         update_pending(false),
20         sync_to_clock(true),
21         late_arrival(false),
22         next_depart(rows.end())
23 {
24         if(!train.get_ai_of_type<AIControl>())
25                 new AIControl(train);
26         if(!train.get_ai_of_type<TrainRouter>())
27                 new TrainRouter(train);
28
29         train.signal_ai_event.connect(sigc::mem_fun(this, &Timetable::event));
30         train.get_layout().get_clock().signal_discontinuity.connect(sigc::mem_fun(this, &Timetable::clock_discontinuity));
31 }
32
33 void Timetable::append_row(const Row &r)
34 {
35         insert_row(rows.size(), r);
36 }
37
38 void Timetable::insert_row(unsigned i, const Row &r)
39 {
40         if(i>rows.size())
41                 throw out_of_range("Timetable::insert_row");
42
43         list<Row>::iterator j = rows.begin();
44         advance(j, i);
45         j = rows.insert(j, r);
46         signal_row_added.emit(i, *j);
47
48         check_update(j);
49 }
50
51 void Timetable::modify_row(unsigned i, const Row &r)
52 {
53         if(i>=rows.size())
54                 throw out_of_range("Timetable::remove_row");
55
56         list<Row>::iterator j = rows.begin();
57         advance(j, i);
58         *j = r;
59         signal_row_modified.emit(i, r);
60
61         check_update(j);
62 }
63
64 void Timetable::remove_row(unsigned i)
65 {
66         if(i>=rows.size())
67                 throw out_of_range("Timetable::remove_row");
68
69         list<Row>::iterator j = rows.begin();
70         advance(j, i);
71
72         check_update(j);
73         if(j==current_row)
74                 --current_row;
75
76         rows.erase(j);
77         signal_row_removed.emit(i);
78 }
79
80 const Timetable::Row &Timetable::get_row(unsigned i) const
81 {
82         if(i>=rows.size())
83                 throw out_of_range("Timetable::get_row");
84
85         list<Row>::const_iterator j = rows.begin();
86         advance(j, i);
87         return *j;
88 }
89
90 void Timetable::tick(const Time::TimeDelta &dt)
91 {
92         if(update_pending && !train.get_block_allocator().is_active())
93                 update_route();
94
95         if(next_depart!=rows.end() && next_depart!=current_row && passed_row(*next_depart, dt))
96                 late_arrival = true;
97 }
98
99 bool Timetable::passed_row(const Row &row, const Time::TimeDelta &dt) const
100 {
101         const Clock &clock = train.get_layout().get_clock();
102
103         Time::TimeDelta t = clock.get_current_time();
104         if(t<row.time)
105                 t += Time::day;
106
107         Time::TimeDelta b = t-dt*clock.get_rate();
108         return b<row.time;
109 }
110
111 void Timetable::save(list<DataFile::Statement> &st) const
112 {
113         for(list<Row>::const_iterator i=rows.begin(); i!=rows.end(); ++i)
114         {
115                 DataFile::Statement ss("row");
116                 i->save(ss.sub);
117                 st.push_back(ss);
118         }
119 }
120
121 void Timetable::check_update(const list<Row>::const_iterator &i)
122 {
123         for(list<Row>::const_iterator j=current_row; (j!=rows.end() && j!=i); ++j)
124                 if(j->type==ARRIVE)
125                         return;
126         update_pending = true;
127 }
128
129 list<Timetable::Row>::iterator Timetable::find_trip(const list<Row>::iterator &begin, list<Row>::iterator *arrive)
130 {
131         list<Row>::iterator i = find_if(begin, rows.end(), RowTypeMatch(DEPART));
132         if(i==rows.end())
133                 return i;
134
135         list<Row>::iterator j = find_if(i, rows.end(), RowTypeMatch(ARRIVE));
136         if(j==rows.end())
137                 return j;
138
139         if(arrive)
140                 *arrive = j;
141         return i;
142 }
143
144 void Timetable::update_route()
145 {
146         update_pending = false;
147         if(rows.empty())
148                 return;
149
150         const Clock &clock = train.get_layout().get_clock();
151
152         if(sync_to_clock)
153         {
154                 sync_to_clock = false;
155                 current_row = rows.begin();
156                 for(list<Row>::iterator i=rows.begin(); i!=rows.end(); ++i)
157                         if(i->type==DEPART && i->time>=clock.get_current_time())
158                         {
159                                 current_row = i;
160                                 break;
161                         }
162         }
163
164         list<Row>::iterator arrive;
165         list<Row>::iterator depart = find_trip(current_row, &arrive);
166         if(depart==rows.end())
167         {
168                 depart = find_trip(rows.begin(), &arrive);
169                 if(depart==rows.end())
170                 {
171                         current_row = rows.end();
172                         return;
173                 }
174         }
175
176         train.ai_message(Message("clear-route"));
177
178         current_row = depart;
179         for(list<Row>::const_iterator i=depart; i!=rows.end(); ++i)
180         {
181                 if(i->type==DEPART)
182                 {
183                         Time::TimeDelta dt;
184                         if(late_arrival)
185                                 dt = Time::min;
186                         else
187                         {
188                                 dt = i->time-clock.get_current_time();
189                                 while(dt<Time::zero)
190                                         dt += Time::day;
191                         }
192                         train.ai_message(Message("set-departure-delay", dt/clock.get_rate()));
193                 }
194                 else
195                 {
196                         train.ai_message(Message("add-waypoint", TrainRouter::Waypoint(*i->target, i->direction)));
197                         if(i->type==ARRIVE)
198                                 break;
199                 }
200         }
201
202         next_depart = find_trip(arrive, 0);
203         if(next_depart==rows.end())
204                 next_depart = find_trip(rows.begin(), 0);
205         if(next_depart!=rows.end())
206         {
207                 Time::TimeDelta dt = next_depart->time-depart->time;
208                 while(dt<=Time::zero)
209                         dt += Time::day;
210                 train.ai_message(Message("set-trip-duration", dt/clock.get_rate()));
211         }
212
213         late_arrival = false;
214 }
215
216 void Timetable::event(TrainAI &, const Message &msg)
217 {
218         if(msg.type=="departed")
219         {
220                 if(current_row->type==DEPART)
221                         ++current_row;
222         }
223         else if(msg.type=="arrived")
224         {
225                 if(current_row->type==ARRIVE)
226                         record_time();
227                 update_pending = true;
228         }
229         else if(msg.type=="waypoint-reached")
230         {
231                 const TrackChain *wp = msg.value.value<const TrackChain *>();
232                 if(current_row->type==THROUGH && current_row->target==wp)
233                 {
234                         record_time();
235                         ++current_row;
236                 }
237         }
238 }
239
240 void Timetable::record_time()
241 {
242         current_row->time = train.get_layout().get_clock().get_current_time();
243         unsigned i = distance(rows.begin(), current_row);
244         signal_row_modified.emit(i, *current_row);
245 }
246
247 void Timetable::clock_discontinuity()
248 {
249         update_pending = true;
250         sync_to_clock = true;
251 }
252
253
254 Timetable::Row::Row():
255         type(ARRIVE),
256         target(0),
257         direction(TrackChain::UNSPECIFIED)
258 { }
259
260 void Timetable::Row::save(list<DataFile::Statement> &st) const
261 {
262         st.push_back((DataFile::Statement("type"), type));
263         st.push_back((DataFile::Statement("time"), time.raw()));
264         st.push_back(target->save_reference());
265         if(direction)
266                 st.push_back((DataFile::Statement("direction"), direction));
267 }
268
269
270 Timetable::Loader::Loader(Timetable &t, Layout &l):
271         DataFile::ObjectLoader<Timetable>(t),
272         layout(l)
273 {
274         add("row", &Loader::row);
275 }
276
277 void Timetable::Loader::row()
278 {
279         Row r;
280         load_sub(r, layout);
281         obj.rows.push_back(r);
282         obj.update_pending = true;
283 }
284
285
286 Timetable::Row::Loader::Loader(Row &r, Layout &l):
287         DataFile::ObjectLoader<Timetable::Row>(r),
288         layout(l)
289 {
290         add("block", &Loader::block);
291         add("direction", &Row::direction);
292         add("time", &Loader::time);
293         add("type", &Row::type);
294         add("zone", &Loader::zone);
295         add("zone", &Loader::zone_numbered);
296 }
297
298 void Timetable::Row::Loader::block(unsigned id)
299 {
300         obj.target = &layout.get_block(id);
301 }
302
303 void Timetable::Row::Loader::time(Time::RawTime t)
304 {
305         obj.time = Time::TimeDelta(t);
306 }
307
308 void Timetable::Row::Loader::zone(const string &name)
309 {
310         zone_numbered(name, 0);
311 }
312
313 void Timetable::Row::Loader::zone_numbered(const string &name, unsigned number)
314 {
315         obj.target = &layout.get_zone(name, number);
316 }
317
318
319 void operator<<(LexicalConverter &conv, Timetable::RowType rt)
320 {
321         switch(rt)
322         {
323         case Timetable::ARRIVE: conv.result("ARRIVE"); return;
324         case Timetable::DEPART: conv.result("DEPART"); return;
325         case Timetable::THROUGH: conv.result("THROUGH"); return;
326         default: throw lexical_error(format("conversion of RowType(%d) to string", rt));
327         }
328 }
329
330 void operator>>(const LexicalConverter &conv, Timetable::RowType &rt)
331 {
332         if(conv.get()=="ARRIVE")
333                 rt = Timetable::ARRIVE;
334         else if(conv.get()=="DEPART")
335                 rt = Timetable::DEPART;
336         else if(conv.get()=="THROUGH")
337                 rt = Timetable::THROUGH;
338         else
339                 throw lexical_error(format("conversion of '%s' to RowType", conv.get()));
340 }
341
342 } // namespace R2C2