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