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 bool Train::free_block(Block &block)
303 for(list<BlockRef>::iterator i=rsv_blocks.begin(); i!=rsv_blocks.end(); ++i)
309 release_blocks(rsv_blocks, i, rsv_blocks.end());
312 else if(i->block->get_sensor_id())
319 int Train::get_entry_to_block(Block &block) const
321 for(list<BlockRef>::const_iterator i=cur_blocks.begin(); i!=cur_blocks.end(); ++i)
324 for(list<BlockRef>::const_iterator i=rsv_blocks.begin(); i!=rsv_blocks.end(); ++i)
330 float Train::get_reserved_distance() const
332 if(cur_blocks.empty())
335 Vehicle &veh = *(reverse ? vehicles.back() : vehicles.front());
336 const VehicleType &vtype = veh.get_type();
338 Track *track = veh.get_track();
341 unsigned entry = veh.get_entry();
343 float result = -vtype.get_length()/2;
346 entry = track->traverse(entry);
347 result += veh.get_offset();
350 result -= veh.get_offset();
353 list<BlockRef>::const_iterator block = cur_blocks.begin();
356 if(!first || !reverse)
357 result += track->get_type().get_path_length(track->get_active_path());
360 if(track->get_type().get_endpoints().size()<2)
363 unsigned exit = track->traverse(entry);
364 Track *next = track->get_link(exit);
366 while(!block->block->get_tracks().count(next))
369 if(block==cur_blocks.end())
370 block = rsv_blocks.begin();
371 if(block==rsv_blocks.end())
375 entry = next->get_endpoint_by_link(*track);
380 void Train::tick(const Time::TimeStamp &t, const Time::TimeDelta &dt)
382 if(!active && stop_timeout && t>=stop_timeout)
384 release_blocks(rsv_blocks);
385 end_of_route = false;
386 stop_timeout = Time::TimeStamp();
389 Driver &driver = layout.get_driver();
393 controller->tick(dt);
394 float speed = controller->get_speed();
395 unsigned speed_notch = find_speed(speed);
397 if(controller->get_reverse()!=reverse)
399 reverse = controller->get_reverse();
400 driver.set_loco_reverse(address, reverse);
402 release_blocks(rsv_blocks);
403 reverse_blocks(cur_blocks);
407 if(speed_notch!=current_speed && !speed_changing && !driver.is_halted() && driver.get_power())
409 speed_changing = true;
410 driver.set_loco_speed(address, speed_notch);
415 set_status(format("Traveling %d kmh", get_travel_speed()));
417 set_status("Waiting");
425 Vehicle &vehicle = *(reverse ? vehicles.back() : vehicles.front());
426 Track *track = vehicle.get_track();
429 for(list<BlockRef>::const_iterator i=cur_blocks.begin(); (!ok && i!=cur_blocks.end()); ++i)
430 ok = i->block->get_tracks().count(track);
432 float d = get_real_speed(current_speed)*(dt/Time::sec);
435 SetFlag setf(advancing);
436 vehicle.advance(reverse ? -d : d);
438 else if(accurate_position)
441 if(overshoot_dist>40*layout.get_catalogue().get_scale())
443 layout.emergency(name+" has not arrived at sensor");
444 accurate_position = false;
448 else if(end_of_route && rsv_blocks.empty())
450 signal_arrived.emit();
454 if(!cur_blocks.empty() && !cur_blocks.front().block->get_sensor_id())
456 Vehicle &veh = *(reverse ? vehicles.front() : vehicles.back());
458 list<BlockRef>::iterator i = cur_blocks.begin();
459 const Block::Endpoint &bep = i->block->get_endpoints()[i->entry];
461 Track *track = bep.track;
462 unsigned entry = bep.track_ep;
465 float dist = veh.get_offset();
467 dist = veh.get_track()->get_type().get_path_length(veh.get_track()->get_active_path())-dist;
468 dist -= veh.get_type().get_length()/2;
471 if(track==veh.get_track())
477 if(i!=cur_blocks.begin())
479 float path_len = track->get_type().get_path_length(track->get_active_path());
483 unsigned exit = track->traverse(entry);
484 Track *next = track->get_link(exit);
485 entry = next->get_endpoint_by_link(*track);
488 if(!i->block->get_tracks().count(track))
491 if(i==cur_blocks.end())
496 if(found && i!=cur_blocks.begin() && dist>10*layout.get_catalogue().get_scale())
498 cur_blocks.front().block->reserve(0);
499 cur_blocks.erase(cur_blocks.begin());
504 void Train::save(list<DataFile::Statement> &st) const
506 st.push_back((DataFile::Statement("name"), name));
508 st.push_back((DataFile::Statement("priority"), priority));
510 for(vector<Vehicle *>::const_iterator i=vehicles.begin(); i!=vehicles.end(); ++i)
511 if(i!=vehicles.begin())
512 st.push_back((DataFile::Statement("vehicle"), (*i)->get_type().get_article_number()));
514 for(unsigned i=0; i<=14; ++i)
515 if(real_speed[i].weight)
516 st.push_back((DataFile::Statement("real_speed"), i, real_speed[i].speed, real_speed[i].weight));
518 if(!cur_blocks.empty())
520 list<BlockRef> blocks = cur_blocks;
522 reverse_blocks(blocks);
524 Block *prev = blocks.front().block->get_endpoints()[blocks.front().entry].link;
525 st.push_back((DataFile::Statement("block_hint"), prev->get_id()));
527 for(list<BlockRef>::const_iterator i=blocks.begin(); i!=blocks.end(); ++i)
528 st.push_back((DataFile::Statement("block"), i->block->get_id()));
533 if(!route->is_temporary())
534 st.push_back((DataFile::Statement("route"), route->get_name()));
535 else if(next_route && !next_route->is_temporary())
536 st.push_back((DataFile::Statement("route"), next_route->get_name()));
541 DataFile::Statement ss("timetable");
542 timetable->save(ss.sub);
547 void Train::control_changed(const Controller::Control &ctrl)
549 signal_control_changed.emit(ctrl.name, ctrl.value);
552 void Train::loco_speed_event(unsigned addr, unsigned speed, bool)
556 current_speed = speed;
557 speed_changing = false;
562 void Train::loco_func_event(unsigned addr, unsigned func, bool state)
564 if(addr==address || (addr==address+1 && loco_type.get_max_function()>4))
569 functions |= 1<<func;
571 functions &= ~(1<<func);
573 signal_function_changed.emit(func, state);
577 void Train::sensor_event(unsigned addr, bool state)
581 // Find the first sensor block from our reserved blocks that isn't this sensor
582 list<BlockRef>::iterator i;
584 for(i=rsv_blocks.begin(); i!=rsv_blocks.end(); ++i)
585 if(i->block->get_sensor_id())
587 if(i->block->get_sensor_id()!=addr)
600 if(result==1 && i!=rsv_blocks.begin())
602 // Compute speed and update related state
603 float travel_time_secs = (Time::now()-last_entry_time)/Time::sec;
609 RealSpeed &rs = real_speed[current_speed];
610 rs.add(travel_dist/travel_time_secs, travel_time_secs);
612 set_status(format("Traveling %d kmh", get_travel_speed()));
617 for(list<BlockRef>::iterator j=rsv_blocks.begin(); j!=i; ++j)
619 j->block->traverse(j->entry, &block_len);
620 travel_dist += block_len;
622 if(j->block->get_sensor_id()==addr && !advancing)
624 const Block::Endpoint &bep = j->block->get_endpoints()[j->entry];
627 Track *track = bep.track->get_link(bep.track_ep);
628 unsigned ep = track->get_endpoint_by_link(*bep.track);
629 vehicles.back()->place(track, ep, 0, Vehicle::BACK_AXLE);
632 vehicles.front()->place(bep.track, bep.track_ep, 0, Vehicle::FRONT_AXLE);
635 last_entry_time = Time::now();
637 accurate_position = true;
640 // Check if we've reached the next route
643 const set<const Track *> &rtracks = next_route->get_tracks();
644 for(list<BlockRef>::iterator j=rsv_blocks.begin(); j!=i; ++j)
645 if(rtracks.count(j->block->get_endpoints()[j->entry].track))
650 signal_route_changed.emit(route);
655 // Move blocks up to the next sensor to our current blocks
656 cur_blocks.splice(cur_blocks.end(), rsv_blocks, rsv_blocks.begin(), i);
658 // Try to get more blocks if we're moving
663 layout.emergency("Sensor for "+name+" triggered out of order");
667 // Find the first sensor in our current blocks that's still active
668 list<BlockRef>::iterator end = cur_blocks.begin();
669 for(list<BlockRef>::iterator i=cur_blocks.begin(); i!=cur_blocks.end(); ++i)
670 if(i->block->get_sensor_id())
672 if(layout.get_driver().get_sensor(i->block->get_sensor_id()))
681 if(end!=cur_blocks.begin() && end!=cur_blocks.end())
682 // Free blocks up to the last inactive sensor
683 release_blocks(cur_blocks, cur_blocks.begin(), end);
687 void Train::turnout_event(unsigned addr, bool)
691 unsigned pending_addr = pending_block->get_turnout_id();
692 bool double_addr = (*pending_block->get_tracks().begin())->get_type().is_double_address();
693 if(addr==pending_addr || (double_addr && addr==pending_addr+1))
703 void Train::halt_event(bool h)
706 accurate_position = false;
709 void Train::block_reserved(const Block &block, const Train *train)
711 if(&block==pending_block && !train)
715 unsigned Train::reserve_more()
721 if(!rsv_blocks.empty())
722 start = &rsv_blocks.back();
723 else if(!cur_blocks.empty())
724 start = &cur_blocks.back();
730 // See how many sensor blocks we already have
732 for(list<BlockRef>::const_iterator i=rsv_blocks.begin(); i!=rsv_blocks.end(); ++i)
733 if(i->block->get_sensor_id())
739 const Route *cur_route = 0;
742 const set<Track *> &tracks = start->block->get_tracks();
743 for(set<Track *>::const_iterator i=tracks.begin(); (cur_route!=route && i!=tracks.end()); ++i)
745 if(route->get_tracks().count(*i))
747 else if(next_route && next_route->get_tracks().count(*i))
748 cur_route = next_route;
752 SetFlag setf(reserving);
754 BlockRef *last = start;
755 BlockRef *good = start;
756 unsigned good_sens = nsens;
757 Train *blocking_train = 0;
758 std::list<BlockRef> contested_blocks;
759 while(good_sens<3 || !contested_blocks.empty())
761 // Traverse to the next block
762 unsigned exit = last->block->traverse(last->entry, cur_route);
763 Block *link = last->block->get_link(exit);
767 int entry = link->get_endpoint_by_link(*last->block);
769 throw LogicError("Block links are inconsistent!");
771 const Block::Endpoint &entry_ep = link->get_endpoints()[entry];
775 if(cur_route!=next_route && next_route && next_route->get_tracks().count(entry_ep.track))
776 cur_route = next_route;
777 else if(!cur_route->get_tracks().count(entry_ep.track))
779 // Keep the blocks if we arrived at the end of the route
789 else if(route && route->get_tracks().count(entry_ep.track))
792 if(link->get_endpoints().size()<2)
804 if(link->get_train()!=blocking_train)
806 // XXX is it possible that this won't free all the blocks we want?
807 if(blocking_train->free_block(*contested_blocks.back().block))
809 // Roll back and start actually reserving the blocks
810 last = &rsv_blocks.back();
811 if(blocking_train->get_priority()==priority)
812 blocking_train->yield_to(*this);
818 pending_block = contested_blocks.front().block;
824 contested_blocks.push_back(BlockRef(link, entry));
825 last = &contested_blocks.back();
830 bool reserved = link->reserve(this);
833 /* We've found another train. If it wants to exit the block from the
834 same endpoint we're trying to enter from or the other way around,
835 treat it as coming towards us. Otherwise treat it as going in the
837 Train *other_train = link->get_train();
838 int other_entry = other_train->get_entry_to_block(*link);
840 throw LogicError("Block reservation inconsistency");
842 int other_prio = other_train->get_priority();
844 bool entry_conflict = (static_cast<unsigned>(entry)==link->traverse(other_entry));
845 bool exit_conflict = (link->traverse(entry)==static_cast<unsigned>(other_entry));
846 if(!entry_conflict && !exit_conflict)
848 /* Same direction, keep the blocks we got so far and wait for the
849 other train to pass */
853 // Ask a lesser priority train to free the block for us
854 if(other_train->get_priority()<priority)
855 if(other_train->free_block(*link))
856 reserved = link->reserve(this);
858 else if(other_prio<priority || (other_prio==priority && other_train!=yielding_to && entry_conflict))
860 /* A lesser priority train is coming at us, we must ask it to free
861 enough blocks to get clear of it to avoid a potential deadlock */
862 blocking_train = other_train;
863 contested_blocks.clear();
864 contested_blocks.push_back(BlockRef(link, entry));
865 last = &contested_blocks.back();
871 pending_block = link;
876 if(link->get_turnout_id())
878 const Endpoint &track_ep = entry_ep.track->get_type().get_endpoints()[entry_ep.track_ep];
880 // Keep the blocks reserved so far, as either us or the other train can diverge
884 // Figure out what path we'd like to take on the turnout
887 path = cur_route->get_turnout(link->get_turnout_id());
889 path = entry_ep.track->get_active_path();
890 if(!((track_ep.paths>>path)&1))
892 for(unsigned i=0; track_ep.paths>>i; ++i)
893 if((track_ep.paths>>i)&1)
897 if(path!=static_cast<int>(entry_ep.track->get_active_path()))
899 // The turnout is set to wrong path - switch and wait for it
900 pending_block = link;
901 entry_ep.track->set_active_path(path);
910 if(!contested_blocks.empty() && contested_blocks.front().block==link)
911 contested_blocks.pop_front();
913 rsv_blocks.push_back(BlockRef(link, entry));
914 last = &rsv_blocks.back();
915 if(last->block->get_sensor_id())
919 // Unreserve blocks that were not good
920 while(!rsv_blocks.empty() && &rsv_blocks.back()!=good)
922 rsv_blocks.back().block->reserve(0);
923 rsv_blocks.erase(--rsv_blocks.end());
926 if(!rsv_blocks.empty() && &rsv_blocks.back()!=start)
927 // We got some new blocks, so no longer need to yield
930 // Make any sensorless blocks at the beginning immediately current
931 list<BlockRef>::iterator i;
932 for(i=rsv_blocks.begin(); (i!=rsv_blocks.end() && !i->block->get_sensor_id()); ++i) ;
933 if(i!=rsv_blocks.begin())
934 cur_blocks.splice(cur_blocks.end(), rsv_blocks, rsv_blocks.begin(), i);
939 float Train::get_real_speed(unsigned i) const
941 if(real_speed[i].weight)
942 return real_speed[i].speed;
946 for(low=i; low>0; --low)
947 if(real_speed[low].weight)
949 for(high=i; high<14; ++high)
950 if(real_speed[high].weight)
953 if(real_speed[high].weight)
955 if(real_speed[low].weight)
957 float f = float(i-low)/(high-low);
958 return real_speed[low].speed*(1-f)+real_speed[high].speed*f;
961 return real_speed[high].speed*float(i)/high;
963 else if(real_speed[low].weight)
964 return real_speed[low].speed*float(i)/low;
969 unsigned Train::find_speed(float real) const
971 if(real<=real_speed[0].speed)
976 for(unsigned i=0; (!high && i<=14); ++i)
977 if(real_speed[i].weight)
979 if(real_speed[i].speed<real)
993 return min(static_cast<unsigned>(low*real/real_speed[low].speed), 14U);
996 float f = (real-real_speed[low].speed)/(real_speed[high].speed-real_speed[low].speed);
997 return static_cast<unsigned>(low*(1-f)+high*f+0.5);
1000 float Train::get_travel_speed() const
1002 float speed = get_real_speed(current_speed);
1003 float scale = layout.get_catalogue().get_scale();
1004 return static_cast<int>(round(speed/scale*3.6/5))*5;
1007 void Train::set_status(const string &s)
1010 signal_status_changed.emit(s);
1013 void Train::release_blocks(list<BlockRef> &blocks)
1015 release_blocks(blocks, blocks.begin(), blocks.end());
1018 void Train::release_blocks(list<BlockRef> &blocks, list<BlockRef>::iterator begin, list<BlockRef>::iterator end)
1022 Block *block = begin->block;
1023 blocks.erase(begin++);
1028 void Train::reverse_blocks(list<BlockRef> &blocks) const
1031 for(list<BlockRef>::iterator i=blocks.begin(); i!=blocks.end(); ++i)
1032 i->entry = i->block->traverse(i->entry);
1036 Train::BlockRef::BlockRef(Block *b, unsigned e):
1041 Train::BlockRef Train::BlockRef::next() const
1043 Block *blk = block->get_endpoints()[block->traverse(entry)].link;
1045 throw InvalidState("At end of line");
1047 int ep = blk->get_endpoint_by_link(*block);
1049 throw LogicError("Block links are inconsistent");
1051 return BlockRef(blk, ep);
1055 Train::RealSpeed::RealSpeed():
1060 void Train::RealSpeed::add(float s, float w)
1062 speed = (speed*weight+s*w)/(weight+w);
1063 weight = min(weight+w, 300.0f);
1067 Train::Loader::Loader(Train &t):
1068 DataFile::BasicLoader<Train>(t),
1072 add("block", &Loader::block);
1073 add("block_hint", &Loader::block_hint);
1074 add("name", &Loader::name);
1075 add("priority", &Train::priority);
1076 add("real_speed", &Loader::real_speed);
1077 add("route", &Loader::route);
1078 add("timetable", &Loader::timetable);
1079 add("vehicle", &Loader::vehicle);
1082 void Train::Loader::finish()
1084 if(!obj.cur_blocks.empty())
1086 const BlockRef &blkref = obj.cur_blocks.front();
1087 const Block::Endpoint &bep = blkref.block->get_endpoints()[blkref.entry];
1088 obj.vehicles.back()->place(bep.track, bep.track_ep, 0, Vehicle::BACK_BUFFER);
1090 obj.set_status("Stopped");
1094 void Train::Loader::block(unsigned id)
1102 blk = &obj.layout.get_block(id);
1104 catch(const KeyError &)
1106 blocks_valid = false;
1112 entry = blk->get_endpoint_by_link(*prev_block);
1117 obj.cur_blocks.push_back(BlockRef(blk, entry));
1119 if(blk->get_sensor_id())
1120 obj.layout.get_driver().set_sensor(blk->get_sensor_id(), true);
1125 void Train::Loader::block_hint(unsigned id)
1129 prev_block = &obj.layout.get_block(id);
1131 catch(const KeyError &)
1133 blocks_valid = false;
1137 void Train::Loader::name(const string &n)
1142 void Train::Loader::real_speed(unsigned i, float speed, float weight)
1144 obj.real_speed[i].speed = speed;
1145 obj.real_speed[i].weight = weight;
1148 void Train::Loader::route(const string &n)
1150 obj.set_route(&obj.layout.get_route(n));
1153 void Train::Loader::timetable()
1156 throw InvalidState("A timetable has already been loaded");
1158 obj.timetable = new Timetable(obj);
1159 load_sub(*obj.timetable);
1162 void Train::Loader::vehicle(unsigned n)
1164 const VehicleType &vtype = obj.layout.get_catalogue().get_vehicle(n);
1165 Vehicle *veh = new Vehicle(obj.layout, vtype);
1166 obj.vehicles.back()->attach_back(*veh);
1167 obj.vehicles.push_back(veh);
1170 } // namespace Marklin