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