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