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)
302 float margin = 10*layout.get_catalogue().get_scale();
303 if(get_reserved_distance_until(&block, false)<controller->get_braking_distance()*1.3+margin)
307 for(list<BlockRef>::iterator i=rsv_blocks.begin(); i!=rsv_blocks.end(); ++i)
313 release_blocks(rsv_blocks, i, rsv_blocks.end());
316 else if(i->block->get_sensor_id())
323 int Train::get_entry_to_block(Block &block) const
325 for(list<BlockRef>::const_iterator i=cur_blocks.begin(); i!=cur_blocks.end(); ++i)
328 for(list<BlockRef>::const_iterator i=rsv_blocks.begin(); i!=rsv_blocks.end(); ++i)
334 float Train::get_reserved_distance() const
336 return get_reserved_distance_until(0, false);
339 void Train::tick(const Time::TimeStamp &t, const Time::TimeDelta &dt)
341 if(!active && stop_timeout && t>=stop_timeout)
343 release_blocks(rsv_blocks);
344 end_of_route = false;
345 stop_timeout = Time::TimeStamp();
348 Driver &driver = layout.get_driver();
352 controller->tick(dt);
353 float speed = controller->get_speed();
354 unsigned speed_notch = find_speed(speed);
356 if(controller->get_reverse()!=reverse)
358 reverse = controller->get_reverse();
359 driver.set_loco_reverse(address, reverse);
361 release_blocks(rsv_blocks);
362 reverse_blocks(cur_blocks);
366 if(speed_notch!=current_speed && !speed_changing && !driver.is_halted() && driver.get_power())
368 speed_changing = true;
369 driver.set_loco_speed(address, speed_notch);
374 set_status(format("Traveling %d kmh", get_travel_speed()));
376 set_status("Waiting");
384 Vehicle &vehicle = *(reverse ? vehicles.back() : vehicles.front());
385 Track *track = vehicle.get_track();
388 for(list<BlockRef>::const_iterator i=cur_blocks.begin(); (!ok && i!=cur_blocks.end()); ++i)
389 ok = i->block->get_tracks().count(track);
391 float d = get_real_speed(current_speed)*(dt/Time::sec);
394 SetFlag setf(advancing);
395 vehicle.advance(reverse ? -d : d);
397 else if(accurate_position)
400 if(overshoot_dist>40*layout.get_catalogue().get_scale())
402 layout.emergency(name+" has not arrived at sensor");
403 accurate_position = false;
407 else if(end_of_route && rsv_blocks.empty())
409 signal_arrived.emit();
413 if(!cur_blocks.empty() && !cur_blocks.front().block->get_sensor_id())
415 float dist = get_reserved_distance_until(cur_blocks.front().block, true);
417 if(dist>10*layout.get_catalogue().get_scale())
419 cur_blocks.front().block->reserve(0);
420 cur_blocks.erase(cur_blocks.begin());
425 void Train::save(list<DataFile::Statement> &st) const
427 st.push_back((DataFile::Statement("name"), name));
429 st.push_back((DataFile::Statement("priority"), priority));
431 for(vector<Vehicle *>::const_iterator i=vehicles.begin(); i!=vehicles.end(); ++i)
432 if(i!=vehicles.begin())
433 st.push_back((DataFile::Statement("vehicle"), (*i)->get_type().get_article_number()));
435 for(unsigned i=0; i<=14; ++i)
436 if(real_speed[i].weight)
437 st.push_back((DataFile::Statement("real_speed"), i, real_speed[i].speed, real_speed[i].weight));
439 if(!cur_blocks.empty())
441 list<BlockRef> blocks = cur_blocks;
443 reverse_blocks(blocks);
445 Block *prev = blocks.front().block->get_endpoints()[blocks.front().entry].link;
446 st.push_back((DataFile::Statement("block_hint"), prev->get_id()));
448 for(list<BlockRef>::const_iterator i=blocks.begin(); i!=blocks.end(); ++i)
449 st.push_back((DataFile::Statement("block"), i->block->get_id()));
454 if(!route->is_temporary())
455 st.push_back((DataFile::Statement("route"), route->get_name()));
456 else if(next_route && !next_route->is_temporary())
457 st.push_back((DataFile::Statement("route"), next_route->get_name()));
462 DataFile::Statement ss("timetable");
463 timetable->save(ss.sub);
468 void Train::control_changed(const Controller::Control &ctrl)
470 signal_control_changed.emit(ctrl.name, ctrl.value);
473 void Train::loco_speed_event(unsigned addr, unsigned speed, bool)
477 current_speed = speed;
478 speed_changing = false;
483 void Train::loco_func_event(unsigned addr, unsigned func, bool state)
485 if(addr==address || (addr==address+1 && loco_type.get_max_function()>4))
490 functions |= 1<<func;
492 functions &= ~(1<<func);
494 signal_function_changed.emit(func, state);
498 void Train::sensor_event(unsigned addr, bool state)
502 // Find the first sensor block from our reserved blocks that isn't this sensor
503 list<BlockRef>::iterator i;
505 for(i=rsv_blocks.begin(); i!=rsv_blocks.end(); ++i)
506 if(i->block->get_sensor_id())
508 if(i->block->get_sensor_id()!=addr)
521 if(result==1 && i!=rsv_blocks.begin())
523 // Compute speed and update related state
524 float travel_time_secs = (Time::now()-last_entry_time)/Time::sec;
530 RealSpeed &rs = real_speed[current_speed];
531 rs.add(travel_dist/travel_time_secs, travel_time_secs);
533 set_status(format("Traveling %d kmh", get_travel_speed()));
538 for(list<BlockRef>::iterator j=rsv_blocks.begin(); j!=i; ++j)
540 j->block->traverse(j->entry, &block_len);
541 travel_dist += block_len;
543 if(j->block->get_sensor_id()==addr && !advancing)
545 const Block::Endpoint &bep = j->block->get_endpoints()[j->entry];
548 Track *track = bep.track->get_link(bep.track_ep);
549 unsigned ep = track->get_endpoint_by_link(*bep.track);
550 vehicles.back()->place(track, ep, 0, Vehicle::BACK_AXLE);
553 vehicles.front()->place(bep.track, bep.track_ep, 0, Vehicle::FRONT_AXLE);
556 last_entry_time = Time::now();
558 accurate_position = true;
561 // Check if we've reached the next route
564 const set<const Track *> &rtracks = next_route->get_tracks();
565 for(list<BlockRef>::iterator j=rsv_blocks.begin(); j!=i; ++j)
566 if(rtracks.count(j->block->get_endpoints()[j->entry].track))
571 signal_route_changed.emit(route);
576 // Move blocks up to the next sensor to our current blocks
577 cur_blocks.splice(cur_blocks.end(), rsv_blocks, rsv_blocks.begin(), i);
579 // Try to get more blocks if we're moving
584 layout.emergency("Sensor for "+name+" triggered out of order");
588 // Find the first sensor in our current blocks that's still active
589 list<BlockRef>::iterator end = cur_blocks.begin();
590 for(list<BlockRef>::iterator i=cur_blocks.begin(); i!=cur_blocks.end(); ++i)
591 if(i->block->get_sensor_id())
593 if(layout.get_driver().get_sensor(i->block->get_sensor_id()))
602 if(end!=cur_blocks.begin() && end!=cur_blocks.end())
603 // Free blocks up to the last inactive sensor
604 release_blocks(cur_blocks, cur_blocks.begin(), end);
608 void Train::turnout_event(unsigned addr, bool)
612 unsigned pending_addr = pending_block->get_turnout_id();
613 bool double_addr = (*pending_block->get_tracks().begin())->get_type().is_double_address();
614 if(addr==pending_addr || (double_addr && addr==pending_addr+1))
624 void Train::halt_event(bool h)
627 accurate_position = false;
630 void Train::block_reserved(const Block &block, const Train *train)
632 if(&block==pending_block && !train)
636 unsigned Train::reserve_more()
642 if(!rsv_blocks.empty())
643 start = &rsv_blocks.back();
644 else if(!cur_blocks.empty())
645 start = &cur_blocks.back();
651 // See how many sensor blocks and how much track we already have
654 for(list<BlockRef>::const_iterator i=rsv_blocks.begin(); i!=rsv_blocks.end(); ++i)
656 if(i->block->get_sensor_id())
661 i->block->traverse(i->entry, &length);
669 const Route *cur_route = 0;
672 const set<Track *> &tracks = start->block->get_tracks();
673 for(set<Track *>::const_iterator i=tracks.begin(); (cur_route!=route && i!=tracks.end()); ++i)
675 if(route->get_tracks().count(*i))
677 else if(next_route && next_route->get_tracks().count(*i))
678 cur_route = next_route;
682 float approach_margin = 50*layout.get_catalogue().get_scale();
683 float min_dist = controller->get_braking_distance()*1.3+approach_margin*2;
685 BlockRef *last = start;
686 BlockRef *good = start;
687 unsigned good_sens = nsens;
688 float good_dist = dist;
689 Train *blocking_train = 0;
690 std::list<BlockRef> contested_blocks;
692 SetFlag setf(reserving);
694 while(good_sens<3 || good_dist<min_dist || !contested_blocks.empty())
696 // Traverse to the next block
698 unsigned exit = last->block->traverse(last->entry, cur_route, &length);
699 Block *link = last->block->get_link(exit);
703 int entry = link->get_endpoint_by_link(*last->block);
705 throw LogicError("Block links are inconsistent!");
707 const Block::Endpoint &entry_ep = link->get_endpoints()[entry];
711 if(cur_route!=next_route && next_route && next_route->get_tracks().count(entry_ep.track))
712 cur_route = next_route;
713 else if(!cur_route->get_tracks().count(entry_ep.track))
715 // Keep the blocks if we arrived at the end of the route
726 else if(route && route->get_tracks().count(entry_ep.track))
729 if(link->get_endpoints().size()<2)
742 if(link->get_train()!=blocking_train)
744 // XXX is it possible that this won't free all the blocks we want?
745 if(blocking_train->free_block(*contested_blocks.back().block))
747 // Roll back and start actually reserving the blocks
748 last = &rsv_blocks.back();
749 if(blocking_train->get_priority()==priority)
750 blocking_train->yield_to(*this);
756 pending_block = contested_blocks.front().block;
762 contested_blocks.push_back(BlockRef(link, entry));
763 last = &contested_blocks.back();
768 bool reserved = link->reserve(this);
771 /* We've found another train. If it wants to exit the block from the
772 same endpoint we're trying to enter from or the other way around,
773 treat it as coming towards us. Otherwise treat it as going in the
775 Train *other_train = link->get_train();
776 int other_entry = other_train->get_entry_to_block(*link);
778 throw LogicError("Block reservation inconsistency");
780 int other_prio = other_train->get_priority();
782 bool entry_conflict = (static_cast<unsigned>(entry)==link->traverse(other_entry));
783 bool exit_conflict = (link->traverse(entry)==static_cast<unsigned>(other_entry));
784 if(!entry_conflict && !exit_conflict)
786 /* Same direction, keep the blocks we got so far and wait for the
787 other train to pass */
792 // Ask a lesser priority train to free the block for us
793 if(other_train->get_priority()<priority)
794 if(other_train->free_block(*link))
795 reserved = link->reserve(this);
797 else if(other_prio<priority || (other_prio==priority && other_train!=yielding_to && entry_conflict))
799 /* A lesser priority train is coming at us, we must ask it to free
800 enough blocks to get clear of it to avoid a potential deadlock */
801 blocking_train = other_train;
802 contested_blocks.clear();
803 contested_blocks.push_back(BlockRef(link, entry));
804 last = &contested_blocks.back();
810 pending_block = link;
815 if(link->get_turnout_id())
817 const Endpoint &track_ep = entry_ep.track->get_type().get_endpoints()[entry_ep.track_ep];
819 // Keep the blocks reserved so far, as either us or the other train can diverge
824 // Figure out what path we'd like to take on the turnout
827 path = cur_route->get_turnout(link->get_turnout_id());
829 path = entry_ep.track->get_active_path();
830 if(!((track_ep.paths>>path)&1))
832 for(unsigned i=0; track_ep.paths>>i; ++i)
833 if((track_ep.paths>>i)&1)
837 if(path!=static_cast<int>(entry_ep.track->get_active_path()))
839 // The turnout is set to wrong path - switch and wait for it
840 pending_block = link;
841 entry_ep.track->set_active_path(path);
850 if(!contested_blocks.empty() && contested_blocks.front().block==link)
851 contested_blocks.pop_front();
853 rsv_blocks.push_back(BlockRef(link, entry));
854 last = &rsv_blocks.back();
855 if(last->block->get_sensor_id())
861 // Unreserve blocks that were not good
862 while(!rsv_blocks.empty() && &rsv_blocks.back()!=good)
864 rsv_blocks.back().block->reserve(0);
865 rsv_blocks.erase(--rsv_blocks.end());
868 if(!rsv_blocks.empty() && &rsv_blocks.back()!=start)
869 // We got some new blocks, so no longer need to yield
872 // Make any sensorless blocks at the beginning immediately current
873 list<BlockRef>::iterator i;
874 for(i=rsv_blocks.begin(); (i!=rsv_blocks.end() && !i->block->get_sensor_id()); ++i) ;
875 if(i!=rsv_blocks.begin())
876 cur_blocks.splice(cur_blocks.end(), rsv_blocks, rsv_blocks.begin(), i);
881 float Train::get_reserved_distance_until(const Block *until_block, bool back) const
883 if(cur_blocks.empty())
886 Vehicle &veh = *(reverse!=back ? vehicles.back() : vehicles.front());
887 const VehicleType &vtype = veh.get_type();
889 Track *track = veh.get_track();
893 list<BlockRef>::const_iterator block = cur_blocks.begin();
894 while(block!=cur_blocks.end() && !block->block->get_tracks().count(track))
896 if(block==cur_blocks.end() || block->block==until_block)
899 unsigned entry = veh.get_entry();
901 float result = veh.get_offset();
903 entry = track->traverse(entry);
905 result = track->get_type().get_path_length(track->get_active_path())-result;
906 result -= vtype.get_length()/2;
910 if(track->get_type().get_endpoints().size()<2)
913 Track *next = track->get_link(track->traverse(entry));
915 if(!block->block->get_tracks().count(next))
919 if(block==cur_blocks.begin())
926 if(block==cur_blocks.end())
927 block = rsv_blocks.begin();
928 if(block==rsv_blocks.end())
932 if(block->block==until_block)
936 entry = next->get_endpoint_by_link(*track);
939 result += track->get_type().get_path_length(track->get_active_path());
945 float Train::get_real_speed(unsigned i) const
947 if(real_speed[i].weight)
948 return real_speed[i].speed;
952 for(low=i; low>0; --low)
953 if(real_speed[low].weight)
955 for(high=i; high<14; ++high)
956 if(real_speed[high].weight)
959 if(real_speed[high].weight)
961 if(real_speed[low].weight)
963 float f = float(i-low)/(high-low);
964 return real_speed[low].speed*(1-f)+real_speed[high].speed*f;
967 return real_speed[high].speed*float(i)/high;
969 else if(real_speed[low].weight)
970 return real_speed[low].speed*float(i)/low;
975 unsigned Train::find_speed(float real) const
977 if(real<=real_speed[0].speed)
982 for(unsigned i=0; (!high && i<=14); ++i)
983 if(real_speed[i].weight)
985 if(real_speed[i].speed<real)
999 return min(static_cast<unsigned>(low*real/real_speed[low].speed), 14U);
1002 float f = (real-real_speed[low].speed)/(real_speed[high].speed-real_speed[low].speed);
1003 return static_cast<unsigned>(low*(1-f)+high*f+0.5);
1006 float Train::get_travel_speed() const
1008 float speed = get_real_speed(current_speed);
1009 float scale = layout.get_catalogue().get_scale();
1010 return static_cast<int>(round(speed/scale*3.6/5))*5;
1013 void Train::set_status(const string &s)
1016 signal_status_changed.emit(s);
1019 void Train::release_blocks(list<BlockRef> &blocks)
1021 release_blocks(blocks, blocks.begin(), blocks.end());
1024 void Train::release_blocks(list<BlockRef> &blocks, list<BlockRef>::iterator begin, list<BlockRef>::iterator end)
1028 Block *block = begin->block;
1029 blocks.erase(begin++);
1034 void Train::reverse_blocks(list<BlockRef> &blocks) const
1037 for(list<BlockRef>::iterator i=blocks.begin(); i!=blocks.end(); ++i)
1038 i->entry = i->block->traverse(i->entry);
1042 Train::BlockRef::BlockRef(Block *b, unsigned e):
1047 Train::BlockRef Train::BlockRef::next() const
1049 Block *blk = block->get_endpoints()[block->traverse(entry)].link;
1051 throw InvalidState("At end of line");
1053 int ep = blk->get_endpoint_by_link(*block);
1055 throw LogicError("Block links are inconsistent");
1057 return BlockRef(blk, ep);
1061 Train::RealSpeed::RealSpeed():
1066 void Train::RealSpeed::add(float s, float w)
1068 speed = (speed*weight+s*w)/(weight+w);
1069 weight = min(weight+w, 300.0f);
1073 Train::Loader::Loader(Train &t):
1074 DataFile::BasicLoader<Train>(t),
1078 add("block", &Loader::block);
1079 add("block_hint", &Loader::block_hint);
1080 add("name", &Loader::name);
1081 add("priority", &Train::priority);
1082 add("real_speed", &Loader::real_speed);
1083 add("route", &Loader::route);
1084 add("timetable", &Loader::timetable);
1085 add("vehicle", &Loader::vehicle);
1088 void Train::Loader::finish()
1090 if(!obj.cur_blocks.empty())
1092 const BlockRef &blkref = obj.cur_blocks.front();
1093 const Block::Endpoint &bep = blkref.block->get_endpoints()[blkref.entry];
1094 obj.vehicles.back()->place(bep.track, bep.track_ep, 0, Vehicle::BACK_BUFFER);
1096 obj.set_status("Stopped");
1100 void Train::Loader::block(unsigned id)
1108 blk = &obj.layout.get_block(id);
1110 catch(const KeyError &)
1112 blocks_valid = false;
1118 entry = blk->get_endpoint_by_link(*prev_block);
1123 obj.cur_blocks.push_back(BlockRef(blk, entry));
1125 if(blk->get_sensor_id())
1126 obj.layout.get_driver().set_sensor(blk->get_sensor_id(), true);
1131 void Train::Loader::block_hint(unsigned id)
1135 prev_block = &obj.layout.get_block(id);
1137 catch(const KeyError &)
1139 blocks_valid = false;
1143 void Train::Loader::name(const string &n)
1148 void Train::Loader::real_speed(unsigned i, float speed, float weight)
1150 obj.real_speed[i].speed = speed;
1151 obj.real_speed[i].weight = weight;
1154 void Train::Loader::route(const string &n)
1156 obj.set_route(&obj.layout.get_route(n));
1159 void Train::Loader::timetable()
1162 throw InvalidState("A timetable has already been loaded");
1164 obj.timetable = new Timetable(obj);
1165 load_sub(*obj.timetable);
1168 void Train::Loader::vehicle(unsigned n)
1170 const VehicleType &vtype = obj.layout.get_catalogue().get_vehicle(n);
1171 Vehicle *veh = new Vehicle(obj.layout, vtype);
1172 obj.vehicles.back()->attach_back(*veh);
1173 obj.vehicles.push_back(veh);
1176 } // namespace Marklin