]> git.tdb.fi Git - r2c2.git/blob - source/libr2c2/blockallocator.cpp
Set pending_block when a block can't be allocated
[r2c2.git] / source / libr2c2 / blockallocator.cpp
1 #include <msp/core/maputils.h>
2 #include <msp/core/raii.h>
3 #include "blockallocator.h"
4 #include "block.h"
5 #include "catalogue.h"
6 #include "driver.h"
7 #include "layout.h"
8 #include "trackcircuit.h"
9 #include "trackiter.h"
10 #include "train.h"
11 #include "vehicle.h"
12
13 using namespace std;
14 using namespace Msp;
15
16 namespace R2C2 {
17
18 struct BlockAllocator::BlockMatch
19 {
20         const Block &block;
21
22         BlockMatch(const Block &b): block(b) { }
23
24         bool operator()(const BlockIter &bi) const { return &*bi==&block; }
25 };
26
27
28 BlockAllocator::BlockAllocator(Train &t):
29         train(t),
30         cur_blocks_end(blocks.end()),
31         pending_block(0),
32         stop_at_block(0),
33         reserving(false)
34 {
35         Layout &layout = train.get_layout();
36         layout.signal_block_reserved.connect(sigc::mem_fun(this, &BlockAllocator::block_reserved));
37         layout.signal_sensor_state_changed.connect(sigc::mem_fun(this, &BlockAllocator::sensor_state_changed));
38
39         const set<Track *> &tracks = layout.get_all<Track>();
40         for(set<Track *>::const_iterator i=tracks.begin(); i!=tracks.end(); ++i)
41                 if((*i)->get_turnout_id())
42                 {
43                         (*i)->signal_path_changing.connect(sigc::hide(sigc::bind(sigc::mem_fun(this, &BlockAllocator::turnout_path_changing), sigc::ref(**i))));
44                         (*i)->signal_path_changed.connect(sigc::hide(sigc::bind(sigc::mem_fun(this, &BlockAllocator::turnout_path_changed), sigc::ref(**i))));
45                 }
46 }
47
48 void BlockAllocator::set_active(bool a)
49 {
50         active = a;
51         if(active)
52                 reserve_more();
53         else
54         {
55                 release_blocks_end(cur_blocks_end);
56                 pending_block = 0;
57         }
58 }
59
60 void BlockAllocator::start_from(const BlockIter &block)
61 {
62         if(!block)
63                 throw invalid_argument("BlockAllocator::start_from");
64
65         clear();
66         reserve_block(block);
67 }
68
69 void BlockAllocator::rewind_to(const Block &block)
70 {
71         if(!active)
72                 return;
73
74         BlockList::iterator i = find_if(cur_blocks_end, blocks.end(), BlockMatch(block));
75         if(i!=blocks.end())
76         {
77                 release_blocks_end(i);
78                 reserve_more();
79         }
80 }
81
82 void BlockAllocator::clear()
83 {
84         active = false;
85         release_blocks_begin(blocks.end());
86         pending_block = 0;
87         stop_at_block = 0;
88 }
89
90 void BlockAllocator::stop_at(const Block *block)
91 {
92         stop_at_block = block;
93         if(active && !block)
94                 reserve_more();
95 }
96
97 const BlockIter &BlockAllocator::first() const
98 {
99         if(blocks.empty())
100                 throw logic_error("no blocks");
101         return blocks.front();
102 }
103
104 const BlockIter &BlockAllocator::last() const
105 {
106         if(blocks.empty())
107                 throw logic_error("no blocks");
108         BlockList::const_iterator i = --blocks.end();
109         if(i->block()==pending_block)
110                 --i;
111         return *i;
112 }
113
114 const BlockIter &BlockAllocator::last_current() const
115 {
116         if(blocks.empty())
117                 throw logic_error("no blocks");
118         if(cur_blocks_end==blocks.begin())
119                 throw logic_error("internal error (no current blocks)");
120         BlockList::const_iterator i = cur_blocks_end;
121         return *--i;
122 }
123
124 const BlockIter &BlockAllocator::iter_for(const Block &block) const
125 {
126         BlockList::const_iterator i = find_if(blocks.begin(), blocks.end(), BlockMatch(block));
127         if(i==blocks.end())
128                 throw key_error(&block);
129         return *i;
130 }
131
132 bool BlockAllocator::has_block(const Block &block) const
133 {
134         return find_if(blocks.begin(), blocks.end(), BlockMatch(block))!=blocks.end();
135 }
136
137 bool BlockAllocator::is_block_current(const Block &block) const
138 {
139         BlockList::const_iterator end = cur_blocks_end;
140         return find_if(blocks.begin(), end, BlockMatch(block))!=cur_blocks_end;
141 }
142
143 void BlockAllocator::reserve_more()
144 {
145         if(blocks.empty())
146                 throw logic_error("no blocks");
147
148         BlockIter start = blocks.back();
149         if(&*start==stop_at_block)
150                 return;
151         else if(&*start==pending_block)
152         {
153                 TrackIter track = start.track_iter();
154                 if(!track.endpoint().has_path(track->get_active_path()))
155                         return;
156         }
157
158         pending_block = 0;
159
160         // See how many sensor blocks and how much track we already have
161         unsigned nsens = 0;
162         float dist = 0;
163         for(BlockList::const_iterator i=cur_blocks_end; i!=blocks.end(); ++i)
164         {
165                 if((*i)->get_sensor_id())
166                         ++nsens;
167                 if(nsens>0)
168                         dist += (*i)->get_path_length(i->entry());
169         }
170
171         float approach_margin = 50*train.get_layout().get_catalogue().get_scale();
172         float min_dist = train.get_controller().get_braking_distance()*1.3+approach_margin*2;
173
174         BlockIter block = start;
175
176         SetFlag setf(reserving);
177
178         while(1)
179         {
180                 BlockIter prev = block;
181                 block = block.next();
182                 if(!block || block->get_endpoints().size()<2)
183                         // The track ends here
184                         break;
185
186                 if(block->get_turnout_id() && !prev->get_turnout_id())
187                 {
188                         /* We are arriving at a turnout.  See if we have enough blocks and
189                         distance reserved. */
190                         if(nsens>=3 && dist>=min_dist)
191                                 break;
192                 }
193
194                 if(!reserve_block(block))
195                 {
196                         pending_block = &*block;
197                         break;
198                 }
199
200                 if(cur_blocks_end==blocks.end())
201                         --cur_blocks_end;
202
203                 TrackIter track = block.track_iter();
204                 if(track->is_path_changing())
205                 {
206                         pending_block = &*block;
207                         break;
208                 }
209                 else
210                 {
211                         const TrackType::Endpoint &entry_ep = track.endpoint();
212                         unsigned path = track->get_active_path();
213                         if(!entry_ep.has_path(path))
214                         {
215                                 const TrackType::Endpoint &exit_ep = track.reverse().endpoint();
216                                 if(entry_ep.has_common_paths(exit_ep))
217                                 {
218                                         unsigned mask = entry_ep.paths&exit_ep.paths;
219                                         for(path=0; mask>1; ++path, mask>>=1) ;
220
221                                         track->set_active_path(path);
222                                         if(track->is_path_changing())
223                                         {
224                                                 pending_block = &*block;
225                                                 break;
226                                         }
227                                 }
228                                 else
229                                         // XXX Do something here
230                                         break;
231                         }
232                 }
233
234                 if(&*block==stop_at_block)
235                         break;
236
237                 if(block->get_sensor_id())
238                         ++nsens;
239                 if(nsens>0)
240                         dist += block->get_path_length(block.entry());
241         }
242
243         // Make any sensorless blocks at the beginning immediately current
244         while(cur_blocks_end!=blocks.end() && !(*cur_blocks_end)->get_sensor_id())
245                 ++cur_blocks_end;
246 }
247
248 bool BlockAllocator::reserve_block(const BlockIter &block)
249 {
250         /* Add it to the list first to present a consistent state in block_reserved
251         signal. */
252         blocks.push_back(block);
253         try
254         {
255                 if(!block->reserve(&train))
256                 {
257                         blocks.pop_back();
258                         return false;
259                 }
260
261                 return true;
262         }
263         catch(...)
264         {
265                 blocks.pop_back();
266                 throw;
267         }
268 }
269
270 void BlockAllocator::release_blocks_begin(const BlockList::iterator &end)
271 {
272         for(BlockList::iterator i=blocks.begin(); i!=end; )
273                 release_block(i++);
274 }
275
276 void BlockAllocator::release_blocks_end(const BlockList::iterator &begin)
277 {
278         // Guard against decrementing blocks.begin()
279         if(begin==blocks.begin())
280                 return release_blocks_begin(blocks.end());
281
282         if(begin==blocks.end())
283                 return;
284
285         /* Release the blocks in reverse order so that a consistent state is
286         presented in block_reserved signal. */
287         bool done = false;
288         for(BlockList::iterator i=--blocks.end(); !done; )
289         {
290                 done = (i==begin);
291                 release_block(i--);
292         }
293 }
294
295 void BlockAllocator::release_block(const BlockList::iterator &i)
296 {
297         if(i==cur_blocks_end)
298                 ++cur_blocks_end;
299         if(&**i==pending_block)
300                 pending_block = 0;
301
302         Block &block = **i;
303         blocks.erase(i);
304         block.reserve(0);
305 }
306
307 void BlockAllocator::reverse()
308 {
309         release_blocks_end(cur_blocks_end);
310         blocks.reverse();
311         for(BlockList::iterator i=blocks.begin(); i!=blocks.end(); ++i)
312                 *i = i->reverse();
313
314         if(active)
315                 reserve_more();
316 }
317
318 void BlockAllocator::turnout_path_changing(Track &track)
319 {
320         BlockList::iterator i = find_if(blocks.begin(), blocks.end(), BlockMatch(track.get_block()));
321         if(i!=blocks.end())
322         {
323                 ++i;
324                 release_blocks_end(i);
325                 pending_block = &track.get_block();
326         }
327 }
328
329 void BlockAllocator::turnout_path_changed(Track &track)
330 {
331         if(&track.get_block()==pending_block && !reserving)
332                 reserve_more();
333 }
334
335 void BlockAllocator::block_reserved(Block &block, const Train *tr)
336 {
337         if(&block==pending_block && !tr && !reserving)
338                 reserve_more();
339 }
340
341 void BlockAllocator::sensor_state_changed(Sensor &sensor, Sensor::State state)
342 {
343         Block *block = 0;
344         if(TrackCircuit *tc = dynamic_cast<TrackCircuit *>(&sensor))
345                 block = &tc->get_block();
346         else
347                 return;
348
349         if(block->get_train()!=&train)
350                 return;
351
352         if(state==Sensor::MAYBE_ACTIVE)
353         {
354                 // Find the first sensor block from our reserved blocks that isn't this sensor
355                 BlockList::iterator end;
356                 unsigned result = 0;
357                 for(end=cur_blocks_end; end!=blocks.end(); ++end)
358                         if((*end)->get_sensor_id())
359                         {
360                                 if(&**end!=block)
361                                 {
362                                         if(result==0)
363                                                 result = 2;
364                                         else if(result==1)
365                                                 break;
366                                 }
367                                 else if(result==0)
368                                         result = 1;
369                                 else if(result==2)
370                                         result = 3;
371                         }
372
373                 if(result==1)
374                 {
375                         // Move blocks up to the next sensor to our current blocks
376                         for(BlockList::iterator j=cur_blocks_end; j!=end; ++j)
377                                 train.signal_advanced.emit(**j);
378                         cur_blocks_end = end;
379
380                         if(active)
381                                 reserve_more();
382                 }
383                 else if(result==3)
384                         train.get_layout().emergency("Sensor for "+train.get_name()+" triggered out of order");
385         }
386         else if(state==Sensor::INACTIVE)
387         {
388                 const Vehicle &veh = train.get_vehicle(train.get_controller().get_reverse() ? 0 : train.get_n_vehicles()-1);
389                 const Block &veh_block = veh.get_track()->get_block();
390                 const Driver &driver = train.get_layout().get_driver();
391
392                 /* Sensors aren't guaranteed to be detriggered in order.  Go through the
393                 block list and locate the first sensor that's still active. */
394                 BlockList::iterator end = blocks.end();
395                 for(BlockList::iterator i=blocks.begin(); i!=cur_blocks_end; ++i)
396                 {
397                         // Avoid freeing blocks that still hold the train's vehicles
398                         if(&**i==&veh_block)
399                                 break;
400
401                         if((*i)->get_sensor_id())
402                         {
403                                 if(driver.get_sensor((*i)->get_sensor_id()))
404                                         break;
405                                 else
406                                         end = i;
407                         }
408                 }
409                 
410                 if(end!=blocks.end())
411                         // Free blocks up to the last inactive sensor
412                         release_blocks_begin(++end);
413         }
414 }
415
416 void BlockAllocator::save(list<DataFile::Statement> &st) const
417 {
418         if(!blocks.empty() && cur_blocks_end!=blocks.begin())
419         {
420                 BlockList cur_blocks(blocks.begin(), BlockList::const_iterator(cur_blocks_end));
421                 BlockIter prev;
422                 if(train.get_controller().get_reverse())
423                 {
424                         cur_blocks.reverse();
425                         prev = cur_blocks.front().next();
426                 }
427                 else
428                         prev = cur_blocks.front().flip();
429
430                 st.push_back((DataFile::Statement("hint"), prev->get_id()));
431
432                 for(BlockList::const_iterator i=cur_blocks.begin(); i!=cur_blocks.end(); ++i)
433                         st.push_back((DataFile::Statement("block"), (*i)->get_id()));
434         }
435 }
436
437
438 BlockAllocator::Loader::Loader(BlockAllocator &ba):
439         DataFile::ObjectLoader<BlockAllocator>(ba),
440         valid(true)
441 {
442         add("block", &Loader::block);
443         add("hint",  &Loader::hint);
444 }
445
446 void BlockAllocator::Loader::block(unsigned id)
447 {
448         if(!valid)
449                 return;
450
451         Block *blk;
452         try
453         {
454                 blk = &obj.train.get_layout().get_block(id);
455         }
456         catch(const key_error &)
457         {
458                 valid = false;
459                 return;
460         }
461
462         int entry = -1;
463         if(prev_block)
464                 entry = blk->get_endpoint_by_link(*prev_block);
465         if(entry<0)
466                 entry = 0;
467
468         obj.blocks.push_back(BlockIter(blk, entry));
469         blk->reserve(&obj.train);
470
471         if(blk->get_sensor_id())
472                 obj.train.get_layout().get_driver().set_sensor(blk->get_sensor_id(), true);
473
474         prev_block = blk;
475 }
476
477 void BlockAllocator::Loader::hint(unsigned id)
478 {
479         try
480         {
481                 prev_block = &obj.train.get_layout().get_block(id);
482         }
483         catch(const key_error &)
484         {
485                 valid = false;
486         }
487 }
488
489 } // namespace R2C2