]> git.tdb.fi Git - r2c2.git/blob - source/libr2c2/trainrouter.cpp
Separate train routing logic to a class derived from TrainAI
[r2c2.git] / source / libr2c2 / trainrouter.cpp
1 #include "layout.h"
2 #include "route.h"
3 #include "trackiter.h"
4 #include "train.h"
5 #include "trainrouter.h"
6 #include "zone.h"
7
8 using namespace std;
9 using namespace Msp;
10
11 namespace R2C2 {
12
13 TrainRouter::TrainRouter(Train &t):
14         TrainAI(t),
15         priority(0),
16         arriving(false),
17         yielding_to(0)
18 {
19         train.get_layout().signal_block_reserved.connect(sigc::mem_fun(this, &TrainRouter::block_reserved));
20         train.signal_advanced.connect(sigc::mem_fun(this, &TrainRouter::train_advanced));
21 }
22
23 void TrainRouter::set_priority(int p)
24 {
25         priority = p;
26 }
27
28 void TrainRouter::yield_to(const Train &t)
29 {
30         yielding_to = &t;
31 }
32
33 bool TrainRouter::set_route(const Route *r)
34 {
35         train.free_noncritical_blocks();
36
37         Route *lead = 0;
38         if(r && train.is_placed())
39         {
40                 TrackIter first = train.get_tail_block().track_iter();
41                 TrackIter next = train.get_head_block().next().track_iter();
42                 if(!r->has_track(*next))
43                 {
44                         lead = Route::find(next, *r);
45                         if(!lead)
46                                 return false;
47                         create_lead_route(lead, lead);
48                 }
49                 else if(!r->has_track(*first))
50                         lead = create_lead_route(0, r);
51         }
52
53         routes.clear();
54         if(lead)
55                 routes.push_back(lead);
56         if(r)
57                 routes.push_back(r);
58         train.stop_at(0);
59         arriving = false;
60
61         train.reserve_more();
62
63         const Route *route = get_route();
64         signal_route_changed.emit(route);
65         signal_event.emit(Message("route-changed", route));
66
67         return true;
68 }
69
70 bool TrainRouter::go_to(Track &to)
71 {
72         if(!train.get_speed())
73         {
74                 for(BlockIter i=train.get_tail_block(); (i && i->get_train()==&train); i=i.next())
75                         if(i->has_track(to))
76                         {
77                                 signal_arrived.emit();
78                                 signal_event.emit(Message("arrived"));
79                                 return set_route(0);
80                         }
81         }
82
83         train.free_noncritical_blocks();
84
85         TrackIter next = train.get_head_block().next().track_iter();
86
87         Route *route = Route::find(next, to);
88         if(!route)
89                 return false;
90         create_lead_route(route, route);
91         return set_route(route);
92 }
93
94 bool TrainRouter::go_to(const Zone &to)
95 {
96         set<Track *> tracks;
97         for(BlockIter i=train.get_tail_block(); (i && i->get_train()==&train); i=i.next())
98                 tracks.insert(i->get_tracks().begin(), i->get_tracks().end());
99
100         const Zone::TrackSet &ztracks = to.get_tracks();
101         unsigned union_size = 0;
102         for(Zone::TrackSet::const_iterator i=ztracks.begin(); i!=ztracks.end(); ++i)
103                 union_size += tracks.count(*i);
104
105         if(union_size==tracks.size() || union_size==ztracks.size())
106         {
107                 signal_arrived.emit();
108                 signal_event.emit(Message("arrived"));
109                 return set_route(0);
110         }
111
112         train.free_noncritical_blocks();
113
114         TrackIter next = train.get_head_block().next().track_iter();
115
116         Route *route = Route::find(next, to);
117         if(!route)
118                 return false;
119         create_lead_route(route, route);
120         route->add_tracks(ztracks);
121         return set_route(route);
122 }
123
124 const Route *TrainRouter::get_route() const
125 {
126         if(routes.empty())
127                 return 0;
128         return routes.front();
129 }
130
131 void TrainRouter::message(const Message &msg)
132 {
133         if(msg.type=="set-route")
134         {
135                 if(msg.value.check_type<Route *>())
136                         set_route(msg.value.value<Route *>());
137                 else
138                         set_route(msg.value.value<const Route *>());
139         }
140         else if(msg.type=="clear-route")
141                 set_route(0);
142         else if(msg.type=="go-to-track")
143                 go_to(*msg.value.value<Track *>());
144         else if(msg.type=="go-to-zone")
145         {
146                 if(msg.value.check_type<Zone *>())
147                         go_to(*msg.value.value<Zone *>());
148                 else
149                         go_to(*msg.value.value<const Zone *>());
150         }
151 }
152
153 void TrainRouter::tick(const Time::TimeStamp &, const Time::TimeDelta &)
154 {
155         if(arriving && !train.get_speed())
156         {
157                 train.set_active(false);
158                 signal_arrived.emit();
159                 signal_event.emit(Message("arrived"));
160                 set_route(0);
161         }
162 }
163
164 void TrainRouter::save(list<DataFile::Statement> &st) const
165 {
166         if(!tag.empty())
167                 st.push_back((DataFile::Statement("tag"), tag));
168
169         st.push_back((DataFile::Statement("priority"), priority));
170
171         if(!routes.empty())
172         {
173                 RouteList::const_iterator i = routes.begin();
174                 for(; (i!=routes.end() && (*i)->is_temporary()); ++i) ;
175                 if(i!=routes.end())
176                         st.push_back((DataFile::Statement("route"), (*i)->get_name()));
177         }
178 }
179
180 void TrainRouter::block_reserved(Block &block, Train *t)
181 {
182         if(t!=&train)
183                 return;
184
185         yielding_to = 0;
186
187         BlockIter b_iter(&block, t->get_entry_to_block(block));
188         BlockIter b_iter_next;
189
190         RouteList::iterator route = routes.begin();
191         if(advance_route(route, block))
192         {
193                 // Check if the block is a turnout and set it to proper path
194                 if(unsigned tid = block.get_turnout_id())
195                 {
196                         int path = (*route)->get_turnout(tid);
197                         if(path>=0)
198                                 b_iter.track_iter()->set_active_path(path);
199                 }
200
201                 // Check if the next block is still part of the designated route
202                 b_iter_next = b_iter.next(*route);
203
204                 RouteList::iterator next_route = route;
205                 if(!advance_route(next_route, *b_iter_next))
206                 {
207                         train.stop_at(&block);
208                         return;
209                 }
210
211         }
212         else
213                 b_iter_next = b_iter.next();
214
215         // Check if there's another train and ask it to free the block if appropriate
216         if(b_iter_next)
217         {
218                 if(Train *other_train = b_iter_next->get_train())
219                 {
220                         /* There's another train ahead of us.  If it wants to exit the block
221                         from the same endpoint we're trying to enter from or the other way
222                         around, treat it as coming towards us.  Otherwise treat it as going
223                         in the same direction. */
224                         int other_entry = other_train->get_entry_to_block(*b_iter_next);
225                         if(other_entry<0)
226                                 throw logic_error("block reservation inconsistency");
227
228                         unsigned exit = b_iter_next.reverse().entry();
229                         unsigned other_exit = BlockIter(b_iter_next.block(), other_entry).reverse().entry();
230                         bool entry_conflict = (b_iter_next.entry()==other_exit);
231                         bool exit_conflict = (exit==static_cast<unsigned>(other_entry));
232                         // TODO: speed matching with preceding train
233
234                         // XXX Should invent a better way to get our counterpart from the other train
235                         TrainRouter *other_router = dynamic_cast<TrainRouter *>(other_train->get_tagged_ai("router"));
236                         int other_prio = (other_router ? other_router->get_priority() : 0);
237
238                         if(!entry_conflict && !exit_conflict && other_prio<priority)
239                         {
240                                 /* Ask a lesser priority train going to the same direction to free
241                                 the block for us */
242                                 other_train->free_block(*b_iter_next);
243                         }
244                         else if(other_train!=yielding_to && (other_prio<priority || (other_prio==priority && entry_conflict)))
245                         {
246                                 /* A lesser priority train is coming at us, we must ask it to free
247                                 enough blocks to get clear of it to avoid a potential deadlock */
248                                 BlockIter last_contested;
249                                 RouteList::iterator j = route;
250                                 for(BlockIter i=b_iter_next; (i && i->get_train()==other_train);)
251                                 {
252                                         if(!advance_route(j, *i))
253                                                 break;
254                                         last_contested = i;
255                                         i = i.next(*j);
256                                 }
257
258                                 if(last_contested)
259                                 {
260                                         if(other_train->free_block(*last_contested))
261                                                 other_router->yield_to(train);
262                                         else
263                                                 yield_to(*other_train);
264                                 }
265                         }
266                 }
267         }
268 }
269
270 void TrainRouter::train_advanced(Block &block)
271 {
272         // Check if we've reached the next route
273         if(routes.size()>1)
274         {
275                 unsigned entry = train.get_entry_to_block(block);
276                 Track &track = *block.get_endpoint(entry).track;
277                 const Route &route = **++routes.begin();
278                 if(route.has_track(track))
279                 {
280                         routes.pop_front();
281                         // XXX Exceptions?
282                         signal_event.emit(Message("route-changed", get_route()));
283                 }
284         }
285
286         BlockIter iter(&block, train.get_entry_to_block(block));
287         iter = iter.next();
288         if(iter && !is_on_route(*iter))
289                 arriving = true;
290 }
291
292 const Route *TrainRouter::get_route_for_block(const Block &block) const
293 {
294         const set<Track *> &tracks = block.get_tracks();
295         for(RouteList::const_iterator i=routes.begin(); i!=routes.end(); ++i)
296                 for(set<Track *>::const_iterator j=tracks.begin(); j!=tracks.end(); ++j)
297                         if((*i)->has_track(**j))
298                                 return *i;
299
300         return 0;
301 }
302
303 Route *TrainRouter::create_lead_route(Route *lead, const Route *target)
304 {
305         if(!lead)
306         {
307                 lead = new Route(train.get_layout());
308                 lead->set_name("Lead");
309                 lead->set_temporary(true);
310         }
311
312         set<Track *> tracks;
313         for(BlockIter i=train.get_tail_block(); (i && i->get_train()==&train); i=i.next())
314         {
315                 const set<Track *> &btracks = i->get_tracks();
316                 for(set<Track *>::const_iterator j=btracks.begin(); j!=btracks.end(); ++j)
317                         if(!target || !target->has_track(**j))
318                                 tracks.insert(*j);
319         }
320
321         lead->add_tracks(tracks);
322
323         return lead;
324 }
325
326 bool TrainRouter::advance_route(RouteList::iterator &iter, const Block &block)
327 {
328         const set<Track *> &tracks = block.get_tracks();
329         for(; iter!=routes.end(); ++iter)
330                 for(set<Track *>::const_iterator j=tracks.begin(); j!=tracks.end(); ++j)
331                         if((*iter)->has_track(**j))
332                                 return true;
333
334         return false;
335 }
336
337 bool TrainRouter::is_on_route(const Block &block)
338 {
339         RouteList::iterator iter = routes.begin();
340         return advance_route(iter, block);
341 }
342
343
344 TrainRouter::Loader::Loader(TrainRouter &r):
345         DataFile::ObjectLoader<TrainRouter>(r)
346 {
347         add("priority", &TrainRouter::priority);
348         add("route",    &Loader::route);
349         add("tag",      &Loader::tag);
350 }
351
352 void TrainRouter::Loader::route(const string &n)
353 {
354         obj.set_route(&obj.train.get_layout().get_route(n));
355 }
356
357 void TrainRouter::Loader::tag(const string &t)
358 {
359         obj.set_tag(t);
360 }
361
362 } // namespace R2C2