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