]> git.tdb.fi Git - r2c2.git/blob - source/libr2c2/train.cpp
Separate train routing logic to a class derived from TrainAI
[r2c2.git] / source / libr2c2 / train.cpp
1 #include <algorithm>
2 #include <cmath>
3 #include <msp/core/maputils.h>
4 #include <msp/strings/format.h>
5 #include <msp/time/units.h>
6 #include <msp/time/utils.h>
7 #include "aicontrol.h"
8 #include "catalogue.h"
9 #include "driver.h"
10 #include "layout.h"
11 #include "route.h"
12 #include "simplecontroller.h"
13 #include "speedquantizer.h"
14 #include "timetable.h"
15 #include "trackiter.h"
16 #include "tracktype.h"
17 #include "train.h"
18 #include "trainrouter.h"
19 #include "vehicle.h"
20 #include "vehicletype.h"
21 #include "zone.h"
22
23 using namespace std;
24 using namespace Msp;
25
26 namespace {
27
28 struct SetFlag
29 {
30         bool &flag;
31
32         SetFlag(bool &f): flag(f) { flag = true; }
33         ~SetFlag() { flag = false; }
34 };
35
36 }
37
38
39 namespace R2C2 {
40
41 Train::Train(Layout &l, const VehicleType &t, unsigned a, const string &p):
42         layout(l),
43         loco_type(t),
44         address(a),
45         protocol(p),
46         preceding_train(0),
47         cur_blocks_end(blocks.end()),
48         clear_blocks_end(blocks.end()),
49         pending_block(0),
50         reserving(false),
51         advancing(false),
52         controller(new SimpleController),
53         active(false),
54         current_speed_step(0),
55         speed_changing(false),
56         reverse(false),
57         functions(0),
58         travel_dist(0),
59         pure_speed(false),
60         speed_quantizer(0),
61         accurate_position(false),
62         overshoot_dist(false)
63 {
64         if(!loco_type.is_locomotive())
65                 throw invalid_argument("Train::Train");
66
67         unsigned speed_steps = layout.get_driver().get_protocol_speed_steps(protocol);
68         if(speed_steps)
69                 speed_quantizer = new SpeedQuantizer(speed_steps);
70
71         vehicles.push_back(new Vehicle(layout, loco_type));
72
73         layout.add_train(*this);
74
75         layout.get_driver().add_loco(address, protocol, loco_type);
76         layout.get_driver().signal_loco_speed.connect(sigc::mem_fun(this, &Train::loco_speed_event));
77         layout.get_driver().signal_loco_function.connect(sigc::mem_fun(this, &Train::loco_func_event));
78
79         layout.signal_block_reserved.connect(sigc::mem_fun(this, &Train::block_reserved));
80         layout.signal_block_state_changed.connect(sigc::mem_fun(this, &Train::block_state_changed));
81
82         layout.get_driver().signal_halt.connect(sigc::mem_fun(this, &Train::halt_event));
83
84         const set<Track *> &tracks = layout.get_tracks();
85         for(set<Track *>::const_iterator i=tracks.begin(); i!=tracks.end(); ++i)
86                 if((*i)->get_turnout_id())
87                         (*i)->signal_path_changed.connect(sigc::hide(sigc::bind(sigc::mem_fun(this, &Train::turnout_path_changed), sigc::ref(**i))));
88
89         controller->signal_control_changed.connect(sigc::mem_fun(this, &Train::control_changed));
90 }
91
92 Train::~Train()
93 {
94         delete controller;
95         for(vector<Vehicle *>::iterator i=vehicles.begin(); i!=vehicles.end(); ++i)
96                 delete *i;
97         layout.remove_train(*this);
98 }
99
100 void Train::set_name(const string &n)
101 {
102         name = n;
103
104         signal_name_changed.emit(name);
105 }
106
107 void Train::add_vehicle(const VehicleType &vt)
108 {
109         Vehicle *veh = new Vehicle(layout, vt);
110         vehicles.back()->attach_back(*veh);
111         vehicles.push_back(veh);
112 }
113
114 void Train::remove_vehicle(unsigned i)
115 {
116         if(i>=vehicles.size())
117                 throw out_of_range("Train::remove_vehicle");
118         if(i==0)
119                 throw logic_error("can't remove locomotive");
120         delete vehicles[i];
121         vehicles.erase(vehicles.begin()+i);
122         if(i<vehicles.size())
123                 vehicles[i-1]->attach_back(*vehicles[i]);
124 }
125
126 unsigned Train::get_n_vehicles() const
127 {
128         return vehicles.size();
129 }
130
131 Vehicle &Train::get_vehicle(unsigned i)
132 {
133         if(i>=vehicles.size())
134                 throw out_of_range("Train::get_vehicle");
135         return *vehicles[i];
136 }
137
138 const Vehicle &Train::get_vehicle(unsigned i) const
139 {
140         if(i>=vehicles.size())
141                 throw out_of_range("Train::get_vehicle");
142         return *vehicles[i];
143 }
144
145 void Train::set_control(const string &n, float v)
146 {
147         controller->set_control(n, v);
148 }
149
150 void Train::set_active(bool a)
151 {
152         if(a==active)
153                 return;
154         if(!a && controller->get_speed())
155                 throw logic_error("moving");
156
157         active = a;
158         if(active)
159         {
160                 stop_timeout = Time::TimeStamp();
161                 reserve_more();
162         }
163         else
164                 stop_timeout = Time::now()+2*Time::sec;
165 }
166
167 void Train::set_function(unsigned func, bool state)
168 {
169         if(!loco_type.get_functions().count(func))
170                 throw invalid_argument("Train::set_function");
171         layout.get_driver().set_loco_function(address, func, state);
172 }
173
174 float Train::get_control(const string &ctrl) const
175 {
176         return controller->get_control(ctrl).value;
177 }
178
179 float Train::get_speed() const
180 {
181         return controller->get_speed();
182 }
183
184 float Train::get_quantized_speed() const
185 {
186         if(speed_quantizer)
187                 return speed_quantizer->quantize_speed(controller->get_speed());
188         else
189                 return controller->get_speed();
190 }
191
192 bool Train::get_function(unsigned func) const
193 {
194         return (functions>>func)&1;
195 }
196
197 void Train::add_ai(TrainAI &ai)
198 {
199         ais.push_back(&ai);
200         ai.signal_event.connect(sigc::bind<0>(signal_ai_event, sigc::ref(ai)));
201 }
202
203 void Train::remove_ai(TrainAI &ai)
204 {
205         list<TrainAI *>::iterator i = find(ais.begin(), ais.end(), &ai);
206         if(i!=ais.end())
207                 ais.erase(i);
208 }
209
210 TrainAI *Train::get_tagged_ai(const string &tag) const
211 {
212         for(list<TrainAI *>::const_iterator i=ais.begin(); i!=ais.end(); ++i)
213                 if((*i)->get_tag()==tag)
214                         return *i;
215
216         return 0;
217 }
218
219 void Train::ai_message(const TrainAI::Message &msg)
220 {
221         for(list<TrainAI *>::iterator i=ais.begin(); i!=ais.end(); ++i)
222                 (*i)->message(msg);
223 }
224
225 void Train::place(Block &block, unsigned entry)
226 {
227         if(controller->get_speed())
228                 throw logic_error("moving");
229
230         release_blocks();
231
232         set_active(false);
233         accurate_position = false;
234
235         blocks.push_back(BlockIter(&block, entry));
236         if(!block.reserve(this))
237         {
238                 blocks.pop_back();
239                 return;
240         }
241
242         if(reverse)
243         {
244                 TrackIter track = BlockIter(&block, entry).reverse().track_iter();
245                 vehicles.front()->place(*track, track.entry(), 0, Vehicle::FRONT_BUFFER);
246         }
247         else
248         {
249                 const Block::Endpoint &bep = block.get_endpoint(entry);
250                 vehicles.back()->place(*bep.track, bep.track_ep, 0, Vehicle::BACK_BUFFER);
251         }
252 }
253
254 void Train::unplace()
255 {
256         if(controller->get_speed())
257                 throw logic_error("moving");
258
259         release_blocks();
260
261         set_active(false);
262         accurate_position = false;
263
264         for(vector<Vehicle *>::iterator i=vehicles.begin(); i!=vehicles.end(); ++i)
265                 (*i)->unplace();
266 }
267
268 void Train::stop_at(Block *block)
269 {
270         stop_at_block = block;
271 }
272
273 bool Train::free_block(Block &block)
274 {
275         if(get_reserved_distance_until(&block, false)<controller->get_braking_distance()*1.3)
276                 return false;
277
278         unsigned nsens = 0;
279         for(BlockList::iterator i=cur_blocks_end; i!=blocks.end(); ++i)
280         {
281                 if(i->block()==&block)
282                 {
283                         if(nsens<1)
284                                 return false;
285                         release_blocks(i, blocks.end());
286                         return true;
287                 }
288                 else if((*i)->get_sensor_id())
289                         ++nsens;
290         }
291
292         return false;
293 }
294
295 void Train::free_noncritical_blocks()
296 {
297         if(blocks.empty())
298                 return;
299
300         if(controller->get_speed()==0)
301         {
302                 release_blocks(cur_blocks_end, blocks.end());
303                 return;
304         }
305
306         float margin = 10*layout.get_catalogue().get_scale();
307         float min_dist = controller->get_braking_distance()*1.3+margin;
308
309         Vehicle &veh = *(reverse ? vehicles.back() : vehicles.front());
310
311         TrackIter track(veh.get_track(), veh.get_entry());
312         BlockList::iterator block = blocks.begin();
313         bool in_rsv = false;
314         while(block!=blocks.end() && !(*block)->has_track(*track))
315         {
316                 ++block;
317                 if(block==cur_blocks_end)
318                         in_rsv = true;
319         }
320
321         float dist = veh.get_offset();
322         if(reverse)
323                 track.reverse();
324         else
325                 dist = track->get_type().get_path_length(track->get_active_path())-dist;
326         dist -= veh.get_type().get_length()/2;
327
328         bool nsens = 0;
329         while(1)
330         {
331                 track = track.next();
332
333                 if(!(*block)->has_track(*track))
334                 {
335                         ++block;
336                         if(block==cur_blocks_end)
337                                 in_rsv = true;
338                         if(block==blocks.end())
339                                 return;
340
341                         if(dist>min_dist && nsens>0)
342                         {
343                                 release_blocks(block, blocks.end());
344                                 return;
345                         }
346
347                         if(in_rsv && (*block)->get_sensor_id())
348                                 ++nsens;
349                 }
350
351                 dist += track->get_type().get_path_length(track->get_active_path());
352         }
353 }
354
355 const BlockIter &Train::get_head_block() const
356 {
357         if(blocks.empty())
358                 throw logic_error("no blocks");
359         return blocks.back();
360 }
361
362 const BlockIter &Train::get_tail_block() const
363 {
364         if(blocks.empty())
365                 throw logic_error("no blocks");
366         return blocks.front();
367 }
368
369 int Train::get_entry_to_block(const Block &block) const
370 {
371         for(BlockList::const_iterator i=blocks.begin(); i!=blocks.end(); ++i)
372                 if(i->block()==&block)
373                         return i->entry();
374         return -1;
375 }
376
377 float Train::get_reserved_distance() const
378 {
379         if(blocks.empty())
380                 return 0;
381
382         float margin = 0;
383         TrackIter next = blocks.back().next().track_iter();
384         if(next && next->get_type().is_turnout())
385                 margin = 15*layout.get_catalogue().get_scale();
386
387         return max(get_reserved_distance_until(0, false)-margin, 0.0f);
388 }
389
390 void Train::tick(const Time::TimeStamp &t, const Time::TimeDelta &dt)
391 {
392         if(!active && stop_timeout && t>=stop_timeout)
393         {
394                 release_blocks(cur_blocks_end, blocks.end());
395                 stop_timeout = Time::TimeStamp();
396         }
397
398         Driver &driver = layout.get_driver();
399
400         for(list<TrainAI *>::iterator i=ais.begin(); i!=ais.end(); ++i)
401                 (*i)->tick(t, dt);
402         controller->tick(dt);
403         float speed = controller->get_speed();
404         bool moving = speed>0;
405
406         if(controller->get_reverse()!=reverse)
407         {
408                 reverse = controller->get_reverse();
409                 bool r = reverse;
410                 if(loco_type.get_swap_direction())
411                         r = !r;
412                 driver.set_loco_reverse(address, r);
413
414                 release_blocks(cur_blocks_end, blocks.end());
415                 reverse_blocks(blocks);
416
417                 reserve_more();
418         }
419
420         if(speed_quantizer)
421         {
422                 unsigned speed_step = speed_quantizer->find_speed_step(speed);
423                 if(speed_step!=current_speed_step && !speed_changing && !driver.is_halted() && driver.get_power())
424                 {
425                         speed_changing = true;
426                         driver.set_loco_speed(address, speed_step);
427
428                         pure_speed = false;
429                 }
430
431                 speed = speed_quantizer->get_speed(current_speed_step);
432         }
433
434         if(moving)
435         {
436                 if(!active)
437                         set_active(true);
438
439                 Vehicle &vehicle = *(reverse ? vehicles.back() : vehicles.front());
440                 Track *track = vehicle.get_track();
441
442                 bool ok = false;
443                 for(BlockList::const_iterator i=blocks.begin(); (!ok && i!=cur_blocks_end); ++i)
444                         ok = (*i)->has_track(*track);
445
446                 float d = speed*(dt/Time::sec);
447                 if(ok)
448                 {
449                         SetFlag setf(advancing);
450                         vehicle.advance(reverse ? -d : d);
451                 }
452                 else if(accurate_position)
453                 {
454                         overshoot_dist += d;
455                         if(overshoot_dist>40*layout.get_catalogue().get_scale())
456                         {
457                                 layout.emergency(name+" has not arrived at sensor");
458                                 accurate_position = false;
459                         }
460                 }
461         }
462
463         if(!blocks.empty() && !blocks.front()->get_sensor_id())
464         {
465                 float dist = get_reserved_distance_until(&*blocks.front(), true);
466
467                 if(dist>10*layout.get_catalogue().get_scale())
468                 {
469                         Block &block = *blocks.front();
470                         blocks.pop_front();
471                         block.reserve(0);
472                 }
473         }
474 }
475
476 void Train::save(list<DataFile::Statement> &st) const
477 {
478         st.push_back((DataFile::Statement("name"), name));
479
480         for(vector<Vehicle *>::const_iterator i=vehicles.begin(); i!=vehicles.end(); ++i)
481                 if(i!=vehicles.begin())
482                         st.push_back((DataFile::Statement("vehicle"), (*i)->get_type().get_article_number()));
483
484         if(speed_quantizer)
485         {
486                 DataFile::Statement ss("quantized_speed");
487                 speed_quantizer->save(ss.sub);
488                 st.push_back(ss);
489         }
490
491         if(!blocks.empty() && cur_blocks_end!=blocks.begin())
492         {
493                 BlockList blks(blocks.begin(), BlockList::const_iterator(cur_blocks_end));
494                 if(reverse)
495                         reverse_blocks(blks);
496
497                 BlockIter prev = blks.front().flip();
498                 st.push_back((DataFile::Statement("block_hint"), prev->get_id()));
499
500                 for(BlockList::const_iterator i=blks.begin(); i!=blks.end(); ++i)
501                         st.push_back((DataFile::Statement("block"), (*i)->get_id()));
502         }
503
504         // XXX Need more generic way of saving AI state
505         for(list<TrainAI *>::const_iterator i=ais.begin(); i!=ais.end(); ++i)
506         {
507                 if(TrainRouter *router = dynamic_cast<TrainRouter *>(*i))
508                 {
509                         DataFile::Statement ss("router");
510                         router->save(ss.sub);
511                         st.push_back(ss);
512                 }
513                 else if(Timetable *timetable = dynamic_cast<Timetable *>(*i))
514                 {
515                         DataFile::Statement ss("timetable");
516                         timetable->save(ss.sub);
517                         st.push_back(ss);
518                 }
519         }
520 }
521
522 void Train::control_changed(const Controller::Control &ctrl)
523 {
524         signal_control_changed.emit(ctrl.name, ctrl.value);
525 }
526
527 void Train::loco_speed_event(unsigned addr, unsigned speed, bool rev)
528 {
529         if(addr==address)
530         {
531                 current_speed_step = speed;
532                 bool r = reverse;
533                 if(loco_type.get_swap_direction())
534                         r = !r;
535                 if(rev!=r)
536                         layout.get_driver().set_loco_reverse(address, r);
537                 speed_changing = false;
538                 pure_speed = false;
539         }
540 }
541
542 void Train::loco_func_event(unsigned addr, unsigned func, bool state)
543 {
544         if(addr==address)
545         {
546                 if(state)
547                         functions |= 1<<func;
548                 else
549                         functions &= ~(1<<func);
550
551                 signal_function_changed.emit(func, state);
552         }
553 }
554
555 void Train::block_state_changed(Block &block, Block::State state)
556 {
557         if(state==Block::MAYBE_ACTIVE)
558         {
559                 // Find the first sensor block from our reserved blocks that isn't this sensor
560                 BlockList::iterator end;
561                 unsigned result = 0;
562                 for(end=cur_blocks_end; end!=blocks.end(); ++end)
563                         if((*end)->get_sensor_id())
564                         {
565                                 if(&**end!=&block)
566                                 {
567                                         if(result==0)
568                                                 result = 2;
569                                         else if(result==1)
570                                                 break;
571                                 }
572                                 else if(result==0)
573                                         result = 1;
574                                 else if(result==2)
575                                         result = 3;
576                         }
577
578                 if(result==1)
579                 {
580                         // Compute speed and update related state
581                         float travel_time_secs = (Time::now()-last_entry_time)/Time::sec;
582
583                         if(pure_speed && speed_quantizer && current_speed_step>0 && travel_time_secs>=2)
584                                 speed_quantizer->learn(current_speed_step, travel_dist/travel_time_secs, travel_time_secs);
585
586                         travel_dist = 0;
587                         for(BlockList::iterator j=cur_blocks_end; j!=end; ++j)
588                         {
589                                 travel_dist += (*j)->get_path_length(j->entry());
590
591                                 if(&**j==&block && !advancing)
592                                 {
593                                         TrackIter track = j->track_iter();
594                                         if(reverse)
595                                         {
596                                                 track = track.flip();
597                                                 vehicles.back()->place(*track, track.entry(), 0, Vehicle::BACK_AXLE);
598                                         }
599                                         else
600                                                 vehicles.front()->place(*track, track.entry(), 0, Vehicle::FRONT_AXLE);
601                                 }
602                         }
603                         last_entry_time = Time::now();
604                         pure_speed = true;
605                         accurate_position = true;
606                         overshoot_dist = 0;
607
608                         // Move blocks up to the next sensor to our current blocks
609                         for(BlockList::iterator j=cur_blocks_end; j!=end; ++j)
610                                 signal_advanced.emit(**j);
611                         cur_blocks_end = end;
612
613                         // Try to get more blocks if we're moving
614                         if(active)
615                                 reserve_more();
616                 }
617                 else if(result==3)
618                         layout.emergency("Sensor for "+name+" triggered out of order");
619         }
620         else if(state==Block::INACTIVE)
621         {
622                 const Vehicle &veh = *(reverse ? vehicles.front() : vehicles.back());
623
624                 // Find the first sensor in our current blocks that's still active
625                 BlockList::iterator end = blocks.begin();
626                 for(BlockList::iterator i=blocks.begin(); i!=cur_blocks_end; ++i)
627                 {
628                         if((*i)->has_track(*veh.get_track()))
629                                 break;
630                         if((*i)->get_sensor_id())
631                         {
632                                 if(layout.get_driver().get_sensor((*i)->get_sensor_id()))
633                                         break;
634                                 else
635                                 {
636                                         end = i;
637                                         ++end;
638                                 }
639                         }
640                 }
641                 
642                 if(end!=blocks.begin() && end!=cur_blocks_end)
643                         // Free blocks up to the last inactive sensor
644                         release_blocks(blocks.begin(), end);
645         }
646 }
647
648 void Train::turnout_path_changed(Track &track)
649 {
650         for(list<BlockIter>::iterator i=blocks.begin(); i!=blocks.end(); ++i)
651                 if((*i)->get_turnout_id()==track.get_turnout_id() && !reserving)
652                 {
653                         if(&**i==pending_block)
654                                 reserve_more();
655                         else
656                                 check_turnout_paths(false);
657                 }
658 }
659
660 void Train::halt_event(bool h)
661 {
662         if(h)
663                 accurate_position = false;
664 }
665
666 void Train::block_reserved(const Block &block, const Train *train)
667 {
668         if(&block==pending_block && !train && !reserving)
669                 reserve_more();
670 }
671
672 void Train::reserve_more()
673 {
674         if(!active || blocks.empty())
675                 return;
676
677         BlockIter start = blocks.back();
678         if(&*start==stop_at_block)
679                 return;
680
681         pending_block = 0;
682         preceding_train = 0;
683
684         // See how many sensor blocks and how much track we already have
685         unsigned nsens = 0;
686         float dist = 0;
687         for(BlockList::const_iterator i=cur_blocks_end; i!=blocks.end(); ++i)
688         {
689                 if((*i)->get_sensor_id())
690                         ++nsens;
691                 if(nsens>0)
692                         dist += (*i)->get_path_length(i->entry());
693         }
694
695         float approach_margin = 50*layout.get_catalogue().get_scale();
696         float min_dist = controller->get_braking_distance()*1.3+approach_margin*2;
697
698         BlockIter block = start;
699
700         SetFlag setf(reserving);
701
702         while(1)
703         {
704                 BlockIter last = block;
705                 block = block.next();
706                 if(!block || block->get_endpoints().size()<2)
707                         // The track ends here
708                         break;
709
710                 if(block->get_turnout_id() && !last->get_turnout_id())
711                 {
712                         /* We are arriving at a turnout.  See if we have enough blocks and
713                         distance reserved. */
714                         if(nsens>=3 && dist>=min_dist)
715                                 break;
716                 }
717
718                 blocks.push_back(block);
719                 if(!block->reserve(this))
720                 {
721                         blocks.pop_back();
722                         pending_block = &*block;
723                         break;
724                 }
725
726                 if(cur_blocks_end==blocks.end())
727                         --cur_blocks_end;
728                 if(clear_blocks_end==blocks.end())
729                         --clear_blocks_end;
730
731                 TrackIter track = block.track_iter();
732                 if(track->is_path_changing())
733                 {
734                         pending_block = &*block;
735                         break;
736                 }
737
738                 if(&*block==stop_at_block)
739                         break;
740
741                 if(block->get_sensor_id())
742                         ++nsens;
743                 if(nsens>0)
744                         dist += block->get_path_length(block.entry());
745         }
746
747         check_turnout_paths(true);
748
749         // Make any sensorless blocks at the beginning immediately current
750         while(cur_blocks_end!=clear_blocks_end && !(*cur_blocks_end)->get_sensor_id())
751                 ++cur_blocks_end;
752 }
753
754 void Train::check_turnout_paths(bool set)
755 {
756         if(clear_blocks_end==blocks.end())
757                 return;
758
759         for(list<BlockIter>::iterator i=clear_blocks_end; i!=blocks.end(); ++i)
760         {
761                 if((*i)->get_turnout_id())
762                 {
763                         TrackIter track = i->track_iter();
764                         const TrackType::Endpoint &track_ep = track.endpoint();
765
766                         unsigned path = 0;
767                         list<BlockIter>::iterator j = i;
768                         if(++j!=blocks.end())
769                         {
770                                 TrackIter rev = j->track_iter().flip();
771                                 unsigned mask = rev.endpoint().paths&track_ep.paths;
772                                 for(path=0; mask>1; mask>>=1, ++path) ;
773                         }
774                         else
775                                 return;
776
777                         if(path!=track->get_active_path())
778                         {
779                                 if(set && !track->is_path_changing())
780                                 {
781                                         track->set_active_path(path);
782                                         if(track->is_path_changing())
783                                                 continue;
784                                 }
785                                 else
786                                         continue;
787                         }
788                 }
789
790                 if(i==clear_blocks_end)
791                         ++clear_blocks_end;
792                 if(i==cur_blocks_end && !(*i)->get_sensor_id())
793                         ++cur_blocks_end;
794         }
795 }
796
797 float Train::get_reserved_distance_until(const Block *until_block, bool back) const
798 {
799         if(blocks.empty())
800                 return 0;
801
802         Vehicle &veh = *(reverse!=back ? vehicles.back() : vehicles.front());
803         const VehicleType &vtype = veh.get_type();
804
805         TrackIter track(veh.get_track(), veh.get_entry());
806         if(!track)  // XXX Probably unnecessary
807                 return 0;
808
809         BlockList::const_iterator block = blocks.begin();
810         while(block!=clear_blocks_end && !(*block)->has_track(*track))
811                 ++block;
812         if(block==clear_blocks_end || &**block==until_block)
813                 return 0;
814
815         float result = veh.get_offset();
816         if(reverse!=back)
817                 track = track.reverse();
818         else
819                 result = track->get_type().get_path_length(track->get_active_path())-result;
820         result -= vtype.get_length()/2;
821
822         while(1)
823         {
824                 track = track.next();
825                 if(!track)
826                         break;
827
828                 if(!(*block)->has_track(*track))
829                 {
830                         if(back)
831                         {
832                                 if(block==blocks.begin())
833                                         break;
834                                 --block;
835                         }
836                         else
837                         {
838                                 ++block;
839                                 if(block==clear_blocks_end)
840                                         break;
841                         }
842
843                         if(&**block==until_block)
844                                 break;
845                 }
846
847                 result += track->get_type().get_path_length(track->get_active_path());
848         }
849
850         return result;
851 }
852
853 void Train::release_blocks()
854 {
855         release_blocks(blocks.begin(), blocks.end());
856 }
857
858 void Train::release_blocks(BlockList::iterator begin, BlockList::iterator end)
859 {
860         while(begin!=end)
861         {
862                 if(begin==cur_blocks_end)
863                         cur_blocks_end = end;
864                 if(begin==clear_blocks_end)
865                         clear_blocks_end = end;
866
867                 Block &block = **begin;
868                 blocks.erase(begin++);
869                 block.reserve(0);
870         }
871 }
872
873 void Train::reverse_blocks(BlockList &blks) const
874 {
875         blks.reverse();
876         for(BlockList::iterator i=blks.begin(); i!=blks.end(); ++i)
877                 *i = i->reverse();
878 }
879
880
881 Train::Loader::Loader(Train &t):
882         DataFile::ObjectLoader<Train>(t),
883         prev_block(0),
884         blocks_valid(true)
885 {
886         add("block",       &Loader::block);
887         add("block_hint",  &Loader::block_hint);
888         add("name",        &Loader::name);
889         add("quantized_speed",  &Loader::quantized_speed);
890         add("router",      &Loader::router);
891         add("timetable",   &Loader::timetable);
892         add("vehicle",     &Loader::vehicle);
893 }
894
895 void Train::Loader::finish()
896 {
897         if(!obj.blocks.empty())
898         {
899                 TrackIter track = obj.blocks.front().track_iter();
900                 float offset = 2*obj.layout.get_catalogue().get_scale();
901                 obj.vehicles.back()->place(*track, track.entry(), offset, Vehicle::BACK_BUFFER);
902         }
903 }
904
905 void Train::Loader::block(unsigned id)
906 {
907         if(!blocks_valid)
908                 return;
909
910         Block *blk;
911         try
912         {
913                 blk = &obj.layout.get_block(id);
914         }
915         catch(const key_error &)
916         {
917                 blocks_valid = false;
918                 return;
919         }
920
921         int entry = -1;
922         if(prev_block)
923                 entry = blk->get_endpoint_by_link(*prev_block);
924         if(entry<0)
925                 entry = 0;
926
927         obj.blocks.push_back(BlockIter(blk, entry));
928         blk->reserve(&obj);
929
930         if(blk->get_sensor_id())
931                 obj.layout.get_driver().set_sensor(blk->get_sensor_id(), true);
932
933         prev_block = blk;
934 }
935
936 void Train::Loader::block_hint(unsigned id)
937 {
938         try
939         {
940                 prev_block = &obj.layout.get_block(id);
941         }
942         catch(const key_error &)
943         {
944                 blocks_valid = false;
945         }
946 }
947
948 void Train::Loader::name(const string &n)
949 {
950         obj.set_name(n);
951 }
952
953 void Train::Loader::quantized_speed()
954 {
955         if(obj.speed_quantizer)
956                 load_sub(*obj.speed_quantizer);
957 }
958
959 void Train::Loader::router()
960 {
961         TrainRouter *rtr = new TrainRouter(obj);
962         load_sub(*rtr);
963 }
964
965 void Train::Loader::timetable()
966 {
967         Timetable *ttbl = new Timetable(obj);
968         load_sub(*ttbl);
969 }
970
971 void Train::Loader::vehicle(ArticleNumber art_nr)
972 {
973         const VehicleType &vtype = obj.layout.get_catalogue().get_vehicle(art_nr);
974         Vehicle *veh = new Vehicle(obj.layout, vtype);
975         obj.vehicles.back()->attach_back(*veh);
976         obj.vehicles.push_back(veh);
977 }
978
979 } // namespace R2C2