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