]> git.tdb.fi Git - r2c2.git/blob - source/libr2c2/trainrouter.cpp
Only consider arrival when a route is set
[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         if(!routes.empty())
287         {
288                 BlockIter iter(&block, train.get_entry_to_block(block));
289                 iter = iter.next();
290                 if(iter && !is_on_route(*iter))
291                         arriving = true;
292         }
293 }
294
295 const Route *TrainRouter::get_route_for_block(const Block &block) const
296 {
297         const set<Track *> &tracks = block.get_tracks();
298         for(RouteList::const_iterator i=routes.begin(); i!=routes.end(); ++i)
299                 for(set<Track *>::const_iterator j=tracks.begin(); j!=tracks.end(); ++j)
300                         if((*i)->has_track(**j))
301                                 return *i;
302
303         return 0;
304 }
305
306 Route *TrainRouter::create_lead_route(Route *lead, const Route *target)
307 {
308         if(!lead)
309         {
310                 lead = new Route(train.get_layout());
311                 lead->set_name("Lead");
312                 lead->set_temporary(true);
313         }
314
315         set<Track *> tracks;
316         for(BlockIter i=train.get_tail_block(); (i && i->get_train()==&train); i=i.next())
317         {
318                 const set<Track *> &btracks = i->get_tracks();
319                 for(set<Track *>::const_iterator j=btracks.begin(); j!=btracks.end(); ++j)
320                         if(!target || !target->has_track(**j))
321                                 tracks.insert(*j);
322         }
323
324         lead->add_tracks(tracks);
325
326         return lead;
327 }
328
329 bool TrainRouter::advance_route(RouteList::iterator &iter, const Block &block)
330 {
331         const set<Track *> &tracks = block.get_tracks();
332         for(; iter!=routes.end(); ++iter)
333                 for(set<Track *>::const_iterator j=tracks.begin(); j!=tracks.end(); ++j)
334                         if((*iter)->has_track(**j))
335                                 return true;
336
337         return false;
338 }
339
340 bool TrainRouter::is_on_route(const Block &block)
341 {
342         RouteList::iterator iter = routes.begin();
343         return advance_route(iter, block);
344 }
345
346
347 TrainRouter::Loader::Loader(TrainRouter &r):
348         DataFile::ObjectLoader<TrainRouter>(r)
349 {
350         add("priority", &TrainRouter::priority);
351         add("route",    &Loader::route);
352         add("tag",      &Loader::tag);
353 }
354
355 void TrainRouter::Loader::route(const string &n)
356 {
357         obj.set_route(&obj.train.get_layout().get_route(n));
358 }
359
360 void TrainRouter::Loader::tag(const string &t)
361 {
362         obj.set_tag(t);
363 }
364
365 } // namespace R2C2