2 #include <msp/strings/format.h>
8 #include "trainrouter.h"
16 Timetable::Timetable(Train &t):
18 current_row(rows.end()),
19 update_pending(false),
22 next_depart(rows.end())
24 if(!train.get_ai_of_type<AIControl>())
26 if(!train.get_ai_of_type<TrainRouter>())
27 new TrainRouter(train);
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));
33 void Timetable::append_row(const Row &r)
35 insert_row(rows.size(), r);
38 void Timetable::insert_row(unsigned i, const Row &r)
41 throw out_of_range("Timetable::insert_row");
43 list<Row>::iterator j = rows.begin();
45 j = rows.insert(j, r);
46 signal_row_added.emit(i, *j);
51 void Timetable::modify_row(unsigned i, const Row &r)
54 throw out_of_range("Timetable::remove_row");
56 list<Row>::iterator j = rows.begin();
59 signal_row_modified.emit(i, r);
64 void Timetable::remove_row(unsigned i)
67 throw out_of_range("Timetable::remove_row");
69 list<Row>::iterator j = rows.begin();
77 signal_row_removed.emit(i);
80 const Timetable::Row &Timetable::get_row(unsigned i) const
83 throw out_of_range("Timetable::get_row");
85 list<Row>::const_iterator j = rows.begin();
90 void Timetable::tick(const Time::TimeDelta &dt)
92 if(update_pending && !train.get_block_allocator().is_active())
95 if(next_depart!=rows.end() && next_depart!=current_row && passed_row(*next_depart, dt))
99 bool Timetable::passed_row(const Row &row, const Time::TimeDelta &dt) const
101 const Clock &clock = train.get_layout().get_clock();
103 Time::TimeDelta t = clock.get_current_time();
107 Time::TimeDelta b = t-dt*clock.get_rate();
111 void Timetable::save(list<DataFile::Statement> &st) const
113 for(list<Row>::const_iterator i=rows.begin(); i!=rows.end(); ++i)
115 DataFile::Statement ss("row");
121 void Timetable::check_update(const list<Row>::const_iterator &i)
123 for(list<Row>::const_iterator j=current_row; (j!=rows.end() && j!=i); ++j)
126 update_pending = true;
129 list<Timetable::Row>::iterator Timetable::find_trip(const list<Row>::iterator &begin, list<Row>::iterator *arrive)
131 list<Row>::iterator i = find_if(begin, rows.end(), RowTypeMatch(DEPART));
135 list<Row>::iterator j = find_if(i, rows.end(), RowTypeMatch(ARRIVE));
144 void Timetable::update_route()
146 update_pending = false;
150 const Clock &clock = train.get_layout().get_clock();
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())
164 list<Row>::iterator arrive;
165 list<Row>::iterator depart = find_trip(current_row, &arrive);
166 if(depart==rows.end())
168 depart = find_trip(rows.begin(), &arrive);
169 if(depart==rows.end())
171 current_row = rows.end();
176 train.ai_message(Message("clear-route"));
178 current_row = depart;
179 for(list<Row>::const_iterator i=depart; i!=rows.end(); ++i)
188 dt = i->time-clock.get_current_time();
192 train.ai_message(Message("set-departure-delay", dt/clock.get_rate()));
196 train.ai_message(Message("add-waypoint", TrainRouter::Waypoint(*i->target, i->direction)));
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())
207 Time::TimeDelta dt = next_depart->time-depart->time;
208 while(dt<=Time::zero)
210 train.ai_message(Message("set-trip-duration", dt/clock.get_rate()));
213 late_arrival = false;
216 void Timetable::event(TrainAI &, const Message &msg)
218 if(msg.type=="departed")
220 if(current_row->type==DEPART)
223 else if(msg.type=="arrived")
225 if(current_row->type==ARRIVE)
227 update_pending = true;
229 else if(msg.type=="waypoint-reached")
231 const TrackChain *wp = msg.value.value<const TrackChain *>();
232 if(current_row->type==THROUGH && current_row->target==wp)
240 void Timetable::record_time()
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);
247 void Timetable::clock_discontinuity()
249 update_pending = true;
250 sync_to_clock = true;
254 Timetable::Row::Row():
257 direction(TrackChain::UNSPECIFIED)
260 void Timetable::Row::save(list<DataFile::Statement> &st) const
262 st.push_back((DataFile::Statement("type"), type));
263 st.push_back((DataFile::Statement("time"), time.raw()));
264 st.push_back(target->save_reference());
266 st.push_back((DataFile::Statement("direction"), direction));
270 Timetable::Loader::Loader(Timetable &t, Layout &l):
271 DataFile::ObjectLoader<Timetable>(t),
274 add("row", &Loader::row);
277 void Timetable::Loader::row()
281 obj.rows.push_back(r);
282 obj.update_pending = true;
286 Timetable::Row::Loader::Loader(Row &r, Layout &l):
287 DataFile::ObjectLoader<Timetable::Row>(r),
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);
298 void Timetable::Row::Loader::block(unsigned id)
300 obj.target = &layout.get_block(id);
303 void Timetable::Row::Loader::time(Time::RawTime t)
305 obj.time = Time::TimeDelta(t);
308 void Timetable::Row::Loader::zone(const string &name)
310 zone_numbered(name, 0);
313 void Timetable::Row::Loader::zone_numbered(const string &name, unsigned number)
315 obj.target = &layout.get_zone(name, number);
319 void operator<<(LexicalConverter &conv, Timetable::RowType rt)
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));
330 void operator>>(const LexicalConverter &conv, Timetable::RowType &rt)
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;
339 throw lexical_error(format("conversion of '%s' to RowType", conv.get()));