]> git.tdb.fi Git - r2c2.git/blob - source/libr2c2/timetable.cpp
Support waypoints in Timetable
[r2c2.git] / source / 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", i->target));
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                 else if(i->type==THROUGH)
166                         train.ai_message(Message("add-waypoint", i->target));
167         }
168 }
169
170 void Timetable::event(TrainAI &, const Message &msg)
171 {
172         if(msg.type=="arrived")
173         {
174                 if(current_row->type==ARRIVE)
175                         record_time();
176                 update_pending = true;
177         }
178         else if(msg.type=="waypoint-reached")
179         {
180                 const TrackChain *wp = msg.value.value<const TrackChain *>();
181                 if(current_row->type==THROUGH && current_row->target==wp)
182                 {
183                         record_time();
184                         ++current_row;
185                 }
186         }
187 }
188
189 void Timetable::record_time()
190 {
191         current_row->time = train.get_layout().get_clock().get_current_time();
192         unsigned i = distance(rows.begin(), current_row);
193         signal_row_modified.emit(i, *current_row);
194 }
195
196
197 Timetable::Row::Row():
198         type(ARRIVE),
199         target(0)
200 { }
201
202 void Timetable::Row::save(list<DataFile::Statement> &st) const
203 {
204         st.push_back((DataFile::Statement("type"), type));
205         st.push_back((DataFile::Statement("time"), time.raw()));
206         st.push_back(target->save_reference());
207 }
208
209
210 Timetable::Loader::Loader(Timetable &t, Layout &l):
211         DataFile::ObjectLoader<Timetable>(t),
212         layout(l)
213 {
214         add("row", &Loader::row);
215 }
216
217 void Timetable::Loader::row()
218 {
219         Row r;
220         load_sub(r, layout);
221         obj.rows.push_back(r);
222         obj.update_pending = true;
223 }
224
225
226 Timetable::Row::Loader::Loader(Row &r, Layout &l):
227         DataFile::ObjectLoader<Timetable::Row>(r),
228         layout(l)
229 {
230         add("block", &Loader::block);
231         add("time", &Loader::time);
232         add("type", &Row::type);
233         add("zone", &Loader::zone);
234 }
235
236 void Timetable::Row::Loader::block(unsigned id)
237 {
238         obj.target = &layout.get_block(id);
239 }
240
241 void Timetable::Row::Loader::time(Time::RawTime t)
242 {
243         obj.time = Time::TimeDelta(t);
244 }
245
246 void Timetable::Row::Loader::zone(const string &name, unsigned number)
247 {
248         obj.target = &layout.get_zone(name, number);
249 }
250
251
252 void operator<<(LexicalConverter &conv, Timetable::RowType rt)
253 {
254         switch(rt)
255         {
256         case Timetable::ARRIVE: conv.result("ARRIVE"); return;
257         case Timetable::DEPART: conv.result("DEPART"); return;
258         case Timetable::THROUGH: conv.result("THROUGH"); return;
259         default: throw lexical_error(format("conversion of RowType(%d) to string", rt));
260         }
261 }
262
263 void operator>>(const LexicalConverter &conv, Timetable::RowType &rt)
264 {
265         if(conv.get()=="ARRIVE")
266                 rt = Timetable::ARRIVE;
267         else if(conv.get()=="DEPART")
268                 rt = Timetable::DEPART;
269         else if(conv.get()=="THROUGH")
270                 rt = Timetable::THROUGH;
271         else
272                 throw lexical_error(format("conversion of '%s' to RowType", conv.get()));
273 }
274
275 } // namespace R2C2