3 This file is part of the MSP Märklin suite
4 Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
5 Distributed under the GPL
9 #include <msp/strings/formatter.h>
10 #include <msp/time/units.h>
11 #include <msp/time/utils.h>
12 #include "aicontrol.h"
13 #include "catalogue.h"
17 #include "simplecontroller.h"
18 #include "timetable.h"
19 #include "tracktype.h"
22 #include "vehicletype.h"
33 SetFlag(bool &f): flag(f) { flag = true; }
34 ~SetFlag() { flag = false; }
42 Train::Train(Layout &l, const VehicleType &t, unsigned a):
51 controller(new AIControl(*this, new SimpleController)),
55 speed_changing(false),
65 accurate_position(false),
68 if(!loco_type.is_locomotive())
69 throw InvalidParameterValue("Initial vehicle must be a locomotive");
71 vehicles.push_back(new Vehicle(layout, loco_type));
73 layout.add_train(*this);
75 layout.get_driver().add_loco(address);
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));
79 layout.signal_block_reserved.connect(sigc::mem_fun(this, &Train::block_reserved));
80 layout.get_driver().signal_sensor.connect(sigc::mem_fun(this, &Train::sensor_event));
81 layout.get_driver().signal_turnout.connect(sigc::mem_fun(this, &Train::turnout_event));
83 layout.get_driver().signal_halt.connect(sigc::mem_fun(this, &Train::halt_event));
85 controller->signal_control_changed.connect(sigc::mem_fun(this, &Train::control_changed));
92 for(vector<Vehicle *>::iterator i=vehicles.begin(); i!=vehicles.end(); ++i)
94 layout.remove_train(*this);
97 void Train::set_name(const string &n)
101 signal_name_changed.emit(name);
104 void Train::set_priority(int p)
109 void Train::yield_to(const Train &t)
114 void Train::add_vehicle(const VehicleType &vt)
116 Vehicle *veh = new Vehicle(layout, vt);
117 vehicles.back()->attach_back(*veh);
118 vehicles.push_back(veh);
121 void Train::remove_vehicle(unsigned i)
123 if(i>=vehicles.size())
124 throw InvalidParameterValue("Vehicle index out of range");
126 throw InvalidParameterValue("Can't remove the locomotive");
128 vehicles.erase(vehicles.begin()+i);
129 if(i<vehicles.size())
130 vehicles[i-1]->attach_back(*vehicles[i]);
133 unsigned Train::get_n_vehicles() const
135 return vehicles.size();
138 Vehicle &Train::get_vehicle(unsigned i)
140 if(i>=vehicles.size())
141 throw InvalidParameterValue("Vehicle index out of range");
145 const Vehicle &Train::get_vehicle(unsigned i) const
147 if(i>=vehicles.size())
148 throw InvalidParameterValue("Vehicle index out of range");
152 void Train::set_control(const string &n, float v)
154 controller->set_control(n, v);
157 void Train::set_active(bool a)
161 if(!a && controller->get_speed())
162 throw InvalidState("Can't deactivate while moving");
167 stop_timeout = Time::TimeStamp();
172 stop_timeout = Time::now()+2*Time::sec;
173 set_status("Stopped");
177 void Train::set_function(unsigned func, bool state)
179 if(!loco_type.get_functions().count(func))
180 throw InvalidParameterValue("Invalid function");
182 layout.get_driver().set_loco_function(address, func, state);
184 layout.get_driver().set_loco_function(address+1, func-4, state);
187 float Train::get_control(const string &ctrl) const
189 return controller->get_control(ctrl).value;
192 float Train::get_speed() const
194 return controller->get_speed();
197 bool Train::get_function(unsigned func) const
199 return (functions>>func)&1;
202 void Train::set_timetable(Timetable *tt)
208 void Train::set_route(const Route *r)
210 if(!rsv_blocks.empty())
212 for(list<BlockRef>::iterator i=rsv_blocks.begin(); i!=rsv_blocks.end(); ++i)
213 if(i->block->get_sensor_id())
215 release_blocks(rsv_blocks, ++i, rsv_blocks.end());
222 end_of_route = false;
224 if(route && !cur_blocks.empty())
226 BlockRef &last = (rsv_blocks.empty() ? cur_blocks.back() : rsv_blocks.back());
227 BlockRef next = last.next();
228 const Block::Endpoint &ep = next.block->get_endpoints()[next.entry];
229 if(!route->get_tracks().count(ep.track))
232 route = Route::find(*ep.track, ep.track_ep, *next_route);
238 signal_route_changed.emit(route);
241 void Train::go_to(const Track &to)
243 for(list<BlockRef>::const_iterator i=cur_blocks.begin(); i!=cur_blocks.end(); ++i)
244 if(i->block->get_tracks().count(const_cast<Track *>(&to)))
246 signal_arrived.emit();
252 if(rsv_blocks.empty())
253 last = &cur_blocks.back();
256 for(list<BlockRef>::iterator i=rsv_blocks.begin(); (i!=rsv_blocks.end() && !last); ++i)
257 if(i->block->get_sensor_id())
261 BlockRef next = last->next();
262 const Block::Endpoint &ep = next.block->get_endpoints()[next.entry];
264 set_route(Route::find(*ep.track, ep.track_ep, to));
267 void Train::place(Block &block, unsigned entry)
269 if(controller->get_speed())
270 throw InvalidState("Must be stopped before placing");
272 release_blocks(rsv_blocks);
273 release_blocks(cur_blocks);
276 accurate_position = false;
278 if(!block.reserve(this))
280 set_status("Unplaced");
284 cur_blocks.push_back(BlockRef(&block, entry));
287 unsigned exit = block.traverse(entry);
288 const Block::Endpoint &bep = block.get_endpoints()[exit];
289 Track *track = bep.track->get_link(bep.track_ep);
290 unsigned ep = track->get_endpoint_by_link(*bep.track);
291 vehicles.front()->place(*track, ep, 0, Vehicle::FRONT_BUFFER);
295 const Block::Endpoint &bep = block.get_endpoints()[entry];
296 vehicles.back()->place(*bep.track, bep.track_ep, 0, Vehicle::BACK_BUFFER);
300 void Train::unplace()
302 if(controller->get_speed())
303 throw InvalidState("Must be stopped before unplacing");
305 release_blocks(rsv_blocks);
306 release_blocks(cur_blocks);
309 accurate_position = false;
311 for(vector<Vehicle *>::iterator i=vehicles.begin(); i!=vehicles.end(); ++i)
314 set_status("Unplaced");
317 bool Train::free_block(Block &block)
319 float margin = 10*layout.get_catalogue().get_scale();
320 if(get_reserved_distance_until(&block, false)<controller->get_braking_distance()*1.3+margin)
324 for(list<BlockRef>::iterator i=rsv_blocks.begin(); i!=rsv_blocks.end(); ++i)
330 release_blocks(rsv_blocks, i, rsv_blocks.end());
333 else if(i->block->get_sensor_id())
340 int Train::get_entry_to_block(Block &block) const
342 for(list<BlockRef>::const_iterator i=cur_blocks.begin(); i!=cur_blocks.end(); ++i)
345 for(list<BlockRef>::const_iterator i=rsv_blocks.begin(); i!=rsv_blocks.end(); ++i)
351 float Train::get_reserved_distance() const
353 return get_reserved_distance_until(0, false);
356 void Train::tick(const Time::TimeStamp &t, const Time::TimeDelta &dt)
358 if(!active && stop_timeout && t>=stop_timeout)
360 release_blocks(rsv_blocks);
361 end_of_route = false;
362 stop_timeout = Time::TimeStamp();
365 Driver &driver = layout.get_driver();
369 controller->tick(dt);
370 float speed = controller->get_speed();
371 unsigned speed_notch = find_speed(speed);
373 if(controller->get_reverse()!=reverse)
375 reverse = controller->get_reverse();
376 driver.set_loco_reverse(address, reverse);
378 release_blocks(rsv_blocks);
379 reverse_blocks(cur_blocks);
383 if(speed_notch!=current_speed && !speed_changing && !driver.is_halted() && driver.get_power())
385 speed_changing = true;
386 driver.set_loco_speed(address, speed_notch);
391 set_status(format("Traveling %d kmh", get_travel_speed()));
393 set_status("Waiting");
401 Vehicle &vehicle = *(reverse ? vehicles.back() : vehicles.front());
402 Track *track = vehicle.get_track();
405 for(list<BlockRef>::const_iterator i=cur_blocks.begin(); (!ok && i!=cur_blocks.end()); ++i)
406 ok = i->block->get_tracks().count(track);
408 float d = get_real_speed(current_speed)*(dt/Time::sec);
411 SetFlag setf(advancing);
412 vehicle.advance(reverse ? -d : d);
414 else if(accurate_position)
417 if(overshoot_dist>40*layout.get_catalogue().get_scale())
419 layout.emergency(name+" has not arrived at sensor");
420 accurate_position = false;
424 else if(end_of_route && rsv_blocks.empty())
426 signal_arrived.emit();
430 if(!cur_blocks.empty() && !cur_blocks.front().block->get_sensor_id())
432 float dist = get_reserved_distance_until(cur_blocks.front().block, true);
434 if(dist>10*layout.get_catalogue().get_scale())
436 cur_blocks.front().block->reserve(0);
437 cur_blocks.erase(cur_blocks.begin());
442 void Train::save(list<DataFile::Statement> &st) const
444 st.push_back((DataFile::Statement("name"), name));
446 st.push_back((DataFile::Statement("priority"), priority));
448 for(vector<Vehicle *>::const_iterator i=vehicles.begin(); i!=vehicles.end(); ++i)
449 if(i!=vehicles.begin())
450 st.push_back((DataFile::Statement("vehicle"), (*i)->get_type().get_article_number()));
452 for(unsigned i=0; i<=14; ++i)
453 if(real_speed[i].weight)
454 st.push_back((DataFile::Statement("real_speed"), i, real_speed[i].speed, real_speed[i].weight));
456 if(!cur_blocks.empty())
458 list<BlockRef> blocks = cur_blocks;
460 reverse_blocks(blocks);
462 Block *prev = blocks.front().block->get_endpoints()[blocks.front().entry].link;
463 st.push_back((DataFile::Statement("block_hint"), prev->get_id()));
465 for(list<BlockRef>::const_iterator i=blocks.begin(); i!=blocks.end(); ++i)
466 st.push_back((DataFile::Statement("block"), i->block->get_id()));
471 if(!route->is_temporary())
472 st.push_back((DataFile::Statement("route"), route->get_name()));
473 else if(next_route && !next_route->is_temporary())
474 st.push_back((DataFile::Statement("route"), next_route->get_name()));
479 DataFile::Statement ss("timetable");
480 timetable->save(ss.sub);
485 void Train::control_changed(const Controller::Control &ctrl)
487 signal_control_changed.emit(ctrl.name, ctrl.value);
490 void Train::loco_speed_event(unsigned addr, unsigned speed, bool)
494 current_speed = speed;
495 speed_changing = false;
500 void Train::loco_func_event(unsigned addr, unsigned func, bool state)
502 if(addr==address || (addr==address+1 && loco_type.get_max_function()>4))
507 functions |= 1<<func;
509 functions &= ~(1<<func);
511 signal_function_changed.emit(func, state);
515 void Train::sensor_event(unsigned addr, bool state)
519 // Find the first sensor block from our reserved blocks that isn't this sensor
520 list<BlockRef>::iterator i;
522 for(i=rsv_blocks.begin(); i!=rsv_blocks.end(); ++i)
523 if(i->block->get_sensor_id())
525 if(i->block->get_sensor_id()!=addr)
538 if(result==1 && i!=rsv_blocks.begin())
540 // Compute speed and update related state
541 float travel_time_secs = (Time::now()-last_entry_time)/Time::sec;
547 RealSpeed &rs = real_speed[current_speed];
548 rs.add(travel_dist/travel_time_secs, travel_time_secs);
550 set_status(format("Traveling %d kmh", get_travel_speed()));
555 for(list<BlockRef>::iterator j=rsv_blocks.begin(); j!=i; ++j)
557 j->block->traverse(j->entry, &block_len);
558 travel_dist += block_len;
560 if(j->block->get_sensor_id()==addr && !advancing)
562 const Block::Endpoint &bep = j->block->get_endpoints()[j->entry];
565 Track *track = bep.track->get_link(bep.track_ep);
566 unsigned ep = track->get_endpoint_by_link(*bep.track);
567 vehicles.back()->place(*track, ep, 0, Vehicle::BACK_AXLE);
570 vehicles.front()->place(*bep.track, bep.track_ep, 0, Vehicle::FRONT_AXLE);
573 last_entry_time = Time::now();
575 accurate_position = true;
578 // Check if we've reached the next route
581 const set<const Track *> &rtracks = next_route->get_tracks();
582 for(list<BlockRef>::iterator j=rsv_blocks.begin(); j!=i; ++j)
583 if(rtracks.count(j->block->get_endpoints()[j->entry].track))
588 signal_route_changed.emit(route);
593 // Move blocks up to the next sensor to our current blocks
594 cur_blocks.splice(cur_blocks.end(), rsv_blocks, rsv_blocks.begin(), i);
596 // Try to get more blocks if we're moving
601 layout.emergency("Sensor for "+name+" triggered out of order");
605 const Vehicle &veh = *(reverse ? vehicles.front() : vehicles.back());
607 // Find the first sensor in our current blocks that's still active
608 list<BlockRef>::iterator end = cur_blocks.begin();
609 for(list<BlockRef>::iterator i=cur_blocks.begin(); i!=cur_blocks.end(); ++i)
611 if(i->block->get_tracks().count(veh.get_track()))
613 if(i->block->get_sensor_id())
615 if(layout.get_driver().get_sensor(i->block->get_sensor_id()))
625 if(end!=cur_blocks.begin() && end!=cur_blocks.end())
626 // Free blocks up to the last inactive sensor
627 release_blocks(cur_blocks, cur_blocks.begin(), end);
631 void Train::turnout_event(unsigned addr, bool)
635 unsigned pending_addr = pending_block->get_turnout_id();
636 bool double_addr = (*pending_block->get_tracks().begin())->get_type().is_double_address();
637 if(addr==pending_addr || (double_addr && addr==pending_addr+1))
647 void Train::halt_event(bool h)
650 accurate_position = false;
653 void Train::block_reserved(const Block &block, const Train *train)
655 if(&block==pending_block && !train && !reserving)
659 unsigned Train::reserve_more()
665 if(!rsv_blocks.empty())
666 start = &rsv_blocks.back();
667 else if(!cur_blocks.empty())
668 start = &cur_blocks.back();
674 // See how many sensor blocks and how much track we already have
677 for(list<BlockRef>::const_iterator i=rsv_blocks.begin(); i!=rsv_blocks.end(); ++i)
679 if(i->block->get_sensor_id())
684 i->block->traverse(i->entry, &length);
692 const Route *cur_route = 0;
695 const set<Track *> &tracks = start->block->get_tracks();
696 for(set<Track *>::const_iterator i=tracks.begin(); (cur_route!=route && i!=tracks.end()); ++i)
698 if(route->get_tracks().count(*i))
700 else if(next_route && next_route->get_tracks().count(*i))
701 cur_route = next_route;
705 float approach_margin = 50*layout.get_catalogue().get_scale();
706 float min_dist = controller->get_braking_distance()*1.3+approach_margin*2;
708 BlockRef *last = start;
709 BlockRef *good = start;
710 unsigned good_sens = nsens;
711 float good_dist = dist;
712 Train *blocking_train = 0;
713 std::list<BlockRef> contested_blocks;
715 SetFlag setf(reserving);
717 while(good_sens<3 || good_dist<min_dist || !contested_blocks.empty())
719 // Traverse to the next block
721 unsigned exit = last->block->traverse(last->entry, cur_route, &length);
722 Block *link = last->block->get_link(exit);
726 int entry = link->get_endpoint_by_link(*last->block);
728 throw LogicError("Block links are inconsistent!");
730 const Block::Endpoint &entry_ep = link->get_endpoints()[entry];
734 if(cur_route!=next_route && next_route && next_route->get_tracks().count(entry_ep.track))
735 cur_route = next_route;
736 else if(!cur_route->get_tracks().count(entry_ep.track))
738 // Keep the blocks if we arrived at the end of the route
749 else if(route && route->get_tracks().count(entry_ep.track))
752 if(link->get_endpoints().size()<2)
765 if(link->get_train()!=blocking_train)
767 // XXX is it possible that this won't free all the blocks we want?
768 if(blocking_train->free_block(*contested_blocks.back().block))
770 // Roll back and start actually reserving the blocks
771 last = &rsv_blocks.back();
772 if(blocking_train->get_priority()==priority)
773 blocking_train->yield_to(*this);
779 pending_block = contested_blocks.front().block;
785 contested_blocks.push_back(BlockRef(link, entry));
786 last = &contested_blocks.back();
791 bool reserved = link->reserve(this);
794 /* We've found another train. If it wants to exit the block from the
795 same endpoint we're trying to enter from or the other way around,
796 treat it as coming towards us. Otherwise treat it as going in the
798 Train *other_train = link->get_train();
799 int other_entry = other_train->get_entry_to_block(*link);
801 throw LogicError("Block reservation inconsistency");
803 int other_prio = other_train->get_priority();
805 bool entry_conflict = (static_cast<unsigned>(entry)==link->traverse(other_entry));
806 bool exit_conflict = (link->traverse(entry)==static_cast<unsigned>(other_entry));
807 if(!entry_conflict && !exit_conflict)
809 /* Same direction, keep the blocks we got so far and wait for the
810 other train to pass */
815 // Ask a lesser priority train to free the block for us
816 if(other_train->get_priority()<priority)
817 if(other_train->free_block(*link))
818 reserved = link->reserve(this);
820 else if(other_prio<priority || (other_prio==priority && other_train!=yielding_to && entry_conflict))
822 /* A lesser priority train is coming at us, we must ask it to free
823 enough blocks to get clear of it to avoid a potential deadlock */
824 blocking_train = other_train;
825 contested_blocks.clear();
826 contested_blocks.push_back(BlockRef(link, entry));
827 last = &contested_blocks.back();
833 pending_block = link;
838 if(link->get_turnout_id())
840 const Endpoint &track_ep = entry_ep.track->get_type().get_endpoints()[entry_ep.track_ep];
842 // Keep the blocks reserved so far, as either us or the other train can diverge
847 // Figure out what path we'd like to take on the turnout
850 path = cur_route->get_turnout(link->get_turnout_id());
852 path = entry_ep.track->get_active_path();
853 if(!((track_ep.paths>>path)&1))
855 for(unsigned i=0; track_ep.paths>>i; ++i)
856 if((track_ep.paths>>i)&1)
860 if(path!=static_cast<int>(entry_ep.track->get_active_path()))
862 // The turnout is set to wrong path - switch and wait for it
863 pending_block = link;
864 entry_ep.track->set_active_path(path);
873 if(!contested_blocks.empty() && contested_blocks.front().block==link)
874 contested_blocks.pop_front();
876 rsv_blocks.push_back(BlockRef(link, entry));
877 last = &rsv_blocks.back();
878 if(last->block->get_sensor_id())
884 // Unreserve blocks that were not good
885 while(!rsv_blocks.empty() && &rsv_blocks.back()!=good)
887 rsv_blocks.back().block->reserve(0);
888 rsv_blocks.erase(--rsv_blocks.end());
891 if(!rsv_blocks.empty() && &rsv_blocks.back()!=start)
892 // We got some new blocks, so no longer need to yield
895 // Make any sensorless blocks at the beginning immediately current
896 list<BlockRef>::iterator i;
897 for(i=rsv_blocks.begin(); (i!=rsv_blocks.end() && !i->block->get_sensor_id()); ++i) ;
898 if(i!=rsv_blocks.begin())
899 cur_blocks.splice(cur_blocks.end(), rsv_blocks, rsv_blocks.begin(), i);
904 float Train::get_reserved_distance_until(const Block *until_block, bool back) const
906 if(cur_blocks.empty())
909 Vehicle &veh = *(reverse!=back ? vehicles.back() : vehicles.front());
910 const VehicleType &vtype = veh.get_type();
912 Track *track = veh.get_track();
916 list<BlockRef>::const_iterator block = cur_blocks.begin();
917 while(block!=rsv_blocks.end() && !block->block->get_tracks().count(track))
920 if(block==cur_blocks.end())
924 block = rsv_blocks.begin();
927 if(block==rsv_blocks.end() || block->block==until_block)
930 unsigned entry = veh.get_entry();
932 float result = veh.get_offset();
934 entry = track->traverse(entry);
936 result = track->get_type().get_path_length(track->get_active_path())-result;
937 result -= vtype.get_length()/2;
941 if(track->get_type().get_endpoints().size()<2)
944 Track *next = track->get_link(track->traverse(entry));
946 if(!block->block->get_tracks().count(next))
950 if(block==cur_blocks.begin())
957 if(block==cur_blocks.end())
958 block = rsv_blocks.begin();
959 if(block==rsv_blocks.end())
963 if(block->block==until_block)
967 entry = next->get_endpoint_by_link(*track);
970 result += track->get_type().get_path_length(track->get_active_path());
976 float Train::get_real_speed(unsigned i) const
978 if(real_speed[i].weight)
979 return real_speed[i].speed;
983 for(low=i; low>0; --low)
984 if(real_speed[low].weight)
986 for(high=i; high<14; ++high)
987 if(real_speed[high].weight)
990 if(real_speed[high].weight)
992 if(real_speed[low].weight)
994 float f = float(i-low)/(high-low);
995 return real_speed[low].speed*(1-f)+real_speed[high].speed*f;
998 return real_speed[high].speed*float(i)/high;
1000 else if(real_speed[low].weight)
1001 return real_speed[low].speed*float(i)/low;
1006 unsigned Train::find_speed(float real) const
1008 if(real<=real_speed[0].speed)
1014 for(unsigned i=0; (!high && i<=14); ++i)
1015 if(real_speed[i].weight)
1018 if(real_speed[i].speed<real)
1032 return min(min(static_cast<unsigned>(low*real/real_speed[low].speed), 14U), last+3);
1035 float f = (real-real_speed[low].speed)/(real_speed[high].speed-real_speed[low].speed);
1036 return static_cast<unsigned>(low*(1-f)+high*f+0.5);
1039 float Train::get_travel_speed() const
1041 float speed = get_real_speed(current_speed);
1042 float scale = layout.get_catalogue().get_scale();
1043 return static_cast<int>(round(speed/scale*3.6/5))*5;
1046 void Train::set_status(const string &s)
1049 signal_status_changed.emit(s);
1052 void Train::release_blocks(list<BlockRef> &blocks)
1054 release_blocks(blocks, blocks.begin(), blocks.end());
1057 void Train::release_blocks(list<BlockRef> &blocks, list<BlockRef>::iterator begin, list<BlockRef>::iterator end)
1061 Block *block = begin->block;
1062 blocks.erase(begin++);
1067 void Train::reverse_blocks(list<BlockRef> &blocks) const
1070 for(list<BlockRef>::iterator i=blocks.begin(); i!=blocks.end(); ++i)
1071 i->entry = i->block->traverse(i->entry);
1075 Train::BlockRef::BlockRef(Block *b, unsigned e):
1080 Train::BlockRef Train::BlockRef::next() const
1082 Block *blk = block->get_endpoints()[block->traverse(entry)].link;
1084 throw InvalidState("At end of line");
1086 int ep = blk->get_endpoint_by_link(*block);
1088 throw LogicError("Block links are inconsistent");
1090 return BlockRef(blk, ep);
1094 Train::RealSpeed::RealSpeed():
1099 void Train::RealSpeed::add(float s, float w)
1101 speed = (speed*weight+s*w)/(weight+w);
1102 weight = min(weight+w, 300.0f);
1106 Train::Loader::Loader(Train &t):
1107 DataFile::BasicLoader<Train>(t),
1111 add("block", &Loader::block);
1112 add("block_hint", &Loader::block_hint);
1113 add("name", &Loader::name);
1114 add("priority", &Train::priority);
1115 add("real_speed", &Loader::real_speed);
1116 add("route", &Loader::route);
1117 add("timetable", &Loader::timetable);
1118 add("vehicle", &Loader::vehicle);
1121 void Train::Loader::finish()
1123 if(!obj.cur_blocks.empty())
1125 const BlockRef &blkref = obj.cur_blocks.front();
1126 const Block::Endpoint &bep = blkref.block->get_endpoints()[blkref.entry];
1127 obj.vehicles.back()->place(*bep.track, bep.track_ep, 0, Vehicle::BACK_BUFFER);
1129 obj.set_status("Stopped");
1133 void Train::Loader::block(unsigned id)
1141 blk = &obj.layout.get_block(id);
1143 catch(const KeyError &)
1145 blocks_valid = false;
1151 entry = blk->get_endpoint_by_link(*prev_block);
1156 obj.cur_blocks.push_back(BlockRef(blk, entry));
1158 if(blk->get_sensor_id())
1159 obj.layout.get_driver().set_sensor(blk->get_sensor_id(), true);
1164 void Train::Loader::block_hint(unsigned id)
1168 prev_block = &obj.layout.get_block(id);
1170 catch(const KeyError &)
1172 blocks_valid = false;
1176 void Train::Loader::name(const string &n)
1181 void Train::Loader::real_speed(unsigned i, float speed, float weight)
1183 obj.real_speed[i].speed = speed;
1184 obj.real_speed[i].weight = weight;
1187 void Train::Loader::route(const string &n)
1189 obj.set_route(&obj.layout.get_route(n));
1192 void Train::Loader::timetable()
1195 throw InvalidState("A timetable has already been loaded");
1197 obj.timetable = new Timetable(obj);
1198 load_sub(*obj.timetable);
1201 void Train::Loader::vehicle(unsigned n)
1203 const VehicleType &vtype = obj.layout.get_catalogue().get_vehicle(n);
1204 Vehicle *veh = new Vehicle(obj.layout, vtype);
1205 obj.vehicles.back()->attach_back(*veh);
1206 obj.vehicles.push_back(veh);
1209 } // namespace Marklin