]> git.tdb.fi Git - r2c2.git/blob - source/libr2c2/trainrouter.cpp
Get rid of the TrainAI tagging system
[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         st.push_back((DataFile::Statement("priority"), priority));
167
168         if(!routes.empty())
169         {
170                 RouteList::const_iterator i = routes.begin();
171                 for(; (i!=routes.end() && (*i)->is_temporary()); ++i) ;
172                 if(i!=routes.end())
173                         st.push_back((DataFile::Statement("route"), (*i)->get_name()));
174         }
175 }
176
177 void TrainRouter::block_reserved(Block &block, Train *t)
178 {
179         if(t!=&train)
180                 return;
181
182         yielding_to = 0;
183
184         BlockIter b_iter(&block, t->get_entry_to_block(block));
185         BlockIter b_iter_next;
186
187         RouteList::iterator route = routes.begin();
188         if(advance_route(route, block))
189         {
190                 // Check if the block is a turnout and set it to proper path
191                 if(unsigned tid = block.get_turnout_id())
192                 {
193                         int path = (*route)->get_turnout(tid);
194                         if(path>=0)
195                                 b_iter.track_iter()->set_active_path(path);
196                 }
197
198                 // Check if the next block is still part of the designated route
199                 b_iter_next = b_iter.next(*route);
200
201                 RouteList::iterator next_route = route;
202                 if(!advance_route(next_route, *b_iter_next))
203                 {
204                         train.stop_at(&block);
205                         return;
206                 }
207
208         }
209         else
210                 b_iter_next = b_iter.next();
211
212         // Check if there's another train and ask it to free the block if appropriate
213         if(b_iter_next)
214         {
215                 if(Train *other_train = b_iter_next->get_train())
216                 {
217                         /* There's another train ahead of us.  If it wants to exit the block
218                         from the same endpoint we're trying to enter from or the other way
219                         around, treat it as coming towards us.  Otherwise treat it as going
220                         in the same direction. */
221                         int other_entry = other_train->get_entry_to_block(*b_iter_next);
222                         if(other_entry<0)
223                                 throw logic_error("block reservation inconsistency");
224
225                         unsigned exit = b_iter_next.reverse().entry();
226                         unsigned other_exit = BlockIter(b_iter_next.block(), other_entry).reverse().entry();
227                         bool entry_conflict = (b_iter_next.entry()==other_exit);
228                         bool exit_conflict = (exit==static_cast<unsigned>(other_entry));
229                         // TODO: speed matching with preceding train
230
231                         TrainRouter *other_router = other_train->get_ai_of_type<TrainRouter>();
232                         int other_prio = (other_router ? other_router->get_priority() : 0);
233
234                         if(!entry_conflict && !exit_conflict && other_prio<priority)
235                         {
236                                 /* Ask a lesser priority train going to the same direction to free
237                                 the block for us */
238                                 other_train->free_block(*b_iter_next);
239                         }
240                         else if(other_train!=yielding_to && (other_prio<priority || (other_prio==priority && entry_conflict)))
241                         {
242                                 /* A lesser priority train is coming at us, we must ask it to free
243                                 enough blocks to get clear of it to avoid a potential deadlock */
244                                 BlockIter last_contested;
245                                 RouteList::iterator j = route;
246                                 for(BlockIter i=b_iter_next; (i && i->get_train()==other_train);)
247                                 {
248                                         if(!advance_route(j, *i))
249                                                 break;
250                                         last_contested = i;
251                                         i = i.next(*j);
252                                 }
253
254                                 if(last_contested)
255                                 {
256                                         if(other_train->free_block(*last_contested))
257                                                 other_router->yield_to(train);
258                                         else
259                                                 yield_to(*other_train);
260                                 }
261                         }
262                 }
263         }
264 }
265
266 void TrainRouter::train_advanced(Block &block)
267 {
268         // Check if we've reached the next route
269         if(routes.size()>1)
270         {
271                 unsigned entry = train.get_entry_to_block(block);
272                 Track &track = *block.get_endpoint(entry).track;
273                 const Route &route = **++routes.begin();
274                 if(route.has_track(track))
275                 {
276                         routes.pop_front();
277                         // XXX Exceptions?
278                         signal_event.emit(Message("route-changed", get_route()));
279                 }
280         }
281
282         if(!routes.empty())
283         {
284                 BlockIter iter(&block, train.get_entry_to_block(block));
285                 iter = iter.next();
286                 if(iter && !is_on_route(*iter))
287                         arriving = true;
288         }
289 }
290
291 const Route *TrainRouter::get_route_for_block(const Block &block) const
292 {
293         const set<Track *> &tracks = block.get_tracks();
294         for(RouteList::const_iterator i=routes.begin(); i!=routes.end(); ++i)
295                 for(set<Track *>::const_iterator j=tracks.begin(); j!=tracks.end(); ++j)
296                         if((*i)->has_track(**j))
297                                 return *i;
298
299         return 0;
300 }
301
302 Route *TrainRouter::create_lead_route(Route *lead, const Route *target)
303 {
304         if(!lead)
305         {
306                 lead = new Route(train.get_layout());
307                 lead->set_name("Lead");
308                 lead->set_temporary(true);
309         }
310
311         set<Track *> tracks;
312         for(BlockIter i=train.get_tail_block(); (i && i->get_train()==&train); i=i.next())
313         {
314                 const set<Track *> &btracks = i->get_tracks();
315                 for(set<Track *>::const_iterator j=btracks.begin(); j!=btracks.end(); ++j)
316                         if(!target || !target->has_track(**j))
317                                 tracks.insert(*j);
318         }
319
320         lead->add_tracks(tracks);
321
322         return lead;
323 }
324
325 bool TrainRouter::advance_route(RouteList::iterator &iter, const Block &block)
326 {
327         const set<Track *> &tracks = block.get_tracks();
328         for(; iter!=routes.end(); ++iter)
329                 for(set<Track *>::const_iterator j=tracks.begin(); j!=tracks.end(); ++j)
330                         if((*iter)->has_track(**j))
331                                 return true;
332
333         return false;
334 }
335
336 bool TrainRouter::is_on_route(const Block &block)
337 {
338         RouteList::iterator iter = routes.begin();
339         return advance_route(iter, block);
340 }
341
342
343 TrainRouter::Loader::Loader(TrainRouter &r):
344         DataFile::ObjectLoader<TrainRouter>(r)
345 {
346         add("priority", &TrainRouter::priority);
347         add("route",    &Loader::route);
348 }
349
350 void TrainRouter::Loader::route(const string &n)
351 {
352         obj.set_route(&obj.train.get_layout().get_route(n));
353 }
354
355 } // namespace R2C2