3 #include <msp/core/maputils.h>
4 #include <msp/strings/format.h>
5 #include <msp/time/units.h>
6 #include <msp/time/utils.h>
12 #include "simplecontroller.h"
13 #include "speedquantizer.h"
14 #include "timetable.h"
15 #include "trackiter.h"
16 #include "tracktype.h"
19 #include "vehicletype.h"
31 SetFlag(bool &f): flag(f) { flag = true; }
32 ~SetFlag() { flag = false; }
40 Train::Train(Layout &l, const VehicleType &t, unsigned a, const string &p):
48 cur_blocks_end(blocks.end()),
49 clear_blocks_end(blocks.end()),
53 controller(new SimpleController),
55 current_speed_step(0),
56 speed_changing(false),
63 accurate_position(false),
66 if(!loco_type.is_locomotive())
67 throw invalid_argument("Train::Train");
69 unsigned speed_steps = layout.get_driver().get_protocol_speed_steps(protocol);
71 speed_quantizer = new SpeedQuantizer(speed_steps);
73 vehicles.push_back(new Vehicle(layout, loco_type));
75 layout.add_train(*this);
77 layout.get_driver().add_loco(address, protocol, loco_type);
78 layout.get_driver().signal_loco_speed.connect(sigc::mem_fun(this, &Train::loco_speed_event));
79 layout.get_driver().signal_loco_function.connect(sigc::mem_fun(this, &Train::loco_func_event));
81 layout.signal_block_reserved.connect(sigc::mem_fun(this, &Train::block_reserved));
82 layout.signal_block_state_changed.connect(sigc::mem_fun(this, &Train::block_state_changed));
84 layout.get_driver().signal_halt.connect(sigc::mem_fun(this, &Train::halt_event));
86 const set<Track *> &tracks = layout.get_tracks();
87 for(set<Track *>::const_iterator i=tracks.begin(); i!=tracks.end(); ++i)
88 if((*i)->get_turnout_id())
89 (*i)->signal_path_changed.connect(sigc::hide(sigc::bind(sigc::mem_fun(this, &Train::turnout_path_changed), sigc::ref(**i))));
91 controller->signal_control_changed.connect(sigc::mem_fun(this, &Train::control_changed));
97 for(vector<Vehicle *>::iterator i=vehicles.begin(); i!=vehicles.end(); ++i)
99 layout.remove_train(*this);
102 void Train::set_name(const string &n)
106 signal_name_changed.emit(name);
109 void Train::set_priority(int p)
114 void Train::yield_to(const Train &t)
119 void Train::add_vehicle(const VehicleType &vt)
121 Vehicle *veh = new Vehicle(layout, vt);
122 vehicles.back()->attach_back(*veh);
123 vehicles.push_back(veh);
126 void Train::remove_vehicle(unsigned i)
128 if(i>=vehicles.size())
129 throw out_of_range("Train::remove_vehicle");
131 throw logic_error("can't remove locomotive");
133 vehicles.erase(vehicles.begin()+i);
134 if(i<vehicles.size())
135 vehicles[i-1]->attach_back(*vehicles[i]);
138 unsigned Train::get_n_vehicles() const
140 return vehicles.size();
143 Vehicle &Train::get_vehicle(unsigned i)
145 if(i>=vehicles.size())
146 throw out_of_range("Train::get_vehicle");
150 const Vehicle &Train::get_vehicle(unsigned i) const
152 if(i>=vehicles.size())
153 throw out_of_range("Train::get_vehicle");
157 void Train::set_control(const string &n, float v)
159 controller->set_control(n, v);
162 void Train::set_active(bool a)
166 if(!a && controller->get_speed())
167 throw logic_error("moving");
172 stop_timeout = Time::TimeStamp();
176 stop_timeout = Time::now()+2*Time::sec;
179 void Train::set_function(unsigned func, bool state)
181 if(!loco_type.get_functions().count(func))
182 throw invalid_argument("Train::set_function");
183 layout.get_driver().set_loco_function(address, func, state);
186 float Train::get_control(const string &ctrl) const
188 return controller->get_control(ctrl).value;
191 float Train::get_speed() const
193 return controller->get_speed();
196 float Train::get_quantized_speed() const
199 return speed_quantizer->quantize_speed(controller->get_speed());
201 return controller->get_speed();
204 bool Train::get_function(unsigned func) const
206 return (functions>>func)&1;
209 void Train::add_ai(TrainAI &ai)
212 ai.signal_event.connect(sigc::bind<0>(signal_ai_event, sigc::ref(ai)));
215 void Train::remove_ai(TrainAI &ai)
217 list<TrainAI *>::iterator i = find(ais.begin(), ais.end(), &ai);
222 TrainAI *Train::get_tagged_ai(const string &tag) const
224 for(list<TrainAI *>::const_iterator i=ais.begin(); i!=ais.end(); ++i)
225 if((*i)->get_tag()==tag)
231 void Train::ai_message(const TrainAI::Message &msg)
233 for(list<TrainAI *>::iterator i=ais.begin(); i!=ais.end(); ++i)
237 bool Train::set_route(const Route *r)
239 free_noncritical_blocks();
242 if(r && !blocks.empty())
244 TrackIter first = blocks.front().track_iter();
245 TrackIter next = blocks.back().next().track_iter();
246 if(!r->has_track(*next))
248 lead = Route::find(next, *r);
251 create_lead_route(lead, lead);
252 routes.push_front(lead);
254 else if(!r->has_track(*first))
255 lead = create_lead_route(0, r);
260 routes.push_back(lead);
263 end_of_route = false;
267 signal_route_changed.emit(get_route());
272 bool Train::go_to(Track &to)
274 for(BlockList::const_iterator i=blocks.begin(); i!=cur_blocks_end; ++i)
275 if((*i)->has_track(to))
277 signal_arrived.emit();
281 free_noncritical_blocks();
283 TrackIter next = blocks.back().next().track_iter();
285 Route *route = Route::find(next, to);
288 create_lead_route(route, route);
289 return set_route(route);
292 bool Train::go_to(const Zone &to)
295 for(BlockList::const_iterator i=blocks.begin(); i!=blocks.end(); ++i)
296 tracks.insert((*i)->get_tracks().begin(), (*i)->get_tracks().end());
298 const Zone::TrackSet &ztracks = to.get_tracks();
299 unsigned union_size = 0;
300 for(Zone::TrackSet::const_iterator i=ztracks.begin(); i!=ztracks.end(); ++i)
301 union_size += tracks.count(*i);
303 if(union_size==tracks.size() || union_size==ztracks.size())
305 signal_arrived.emit();
309 free_noncritical_blocks();
311 TrackIter next = blocks.back().next().track_iter();
313 Route *route = Route::find(next, to);
316 create_lead_route(route, route);
317 route->add_tracks(ztracks);
318 return set_route(route);
321 const Route *Train::get_route() const
325 return routes.front();
328 void Train::place(Block &block, unsigned entry)
330 if(controller->get_speed())
331 throw logic_error("moving");
336 accurate_position = false;
338 blocks.push_back(BlockIter(&block, entry));
339 if(!block.reserve(this))
347 TrackIter track = BlockIter(&block, entry).reverse().track_iter();
348 vehicles.front()->place(*track, track.entry(), 0, Vehicle::FRONT_BUFFER);
352 const Block::Endpoint &bep = block.get_endpoint(entry);
353 vehicles.back()->place(*bep.track, bep.track_ep, 0, Vehicle::BACK_BUFFER);
357 void Train::unplace()
359 if(controller->get_speed())
360 throw logic_error("moving");
365 accurate_position = false;
367 for(vector<Vehicle *>::iterator i=vehicles.begin(); i!=vehicles.end(); ++i)
371 bool Train::free_block(Block &block)
373 if(get_reserved_distance_until(&block, false)<controller->get_braking_distance()*1.3)
377 for(BlockList::iterator i=cur_blocks_end; i!=blocks.end(); ++i)
379 if(i->block()==&block)
383 release_blocks(i, blocks.end());
386 else if((*i)->get_sensor_id())
393 void Train::free_noncritical_blocks()
398 if(controller->get_speed()==0)
400 release_blocks(cur_blocks_end, blocks.end());
404 float margin = 10*layout.get_catalogue().get_scale();
405 float min_dist = controller->get_braking_distance()*1.3+margin;
407 Vehicle &veh = *(reverse ? vehicles.back() : vehicles.front());
409 TrackIter track(veh.get_track(), veh.get_entry());
410 BlockList::iterator block = blocks.begin();
412 while(block!=blocks.end() && !(*block)->has_track(*track))
415 if(block==cur_blocks_end)
419 float dist = veh.get_offset();
423 dist = track->get_type().get_path_length(track->get_active_path())-dist;
424 dist -= veh.get_type().get_length()/2;
429 track = track.next();
431 if(!(*block)->has_track(*track))
434 if(block==cur_blocks_end)
436 if(block==blocks.end())
439 if(dist>min_dist && nsens>0)
441 release_blocks(block, blocks.end());
445 if(in_rsv && (*block)->get_sensor_id())
449 dist += track->get_type().get_path_length(track->get_active_path());
453 int Train::get_entry_to_block(const Block &block) const
455 for(BlockList::const_iterator i=blocks.begin(); i!=blocks.end(); ++i)
456 if(i->block()==&block)
461 float Train::get_reserved_distance() const
467 TrackIter next = blocks.back().next().track_iter();
468 if(next && next->get_type().is_turnout())
469 margin = 15*layout.get_catalogue().get_scale();
471 return max(get_reserved_distance_until(0, false)-margin, 0.0f);
474 void Train::tick(const Time::TimeStamp &t, const Time::TimeDelta &dt)
476 if(!active && stop_timeout && t>=stop_timeout)
478 release_blocks(cur_blocks_end, blocks.end());
479 stop_timeout = Time::TimeStamp();
482 Driver &driver = layout.get_driver();
484 for(list<TrainAI *>::iterator i=ais.begin(); i!=ais.end(); ++i)
486 controller->tick(dt);
487 float speed = controller->get_speed();
488 bool moving = speed>0;
490 if(controller->get_reverse()!=reverse)
492 reverse = controller->get_reverse();
494 if(loco_type.get_swap_direction())
496 driver.set_loco_reverse(address, r);
498 release_blocks(cur_blocks_end, blocks.end());
499 reverse_blocks(blocks);
506 unsigned speed_step = speed_quantizer->find_speed_step(speed);
507 if(speed_step!=current_speed_step && !speed_changing && !driver.is_halted() && driver.get_power())
509 speed_changing = true;
510 driver.set_loco_speed(address, speed_step);
515 speed = speed_quantizer->get_speed(current_speed_step);
523 Vehicle &vehicle = *(reverse ? vehicles.back() : vehicles.front());
524 Track *track = vehicle.get_track();
527 for(BlockList::const_iterator i=blocks.begin(); (!ok && i!=cur_blocks_end); ++i)
528 ok = (*i)->has_track(*track);
530 float d = speed*(dt/Time::sec);
533 SetFlag setf(advancing);
534 vehicle.advance(reverse ? -d : d);
536 else if(accurate_position)
539 if(overshoot_dist>40*layout.get_catalogue().get_scale())
541 layout.emergency(name+" has not arrived at sensor");
542 accurate_position = false;
546 else if(end_of_route && cur_blocks_end==blocks.end())
549 signal_arrived.emit();
553 if(!blocks.empty() && !blocks.front()->get_sensor_id())
555 float dist = get_reserved_distance_until(&*blocks.front(), true);
557 if(dist>10*layout.get_catalogue().get_scale())
559 Block &block = *blocks.front();
566 void Train::save(list<DataFile::Statement> &st) const
568 st.push_back((DataFile::Statement("name"), name));
570 st.push_back((DataFile::Statement("priority"), priority));
572 for(vector<Vehicle *>::const_iterator i=vehicles.begin(); i!=vehicles.end(); ++i)
573 if(i!=vehicles.begin())
574 st.push_back((DataFile::Statement("vehicle"), (*i)->get_type().get_article_number()));
578 DataFile::Statement ss("quantized_speed");
579 speed_quantizer->save(ss.sub);
583 if(!blocks.empty() && cur_blocks_end!=blocks.begin())
585 BlockList blks(blocks.begin(), BlockList::const_iterator(cur_blocks_end));
587 reverse_blocks(blks);
589 BlockIter prev = blks.front().flip();
590 st.push_back((DataFile::Statement("block_hint"), prev->get_id()));
592 for(BlockList::const_iterator i=blks.begin(); i!=blks.end(); ++i)
593 st.push_back((DataFile::Statement("block"), (*i)->get_id()));
598 list<const Route *>::const_iterator i = routes.begin();
599 for(; (i!=routes.end() && (*i)->is_temporary()); ++i) ;
601 st.push_back((DataFile::Statement("route"), (*i)->get_name()));
604 // XXX Need more generic way of saving AI state
605 for(list<TrainAI *>::const_iterator i=ais.begin(); i!=ais.end(); ++i)
606 if(Timetable *timetable = dynamic_cast<Timetable *>(*i))
608 DataFile::Statement ss("timetable");
609 timetable->save(ss.sub);
614 void Train::control_changed(const Controller::Control &ctrl)
616 signal_control_changed.emit(ctrl.name, ctrl.value);
619 void Train::loco_speed_event(unsigned addr, unsigned speed, bool rev)
623 current_speed_step = speed;
625 if(loco_type.get_swap_direction())
628 layout.get_driver().set_loco_reverse(address, r);
629 speed_changing = false;
634 void Train::loco_func_event(unsigned addr, unsigned func, bool state)
639 functions |= 1<<func;
641 functions &= ~(1<<func);
643 signal_function_changed.emit(func, state);
647 void Train::block_state_changed(Block &block, Block::State state)
649 if(state==Block::MAYBE_ACTIVE)
651 // Find the first sensor block from our reserved blocks that isn't this sensor
652 BlockList::iterator end;
654 for(end=cur_blocks_end; end!=blocks.end(); ++end)
655 if((*end)->get_sensor_id())
672 // Compute speed and update related state
673 float travel_time_secs = (Time::now()-last_entry_time)/Time::sec;
675 if(pure_speed && speed_quantizer && current_speed_step>0 && travel_time_secs>=2)
676 speed_quantizer->learn(current_speed_step, travel_dist/travel_time_secs, travel_time_secs);
679 for(BlockList::iterator j=cur_blocks_end; j!=end; ++j)
681 travel_dist += (*j)->get_path_length(j->entry());
683 if(&**j==&block && !advancing)
685 TrackIter track = j->track_iter();
688 track = track.flip();
689 vehicles.back()->place(*track, track.entry(), 0, Vehicle::BACK_AXLE);
692 vehicles.front()->place(*track, track.entry(), 0, Vehicle::FRONT_AXLE);
695 last_entry_time = Time::now();
697 accurate_position = true;
700 // Check if we've reached the next route
703 const Route &route = **(++routes.begin());
704 for(BlockList::iterator j=cur_blocks_end; j!=end; ++j)
705 if(route.has_track(*j->track_iter()))
709 signal_route_changed.emit(routes.front());
714 // Move blocks up to the next sensor to our current blocks
715 for(BlockList::iterator j=cur_blocks_end; j!=end; ++j)
716 signal_advanced.emit(**j);
717 cur_blocks_end = end;
719 // Try to get more blocks if we're moving
724 layout.emergency("Sensor for "+name+" triggered out of order");
726 else if(state==Block::INACTIVE)
728 const Vehicle &veh = *(reverse ? vehicles.front() : vehicles.back());
730 // Find the first sensor in our current blocks that's still active
731 BlockList::iterator end = blocks.begin();
732 for(BlockList::iterator i=blocks.begin(); i!=cur_blocks_end; ++i)
734 if((*i)->has_track(*veh.get_track()))
736 if((*i)->get_sensor_id())
738 if(layout.get_driver().get_sensor((*i)->get_sensor_id()))
748 if(end!=blocks.begin() && end!=cur_blocks_end)
749 // Free blocks up to the last inactive sensor
750 release_blocks(blocks.begin(), end);
754 void Train::turnout_path_changed(Track &track)
756 for(list<BlockIter>::iterator i=blocks.begin(); i!=blocks.end(); ++i)
757 if((*i)->get_turnout_id()==track.get_turnout_id() && !reserving)
758 check_turnout_paths(false);
761 void Train::halt_event(bool h)
764 accurate_position = false;
767 void Train::block_reserved(const Block &block, const Train *train)
769 if(&block==pending_block && !train && !reserving)
773 void Train::reserve_more()
775 if(!active || blocks.empty() || end_of_route)
778 BlockIter start = blocks.back();
783 // See how many sensor blocks and how much track we already have
786 for(BlockList::const_iterator i=cur_blocks_end; i!=blocks.end(); ++i)
788 if((*i)->get_sensor_id())
791 dist += (*i)->get_path_length(i->entry());
794 list<const Route *>::iterator cur_route = routes.begin();
795 advance_route(cur_route, *start.track_iter());
797 float approach_margin = 50*layout.get_catalogue().get_scale();
798 float min_dist = controller->get_braking_distance()*1.3+approach_margin*2;
800 BlockIter block = start;
801 list<BlockIter>::iterator good_end = blocks.end();
802 Train *blocking_train = 0;
803 BlockList contested_blocks;
805 SetFlag setf(reserving);
809 BlockIter last = block;
810 block = block.next(cur_route!=routes.end() ? *cur_route : 0);
811 if(!block || block->get_endpoints().size()<2)
815 good_end = blocks.end();
821 TrackIter track = block.track_iter();
823 if(cur_route!=routes.end())
825 if(!advance_route(cur_route, *track))
827 // Keep the blocks if we arrived at the end of the route
830 good_end = blocks.end();
837 if(block->get_turnout_id() && !last->get_turnout_id())
839 /* We can keep the blocks if we arrive at a turnout from a non-turnout
840 block. Having a turnout block as our last reserved block is not good
841 as it would limit our diversion possibilities for little benefit. */
842 good_end = blocks.end();
843 if(nsens>=3 && dist>=min_dist)
849 if(block->get_train()!=blocking_train)
851 if(blocking_train->free_block(*contested_blocks.back()))
853 // Roll back and start actually reserving the blocks
854 block = blocks.back();
855 cur_route = routes.begin();
856 advance_route(cur_route, *block.track_iter().track());
857 if(blocking_train->get_priority()==priority)
858 blocking_train->yield_to(*this);
864 yield_to(*blocking_train);
865 pending_block = contested_blocks.front().block();
871 contested_blocks.push_back(block);
876 blocks.push_back(block);
877 bool reserved = block->reserve(this);
881 /* We've found another train. If it wants to exit the block from the
882 same endpoint we're trying to enter from or the other way around,
883 treat it as coming towards us. Otherwise treat it as going in the
885 Train *other_train = block->get_train();
886 int other_entry = other_train->get_entry_to_block(*block);
888 throw logic_error("block reservation inconsistency");
890 unsigned exit = block.reverse().entry();
891 unsigned other_exit = BlockIter(block.block(), other_entry).reverse().entry();
892 bool entry_conflict = (block.entry()==other_exit);
893 bool exit_conflict = (exit==static_cast<unsigned>(other_entry));
894 if(!entry_conflict && !last->get_turnout_id())
896 /* The other train is not coming to the blocks we're holding, so we
898 good_end = blocks.end();
900 if(static_cast<unsigned>(other_entry)==block.entry())
901 preceding_train = other_train;
904 int other_prio = other_train->get_priority();
906 if(!entry_conflict && !exit_conflict && other_prio<priority)
908 /* Ask a lesser priority train going to the same direction to free
910 if(other_train->free_block(*block))
912 blocks.push_back(block);
913 if(!(reserved = block->reserve(this)))
917 else if(other_train!=yielding_to && (other_prio<priority || (other_prio==priority && entry_conflict)))
919 /* A lesser priority train is coming at us, we must ask it to free
920 enough blocks to get clear of it to avoid a potential deadlock */
921 blocking_train = other_train;
922 contested_blocks.clear();
923 contested_blocks.push_back(block);
929 pending_block = &*block;
934 if(!contested_blocks.empty() && contested_blocks.front()==block)
935 contested_blocks.pop_front();
937 if(cur_blocks_end==blocks.end())
939 if(clear_blocks_end==blocks.end())
941 if(good_end==blocks.end())
944 if(block->get_sensor_id())
947 dist += block->get_path_length(block.entry());
950 // Unreserve blocks that were not good
951 release_blocks(good_end, blocks.end());
953 if(blocks.back()!=start)
954 // We got some new blocks, so no longer need to yield
957 check_turnout_paths(true);
959 // Make any sensorless blocks at the beginning immediately current
960 while(cur_blocks_end!=clear_blocks_end && !(*cur_blocks_end)->get_sensor_id())
964 void Train::check_turnout_paths(bool set)
966 if(clear_blocks_end==blocks.end())
969 for(list<BlockIter>::iterator i=clear_blocks_end; i!=blocks.end(); ++i)
971 if((*i)->get_turnout_id())
973 TrackIter track = i->track_iter();
974 const TrackType::Endpoint &track_ep = track.endpoint();
977 list<BlockIter>::iterator j = i;
978 if(++j!=blocks.end())
980 TrackIter rev = j->track_iter().flip();
981 unsigned mask = rev.endpoint().paths&track_ep.paths;
982 for(path=0; mask>1; mask>>=1, ++path) ;
987 if(path!=track->get_active_path())
990 track->set_active_path(path);
992 /* Check again, in case the driver was able to service the request
994 if(!set || path!=track->get_active_path())
999 if(i==clear_blocks_end)
1001 if(i==cur_blocks_end && !(*i)->get_sensor_id())
1006 float Train::get_reserved_distance_until(const Block *until_block, bool back) const
1011 Vehicle &veh = *(reverse!=back ? vehicles.back() : vehicles.front());
1012 const VehicleType &vtype = veh.get_type();
1014 TrackIter track(veh.get_track(), veh.get_entry());
1015 if(!track) // XXX Probably unnecessary
1018 BlockList::const_iterator block = blocks.begin();
1019 while(block!=clear_blocks_end && !(*block)->has_track(*track))
1021 if(block==clear_blocks_end || &**block==until_block)
1024 float result = veh.get_offset();
1026 track = track.reverse();
1028 result = track->get_type().get_path_length(track->get_active_path())-result;
1029 result -= vtype.get_length()/2;
1033 track = track.next();
1037 if(!(*block)->has_track(*track))
1041 if(block==blocks.begin())
1048 if(block==clear_blocks_end)
1052 if(&**block==until_block)
1056 result += track->get_type().get_path_length(track->get_active_path());
1062 void Train::release_blocks()
1064 release_blocks(blocks.begin(), blocks.end());
1067 void Train::release_blocks(BlockList::iterator begin, BlockList::iterator end)
1071 if(begin==cur_blocks_end)
1072 cur_blocks_end = end;
1073 if(begin==clear_blocks_end)
1074 clear_blocks_end = end;
1076 Block &block = **begin;
1077 blocks.erase(begin++);
1080 if(begin==blocks.end())
1081 end_of_route = false;
1085 void Train::reverse_blocks(BlockList &blks) const
1088 for(BlockList::iterator i=blks.begin(); i!=blks.end(); ++i)
1092 bool Train::advance_route(list<const Route *>::iterator &iter, Track &track)
1094 while(iter!=routes.end() && !(*iter)->has_track(track))
1096 if(iter==routes.end())
1102 Route *Train::create_lead_route(Route *lead, const Route *target)
1106 lead = new Route(layout);
1107 lead->set_name("Lead");
1108 lead->set_temporary(true);
1111 set<Track *> tracks;
1112 for(BlockList::iterator i=blocks.begin(); i!=blocks.end(); ++i)
1114 const set<Track *> &btracks = (*i)->get_tracks();
1115 for(set<Track *>::const_iterator j=btracks.begin(); j!=btracks.end(); ++j)
1116 if(!target || !target->has_track(**j))
1120 lead->add_tracks(tracks);
1126 Train::Loader::Loader(Train &t):
1127 DataFile::ObjectLoader<Train>(t),
1131 add("block", &Loader::block);
1132 add("block_hint", &Loader::block_hint);
1133 add("name", &Loader::name);
1134 add("priority", &Train::priority);
1135 add("quantized_speed", &Loader::quantized_speed);
1136 add("route", &Loader::route);
1137 add("timetable", &Loader::timetable);
1138 add("vehicle", &Loader::vehicle);
1141 void Train::Loader::finish()
1143 if(!obj.blocks.empty())
1145 TrackIter track = obj.blocks.front().track_iter();
1146 float offset = 2*obj.layout.get_catalogue().get_scale();
1147 obj.vehicles.back()->place(*track, track.entry(), offset, Vehicle::BACK_BUFFER);
1151 void Train::Loader::block(unsigned id)
1159 blk = &obj.layout.get_block(id);
1161 catch(const key_error &)
1163 blocks_valid = false;
1169 entry = blk->get_endpoint_by_link(*prev_block);
1173 obj.blocks.push_back(BlockIter(blk, entry));
1176 if(blk->get_sensor_id())
1177 obj.layout.get_driver().set_sensor(blk->get_sensor_id(), true);
1182 void Train::Loader::block_hint(unsigned id)
1186 prev_block = &obj.layout.get_block(id);
1188 catch(const key_error &)
1190 blocks_valid = false;
1194 void Train::Loader::name(const string &n)
1199 void Train::Loader::quantized_speed()
1201 if(obj.speed_quantizer)
1202 load_sub(*obj.speed_quantizer);
1205 void Train::Loader::route(const string &n)
1207 obj.set_route(&obj.layout.get_route(n));
1210 void Train::Loader::timetable()
1212 Timetable *ttbl = new Timetable(obj);
1216 void Train::Loader::vehicle(ArticleNumber art_nr)
1218 const VehicleType &vtype = obj.layout.get_catalogue().get_vehicle(art_nr);
1219 Vehicle *veh = new Vehicle(obj.layout, vtype);
1220 obj.vehicles.back()->attach_back(*veh);
1221 obj.vehicles.push_back(veh);