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())
427 signal_arrived.emit();
431 if(!cur_blocks.empty() && !cur_blocks.front().block->get_sensor_id())
433 float dist = get_reserved_distance_until(cur_blocks.front().block, true);
435 if(dist>10*layout.get_catalogue().get_scale())
437 cur_blocks.front().block->reserve(0);
438 cur_blocks.erase(cur_blocks.begin());
443 void Train::save(list<DataFile::Statement> &st) const
445 st.push_back((DataFile::Statement("name"), name));
447 st.push_back((DataFile::Statement("priority"), priority));
449 for(vector<Vehicle *>::const_iterator i=vehicles.begin(); i!=vehicles.end(); ++i)
450 if(i!=vehicles.begin())
451 st.push_back((DataFile::Statement("vehicle"), (*i)->get_type().get_article_number()));
453 for(unsigned i=0; i<=14; ++i)
454 if(real_speed[i].weight)
455 st.push_back((DataFile::Statement("real_speed"), i, real_speed[i].speed, real_speed[i].weight));
457 if(!cur_blocks.empty())
459 list<BlockRef> blocks = cur_blocks;
461 reverse_blocks(blocks);
463 Block *prev = blocks.front().block->get_endpoints()[blocks.front().entry].link;
464 st.push_back((DataFile::Statement("block_hint"), prev->get_id()));
466 for(list<BlockRef>::const_iterator i=blocks.begin(); i!=blocks.end(); ++i)
467 st.push_back((DataFile::Statement("block"), i->block->get_id()));
472 if(!route->is_temporary())
473 st.push_back((DataFile::Statement("route"), route->get_name()));
474 else if(next_route && !next_route->is_temporary())
475 st.push_back((DataFile::Statement("route"), next_route->get_name()));
480 DataFile::Statement ss("timetable");
481 timetable->save(ss.sub);
486 void Train::control_changed(const Controller::Control &ctrl)
488 signal_control_changed.emit(ctrl.name, ctrl.value);
491 void Train::loco_speed_event(unsigned addr, unsigned speed, bool)
495 current_speed = speed;
496 speed_changing = false;
501 void Train::loco_func_event(unsigned addr, unsigned func, bool state)
503 if(addr==address || (addr==address+1 && loco_type.get_max_function()>4))
508 functions |= 1<<func;
510 functions &= ~(1<<func);
512 signal_function_changed.emit(func, state);
516 void Train::sensor_event(unsigned addr, bool state)
520 // Find the first sensor block from our reserved blocks that isn't this sensor
521 list<BlockRef>::iterator i;
523 for(i=rsv_blocks.begin(); i!=rsv_blocks.end(); ++i)
524 if(i->block->get_sensor_id())
526 if(i->block->get_sensor_id()!=addr)
539 if(result==1 && i!=rsv_blocks.begin())
541 // Compute speed and update related state
542 float travel_time_secs = (Time::now()-last_entry_time)/Time::sec;
548 RealSpeed &rs = real_speed[current_speed];
549 rs.add(travel_dist/travel_time_secs, travel_time_secs);
551 set_status(format("Traveling %d kmh", get_travel_speed()));
556 for(list<BlockRef>::iterator j=rsv_blocks.begin(); j!=i; ++j)
558 j->block->traverse(j->entry, &block_len);
559 travel_dist += block_len;
561 if(j->block->get_sensor_id()==addr && !advancing)
563 const Block::Endpoint &bep = j->block->get_endpoints()[j->entry];
566 Track *track = bep.track->get_link(bep.track_ep);
567 unsigned ep = track->get_endpoint_by_link(*bep.track);
568 vehicles.back()->place(*track, ep, 0, Vehicle::BACK_AXLE);
571 vehicles.front()->place(*bep.track, bep.track_ep, 0, Vehicle::FRONT_AXLE);
574 last_entry_time = Time::now();
576 accurate_position = true;
579 // Check if we've reached the next route
582 const set<const Track *> &rtracks = next_route->get_tracks();
583 for(list<BlockRef>::iterator j=rsv_blocks.begin(); j!=i; ++j)
584 if(rtracks.count(j->block->get_endpoints()[j->entry].track))
589 signal_route_changed.emit(route);
594 // Move blocks up to the next sensor to our current blocks
595 cur_blocks.splice(cur_blocks.end(), rsv_blocks, rsv_blocks.begin(), i);
597 // Try to get more blocks if we're moving
602 layout.emergency("Sensor for "+name+" triggered out of order");
606 const Vehicle &veh = *(reverse ? vehicles.front() : vehicles.back());
608 // Find the first sensor in our current blocks that's still active
609 list<BlockRef>::iterator end = cur_blocks.begin();
610 for(list<BlockRef>::iterator i=cur_blocks.begin(); i!=cur_blocks.end(); ++i)
612 if(i->block->get_tracks().count(veh.get_track()))
614 if(i->block->get_sensor_id())
616 if(layout.get_driver().get_sensor(i->block->get_sensor_id()))
626 if(end!=cur_blocks.begin() && end!=cur_blocks.end())
627 // Free blocks up to the last inactive sensor
628 release_blocks(cur_blocks, cur_blocks.begin(), end);
632 void Train::turnout_event(unsigned addr, bool)
636 unsigned pending_addr = pending_block->get_turnout_id();
637 bool double_addr = (*pending_block->get_tracks().begin())->get_type().is_double_address();
638 if(addr==pending_addr || (double_addr && addr==pending_addr+1))
648 void Train::halt_event(bool h)
651 accurate_position = false;
654 void Train::block_reserved(const Block &block, const Train *train)
656 if(&block==pending_block && !train && !reserving)
660 unsigned Train::reserve_more()
666 if(!rsv_blocks.empty())
667 start = &rsv_blocks.back();
668 else if(!cur_blocks.empty())
669 start = &cur_blocks.back();
675 // See how many sensor blocks and how much track we already have
678 for(list<BlockRef>::const_iterator i=rsv_blocks.begin(); i!=rsv_blocks.end(); ++i)
680 if(i->block->get_sensor_id())
685 i->block->traverse(i->entry, &length);
693 const Route *cur_route = 0;
696 const set<Track *> &tracks = start->block->get_tracks();
697 for(set<Track *>::const_iterator i=tracks.begin(); (cur_route!=route && i!=tracks.end()); ++i)
699 if(route->get_tracks().count(*i))
701 else if(next_route && next_route->get_tracks().count(*i))
702 cur_route = next_route;
706 float approach_margin = 50*layout.get_catalogue().get_scale();
707 float min_dist = controller->get_braking_distance()*1.3+approach_margin*2;
709 BlockRef *last = start;
710 BlockRef *good = start;
711 unsigned good_sens = nsens;
712 float good_dist = dist;
713 Train *blocking_train = 0;
714 std::list<BlockRef> contested_blocks;
716 SetFlag setf(reserving);
718 while(good_sens<3 || good_dist<min_dist || !contested_blocks.empty())
720 // Traverse to the next block
722 unsigned exit = last->block->traverse(last->entry, cur_route, &length);
723 Block *link = last->block->get_link(exit);
727 int entry = link->get_endpoint_by_link(*last->block);
729 throw LogicError("Block links are inconsistent!");
731 const Block::Endpoint &entry_ep = link->get_endpoints()[entry];
735 if(cur_route!=next_route && next_route && next_route->get_tracks().count(entry_ep.track))
736 cur_route = next_route;
737 else if(!cur_route->get_tracks().count(entry_ep.track))
739 // Keep the blocks if we arrived at the end of the route
750 else if(route && route->get_tracks().count(entry_ep.track))
753 if(link->get_endpoints().size()<2)
766 if(link->get_train()!=blocking_train)
768 // XXX is it possible that this won't free all the blocks we want?
769 if(blocking_train->free_block(*contested_blocks.back().block))
771 // Roll back and start actually reserving the blocks
772 last = &rsv_blocks.back();
773 if(blocking_train->get_priority()==priority)
774 blocking_train->yield_to(*this);
780 pending_block = contested_blocks.front().block;
786 contested_blocks.push_back(BlockRef(link, entry));
787 last = &contested_blocks.back();
792 bool reserved = link->reserve(this);
795 /* We've found another train. If it wants to exit the block from the
796 same endpoint we're trying to enter from or the other way around,
797 treat it as coming towards us. Otherwise treat it as going in the
799 Train *other_train = link->get_train();
800 int other_entry = other_train->get_entry_to_block(*link);
802 throw LogicError("Block reservation inconsistency");
804 int other_prio = other_train->get_priority();
806 bool entry_conflict = (static_cast<unsigned>(entry)==link->traverse(other_entry));
807 bool exit_conflict = (link->traverse(entry)==static_cast<unsigned>(other_entry));
808 if(!entry_conflict && !exit_conflict)
810 /* Same direction, keep the blocks we got so far and wait for the
811 other train to pass */
816 // Ask a lesser priority train to free the block for us
817 if(other_train->get_priority()<priority)
818 if(other_train->free_block(*link))
819 reserved = link->reserve(this);
821 else if(other_prio<priority || (other_prio==priority && other_train!=yielding_to && entry_conflict))
823 /* A lesser priority train is coming at us, we must ask it to free
824 enough blocks to get clear of it to avoid a potential deadlock */
825 blocking_train = other_train;
826 contested_blocks.clear();
827 contested_blocks.push_back(BlockRef(link, entry));
828 last = &contested_blocks.back();
834 pending_block = link;
839 if(link->get_turnout_id())
841 const Endpoint &track_ep = entry_ep.track->get_type().get_endpoints()[entry_ep.track_ep];
843 // Keep the blocks reserved so far, as either us or the other train can diverge
848 // Figure out what path we'd like to take on the turnout
851 path = cur_route->get_turnout(link->get_turnout_id());
853 path = entry_ep.track->get_active_path();
854 if(!((track_ep.paths>>path)&1))
856 for(unsigned i=0; track_ep.paths>>i; ++i)
857 if((track_ep.paths>>i)&1)
861 if(path!=static_cast<int>(entry_ep.track->get_active_path()))
863 // The turnout is set to wrong path - switch and wait for it
864 pending_block = link;
865 entry_ep.track->set_active_path(path);
874 if(!contested_blocks.empty() && contested_blocks.front().block==link)
875 contested_blocks.pop_front();
877 rsv_blocks.push_back(BlockRef(link, entry));
878 last = &rsv_blocks.back();
879 if(last->block->get_sensor_id())
885 // Unreserve blocks that were not good
886 while(!rsv_blocks.empty() && &rsv_blocks.back()!=good)
888 rsv_blocks.back().block->reserve(0);
889 rsv_blocks.erase(--rsv_blocks.end());
892 if(!rsv_blocks.empty() && &rsv_blocks.back()!=start)
893 // We got some new blocks, so no longer need to yield
896 // Make any sensorless blocks at the beginning immediately current
897 list<BlockRef>::iterator i;
898 for(i=rsv_blocks.begin(); (i!=rsv_blocks.end() && !i->block->get_sensor_id()); ++i) ;
899 if(i!=rsv_blocks.begin())
900 cur_blocks.splice(cur_blocks.end(), rsv_blocks, rsv_blocks.begin(), i);
905 float Train::get_reserved_distance_until(const Block *until_block, bool back) const
907 if(cur_blocks.empty())
910 Vehicle &veh = *(reverse!=back ? vehicles.back() : vehicles.front());
911 const VehicleType &vtype = veh.get_type();
913 Track *track = veh.get_track();
917 list<BlockRef>::const_iterator block = cur_blocks.begin();
918 while(block!=rsv_blocks.end() && !block->block->get_tracks().count(track))
921 if(block==cur_blocks.end())
925 block = rsv_blocks.begin();
928 if(block==rsv_blocks.end() || block->block==until_block)
931 unsigned entry = veh.get_entry();
933 float result = veh.get_offset();
935 entry = track->traverse(entry);
937 result = track->get_type().get_path_length(track->get_active_path())-result;
938 result -= vtype.get_length()/2;
942 if(track->get_type().get_endpoints().size()<2)
945 Track *next = track->get_link(track->traverse(entry));
947 if(!block->block->get_tracks().count(next))
951 if(block==cur_blocks.begin())
958 if(block==cur_blocks.end())
959 block = rsv_blocks.begin();
960 if(block==rsv_blocks.end())
964 if(block->block==until_block)
968 entry = next->get_endpoint_by_link(*track);
971 result += track->get_type().get_path_length(track->get_active_path());
977 float Train::get_real_speed(unsigned i) const
979 if(real_speed[i].weight)
980 return real_speed[i].speed;
984 for(low=i; low>0; --low)
985 if(real_speed[low].weight)
987 for(high=i; high<14; ++high)
988 if(real_speed[high].weight)
991 if(real_speed[high].weight)
993 if(real_speed[low].weight)
995 float f = float(i-low)/(high-low);
996 return real_speed[low].speed*(1-f)+real_speed[high].speed*f;
999 return real_speed[high].speed*float(i)/high;
1001 else if(real_speed[low].weight)
1002 return real_speed[low].speed*float(i)/low;
1007 unsigned Train::find_speed(float real) const
1009 if(real<=real_speed[0].speed)
1015 for(unsigned i=0; (!high && i<=14); ++i)
1016 if(real_speed[i].weight)
1019 if(real_speed[i].speed<real)
1033 return min(min(static_cast<unsigned>(low*real/real_speed[low].speed), 14U), last+3);
1036 float f = (real-real_speed[low].speed)/(real_speed[high].speed-real_speed[low].speed);
1037 return static_cast<unsigned>(low*(1-f)+high*f+0.5);
1040 float Train::get_travel_speed() const
1042 float speed = get_real_speed(current_speed);
1043 float scale = layout.get_catalogue().get_scale();
1044 return static_cast<int>(round(speed/scale*3.6/5))*5;
1047 void Train::set_status(const string &s)
1050 signal_status_changed.emit(s);
1053 void Train::release_blocks(list<BlockRef> &blocks)
1055 release_blocks(blocks, blocks.begin(), blocks.end());
1058 void Train::release_blocks(list<BlockRef> &blocks, list<BlockRef>::iterator begin, list<BlockRef>::iterator end)
1062 Block *block = begin->block;
1063 blocks.erase(begin++);
1068 void Train::reverse_blocks(list<BlockRef> &blocks) const
1071 for(list<BlockRef>::iterator i=blocks.begin(); i!=blocks.end(); ++i)
1072 i->entry = i->block->traverse(i->entry);
1076 Train::BlockRef::BlockRef(Block *b, unsigned e):
1081 Train::BlockRef Train::BlockRef::next() const
1083 Block *blk = block->get_endpoints()[block->traverse(entry)].link;
1085 throw InvalidState("At end of line");
1087 int ep = blk->get_endpoint_by_link(*block);
1089 throw LogicError("Block links are inconsistent");
1091 return BlockRef(blk, ep);
1095 Train::RealSpeed::RealSpeed():
1100 void Train::RealSpeed::add(float s, float w)
1102 speed = (speed*weight+s*w)/(weight+w);
1103 weight = min(weight+w, 300.0f);
1107 Train::Loader::Loader(Train &t):
1108 DataFile::BasicLoader<Train>(t),
1112 add("block", &Loader::block);
1113 add("block_hint", &Loader::block_hint);
1114 add("name", &Loader::name);
1115 add("priority", &Train::priority);
1116 add("real_speed", &Loader::real_speed);
1117 add("route", &Loader::route);
1118 add("timetable", &Loader::timetable);
1119 add("vehicle", &Loader::vehicle);
1122 void Train::Loader::finish()
1124 if(!obj.cur_blocks.empty())
1126 const BlockRef &blkref = obj.cur_blocks.front();
1127 const Block::Endpoint &bep = blkref.block->get_endpoints()[blkref.entry];
1128 float offset = 2*obj.layout.get_catalogue().get_scale();
1129 obj.vehicles.back()->place(*bep.track, bep.track_ep, offset, Vehicle::BACK_BUFFER);
1131 obj.set_status("Stopped");
1135 void Train::Loader::block(unsigned id)
1143 blk = &obj.layout.get_block(id);
1145 catch(const KeyError &)
1147 blocks_valid = false;
1153 entry = blk->get_endpoint_by_link(*prev_block);
1158 obj.cur_blocks.push_back(BlockRef(blk, entry));
1160 if(blk->get_sensor_id())
1161 obj.layout.get_driver().set_sensor(blk->get_sensor_id(), true);
1166 void Train::Loader::block_hint(unsigned id)
1170 prev_block = &obj.layout.get_block(id);
1172 catch(const KeyError &)
1174 blocks_valid = false;
1178 void Train::Loader::name(const string &n)
1183 void Train::Loader::real_speed(unsigned i, float speed, float weight)
1185 obj.real_speed[i].speed = speed;
1186 obj.real_speed[i].weight = weight;
1189 void Train::Loader::route(const string &n)
1191 obj.set_route(&obj.layout.get_route(n));
1194 void Train::Loader::timetable()
1197 throw InvalidState("A timetable has already been loaded");
1199 obj.timetable = new Timetable(obj);
1200 load_sub(*obj.timetable);
1203 void Train::Loader::vehicle(unsigned n)
1205 const VehicleType &vtype = obj.layout.get_catalogue().get_vehicle(n);
1206 Vehicle *veh = new Vehicle(obj.layout, vtype);
1207 obj.vehicles.back()->attach_back(*veh);
1208 obj.vehicles.push_back(veh);
1211 } // namespace Marklin