]> git.tdb.fi Git - r2c2.git/blob - libr2c2/timetable.cpp
Don't crash if a train has no router
[r2c2.git] / libr2c2 / timetable.cpp
1 #include <msp/strings/format.h>
2 #include "clock.h"
3 #include "layout.h"
4 #include "timetable.h"
5 #include "train.h"
6 #include "zone.h"
7
8 using namespace std;
9 using namespace Msp;
10
11 namespace R2C2 {
12
13 Timetable::Timetable(Train &t):
14         TrainAI(t),
15         current_row(rows.end()),
16         update_pending(false)
17 {
18         train.signal_ai_event.connect(sigc::mem_fun(this, &Timetable::event));
19 }
20
21 void Timetable::append_row(const Row &r)
22 {
23         insert_row(rows.size(), r);
24 }
25
26 void Timetable::insert_row(unsigned i, const Row &r)
27 {
28         if(i>rows.size())
29                 throw out_of_range("Timetable::insert_row");
30
31         list<Row>::iterator j = rows.begin();
32         advance(j, i);
33         j = rows.insert(j, r);
34         signal_row_added.emit(i, *j);
35
36         check_update(j);
37 }
38
39 void Timetable::modify_row(unsigned i, const Row &r)
40 {
41         if(i>=rows.size())
42                 throw out_of_range("Timetable::remove_row");
43
44         list<Row>::iterator j = rows.begin();
45         advance(j, i);
46         *j = r;
47         signal_row_modified.emit(i, r);
48
49         check_update(j);
50 }
51
52 void Timetable::remove_row(unsigned i)
53 {
54         if(i>=rows.size())
55                 throw out_of_range("Timetable::remove_row");
56
57         list<Row>::iterator j = rows.begin();
58         advance(j, i);
59
60         check_update(j);
61         if(j==current_row)
62                 --current_row;
63
64         rows.erase(j);
65         signal_row_removed.emit(i);
66 }
67
68 const Timetable::Row &Timetable::get_row(unsigned i) const
69 {
70         if(i>=rows.size())
71                 throw out_of_range("Timetable::get_row");
72
73         list<Row>::const_iterator j = rows.begin();
74         advance(j, i);
75         return *j;
76 }
77
78 void Timetable::tick(const Time::TimeDelta &dt)
79 {
80         if(update_pending && !train.get_block_allocator().is_active())
81                 update_route();
82
83         if(current_row->type==DEPART)
84         {
85                 const Clock &clock = train.get_layout().get_clock();
86
87                 Time::TimeDelta t = clock.get_current_time();
88                 if(t<current_row->time)
89                         t += Time::day;
90
91                 Time::TimeDelta b = t-dt*clock.get_rate();
92                 if(b<current_row->time)
93                 {
94                         train.ai_message(Message("set-target-speed", train.get_maximum_speed()));
95                         ++current_row;
96                 }
97         }
98 }
99
100 void Timetable::save(list<DataFile::Statement> &st) const
101 {
102         for(list<Row>::const_iterator i=rows.begin(); i!=rows.end(); ++i)
103         {
104                 DataFile::Statement ss("row");
105                 i->save(ss.sub);
106                 st.push_back(ss);
107         }
108 }
109
110 void Timetable::check_update(list<Row>::const_iterator i)
111 {
112         for(list<Row>::const_iterator j=current_row; (j!=rows.end() && j!=i); ++j)
113                 if(j->type==ARRIVE)
114                         return;
115         update_pending = true;
116 }
117
118 void Timetable::update_route()
119 {
120         update_pending = false;
121         if(rows.empty())
122                 return;
123
124         list<Row>::iterator depart = rows.end();
125         for(list<Row>::iterator i=current_row;; )
126         {
127                 if(i==rows.end())
128                 {
129                         i = rows.begin();
130                         depart = rows.end();
131                 }
132
133                 if(i->type==DEPART)
134                         depart = i;
135                 else if(depart!=rows.end() && i->type==ARRIVE)
136                         break;
137
138                 ++i;
139                 if(i==current_row)
140                 {
141                         current_row = rows.end();
142                         return;
143                 }
144         }
145
146         train.ai_message(Message("clear-route"));
147
148         current_row = depart;
149         for(list<Row>::const_iterator i=depart; i!=rows.end(); ++i)
150         {
151                 if(i->type==ARRIVE)
152                 {
153                         train.ai_message(Message("set-destination-zone", i->zone));
154                         break;
155                 }
156                 else if(i->type==DEPART)
157                 {
158                         const Clock &clock = train.get_layout().get_clock();
159                         Time::TimeDelta dt = i->time-clock.get_current_time();
160                         while(dt<Time::zero)
161                                 dt += Time::day;
162                         dt /= clock.get_rate();
163                         train.ai_message(Message("set-departure-delay", dt));
164                 }
165         }
166 }
167
168 void Timetable::event(TrainAI &, const Message &msg)
169 {
170         if(msg.type=="arrived")
171         {
172                 if(current_row->type==ARRIVE)
173                 {
174                         current_row->time = train.get_layout().get_clock().get_current_time();
175                         unsigned i = distance(rows.begin(), current_row);
176                         signal_row_modified.emit(i, *current_row);
177                 }
178                 update_pending = true;
179         }
180 }
181
182
183 Timetable::Row::Row():
184         type(ARRIVE),
185         zone(0)
186 { }
187
188 void Timetable::Row::save(list<DataFile::Statement> &st) const
189 {
190         st.push_back((DataFile::Statement("type"), type));
191         st.push_back((DataFile::Statement("time"), time.raw()));
192         st.push_back((DataFile::Statement("zone"), zone->get_group(), zone->get_number()));
193 }
194
195
196 Timetable::Loader::Loader(Timetable &t, Layout &l):
197         DataFile::ObjectLoader<Timetable>(t),
198         layout(l)
199 {
200         add("row", &Loader::row);
201 }
202
203 void Timetable::Loader::row()
204 {
205         Row r;
206         load_sub(r, layout);
207         obj.rows.push_back(r);
208         obj.update_pending = true;
209 }
210
211
212 Timetable::Row::Loader::Loader(Row &r, Layout &l):
213         DataFile::ObjectLoader<Timetable::Row>(r),
214         layout(l)
215 {
216         add("zone", &Loader::zone);
217         add("time", &Loader::time);
218         add("type", &Row::type);
219 }
220
221 void Timetable::Row::Loader::zone(const string &name, unsigned number)
222 {
223         obj.zone = &layout.get_zone(name, number);
224 }
225
226 void Timetable::Row::Loader::time(Time::RawTime t)
227 {
228         obj.time = Time::TimeDelta(t);
229 }
230
231
232 void operator<<(LexicalConverter &conv, Timetable::RowType rt)
233 {
234         switch(rt)
235         {
236         case Timetable::ARRIVE: conv.result("ARRIVE"); return;
237         case Timetable::DEPART: conv.result("DEPART"); return;
238         default: throw lexical_error(format("conversion of RowType(%d) to string", rt));
239         }
240 }
241
242 void operator>>(const LexicalConverter &conv, Timetable::RowType &rt)
243 {
244         if(conv.get()=="ARRIVE")
245                 rt = Timetable::ARRIVE;
246         else if(conv.get()=="DEPART")
247                 rt = Timetable::DEPART;
248         else
249                 throw lexical_error(format("conversion of '%s' to RowType", conv.get()));
250 }
251
252 } // namespace R2C2