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 "trackiter.h"
20 #include "tracktype.h"
23 #include "vehicletype.h"
34 SetFlag(bool &f): flag(f) { flag = true; }
35 ~SetFlag() { flag = false; }
43 Train::Train(Layout &l, const VehicleType &t, unsigned a):
49 cur_blocks_end(blocks.end()),
53 controller(new AIControl(*this, new SimpleController)),
57 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 bool Train::set_route(const Route *r)
210 free_noncritical_blocks();
213 if(r && !blocks.empty())
215 TrackIter first = blocks.front().track_iter();
216 TrackIter next = blocks.back().next().track_iter();
217 if(!r->has_track(*next))
219 lead = Route::find(next, *r);
222 create_lead_route(lead, lead);
223 routes.push_front(lead);
225 else if(!r->has_track(*first))
226 lead = create_lead_route(0, r);
231 routes.push_back(lead);
234 end_of_route = false;
238 signal_route_changed.emit(get_route());
243 bool Train::go_to(Track &to)
245 for(BlockList::const_iterator i=blocks.begin(); i!=cur_blocks_end; ++i)
246 if((*i)->has_track(to))
248 signal_arrived.emit();
252 free_noncritical_blocks();
254 TrackIter next = blocks.back().next().track_iter();
256 Route *route = Route::find(next, to);
259 create_lead_route(route, route);
260 return set_route(route);
263 bool Train::divert(Track &from)
265 if(!from.get_turnout_id())
266 throw InvalidParameterValue("Can't divert from a non-turnout");
272 list<RouteRef>::iterator route = routes.begin();
274 // Follow our routes to find out where we're entering the turnout
275 for(TrackLoopIter track = blocks.front().track_iter();;)
277 if(!advance_route(route, *track))
282 Block &block = track->get_block();
283 if(block.get_train()==this && !free_block(block))
286 int route_path = route->route->get_turnout(from.get_turnout_id());
288 // Check that more than one path is available
289 unsigned ep_paths = track->get_type().get_endpoints()[track.entry()].paths;
290 if(!(ep_paths&(ep_paths-1)))
293 // Choose some other path
294 for(int i=0; ep_paths>>i; ++i)
295 if((ep_paths&(1<<i)) && i!=route_path)
301 entry = track.entry();
305 track = track.next(route->route->get_path(*track));
307 if(!track || track.looped())
311 TrackIter track = TrackIter(&from, entry).next(path);
316 for(list<RouteRef>::iterator i=routes.begin(); i!=routes.end(); ++i)
317 tracks.insert(i->route->get_tracks().begin(), i->route->get_tracks().end());
318 RefPtr<Route> diversion = Route::find(track, tracks);
322 diversion->set_name("Diversion");
323 diversion->add_track(from);
324 diversion->set_turnout(from.get_turnout_id(), path);
326 if(!is_valid_diversion(*diversion, TrackIter(&from, entry)))
329 // Follow the diversion route until we get back to the original route
330 list<RouteRef>::iterator end = routes.end();
333 for(list<RouteRef>::iterator i=route; (end==routes.end() && i!=routes.end()); ++i)
334 if(i->route->has_track(*track))
337 if(end!=routes.end())
339 else if(!diversion->has_track(*track))
340 throw LogicError("Pathfinder returned a bad route");
342 track = track.next(diversion->get_path(*track));
346 // We are rejoining the same route we diverted from, duplicate it
347 routes.insert(end, *route);
351 routes.erase(route, end);
353 routes.insert(end, RouteRef(diversion.release(), from.get_turnout_id()));
358 const Route *Train::get_route() const
362 return routes.front().route;
365 void Train::place(Block &block, unsigned entry)
367 if(controller->get_speed())
368 throw InvalidState("Must be stopped before placing");
373 accurate_position = false;
375 if(!block.reserve(this))
377 set_status("Unplaced");
381 blocks.push_back(BlockIter(&block, entry));
384 TrackIter track = BlockIter(&block, entry).reverse().track_iter();
385 vehicles.front()->place(*track, track.entry(), 0, Vehicle::FRONT_BUFFER);
389 const Block::Endpoint &bep = block.get_endpoints()[entry];
390 vehicles.back()->place(*bep.track, bep.track_ep, 0, Vehicle::BACK_BUFFER);
394 void Train::unplace()
396 if(controller->get_speed())
397 throw InvalidState("Must be stopped before unplacing");
402 accurate_position = false;
404 for(vector<Vehicle *>::iterator i=vehicles.begin(); i!=vehicles.end(); ++i)
407 set_status("Unplaced");
410 bool Train::free_block(Block &block)
412 float margin = 10*layout.get_catalogue().get_scale();
413 if(get_reserved_distance_until(&block, false)<controller->get_braking_distance()*1.3+margin)
417 for(BlockList::iterator i=cur_blocks_end; i!=blocks.end(); ++i)
419 if(i->block()==&block)
423 release_blocks(i, blocks.end());
426 else if((*i)->get_sensor_id())
433 void Train::free_noncritical_blocks()
438 if(controller->get_speed()==0)
440 release_blocks(cur_blocks_end, blocks.end());
444 float margin = 10*layout.get_catalogue().get_scale();
445 float min_dist = controller->get_braking_distance()*1.3+margin;
447 Vehicle &veh = *(reverse ? vehicles.back() : vehicles.front());
449 TrackIter track(veh.get_track(), veh.get_entry());
450 BlockList::iterator block = blocks.begin();
452 while(block!=blocks.end() && !(*block)->has_track(*track))
455 if(block==cur_blocks_end)
459 float dist = veh.get_offset();
463 dist = track->get_type().get_path_length(track->get_active_path())-dist;
464 dist -= veh.get_type().get_length()/2;
469 track = track.next();
471 if(!(*block)->has_track(*track))
474 if(block==cur_blocks_end)
476 if(block==blocks.end())
479 if(dist>min_dist && nsens>0)
481 release_blocks(block, blocks.end());
485 if(in_rsv && (*block)->get_sensor_id())
489 dist += track->get_type().get_path_length(track->get_active_path());
493 int Train::get_entry_to_block(Block &block) const
495 for(BlockList::const_iterator i=blocks.begin(); i!=blocks.end(); ++i)
496 if(i->block()==&block)
501 float Train::get_reserved_distance() const
503 return get_reserved_distance_until(0, false);
506 void Train::tick(const Time::TimeStamp &t, const Time::TimeDelta &dt)
508 if(!active && stop_timeout && t>=stop_timeout)
510 release_blocks(cur_blocks_end, blocks.end());
511 end_of_route = false;
512 stop_timeout = Time::TimeStamp();
515 Driver &driver = layout.get_driver();
519 controller->tick(dt);
520 float speed = controller->get_speed();
521 unsigned speed_notch = find_speed(speed);
523 if(controller->get_reverse()!=reverse)
525 reverse = controller->get_reverse();
526 driver.set_loco_reverse(address, reverse);
528 release_blocks(cur_blocks_end, blocks.end());
529 reverse_blocks(blocks);
533 if(speed_notch!=current_speed && !speed_changing && !driver.is_halted() && driver.get_power())
535 speed_changing = true;
536 driver.set_loco_speed(address, speed_notch);
541 set_status(format("Traveling %d kmh", get_travel_speed()));
543 set_status("Waiting");
551 Vehicle &vehicle = *(reverse ? vehicles.back() : vehicles.front());
552 Track *track = vehicle.get_track();
555 for(BlockList::const_iterator i=blocks.begin(); (!ok && i!=cur_blocks_end); ++i)
556 ok = (*i)->has_track(*track);
558 float d = get_real_speed(current_speed)*(dt/Time::sec);
561 SetFlag setf(advancing);
562 vehicle.advance(reverse ? -d : d);
564 else if(accurate_position)
567 if(overshoot_dist>40*layout.get_catalogue().get_scale())
569 layout.emergency(name+" has not arrived at sensor");
570 accurate_position = false;
574 else if(end_of_route && cur_blocks_end==blocks.end())
577 signal_arrived.emit();
581 if(!blocks.empty() && !blocks.front()->get_sensor_id())
583 float dist = get_reserved_distance_until(&*blocks.front(), true);
585 if(dist>10*layout.get_catalogue().get_scale())
587 blocks.front()->reserve(0);
593 void Train::save(list<DataFile::Statement> &st) const
595 st.push_back((DataFile::Statement("name"), name));
597 st.push_back((DataFile::Statement("priority"), priority));
599 for(vector<Vehicle *>::const_iterator i=vehicles.begin(); i!=vehicles.end(); ++i)
600 if(i!=vehicles.begin())
601 st.push_back((DataFile::Statement("vehicle"), (*i)->get_type().get_article_number()));
603 for(unsigned i=0; i<=14; ++i)
604 if(real_speed[i].weight)
605 st.push_back((DataFile::Statement("real_speed"), i, real_speed[i].speed, real_speed[i].weight));
607 if(!blocks.empty() && cur_blocks_end!=blocks.begin())
609 BlockList blks(blocks.begin(), BlockList::const_iterator(cur_blocks_end));
611 reverse_blocks(blks);
613 BlockIter prev = blks.front().flip();
614 st.push_back((DataFile::Statement("block_hint"), prev->get_id()));
616 for(BlockList::const_iterator i=blks.begin(); i!=blks.end(); ++i)
617 st.push_back((DataFile::Statement("block"), (*i)->get_id()));
622 list<RouteRef>::const_iterator i = routes.begin();
623 for(; (i!=routes.end() && i->route->is_temporary()); ++i) ;
625 st.push_back((DataFile::Statement("route"), i->route->get_name()));
630 DataFile::Statement ss("timetable");
631 timetable->save(ss.sub);
636 void Train::control_changed(const Controller::Control &ctrl)
638 signal_control_changed.emit(ctrl.name, ctrl.value);
641 void Train::loco_speed_event(unsigned addr, unsigned speed, bool)
645 current_speed = speed;
646 speed_changing = false;
651 void Train::loco_func_event(unsigned addr, unsigned func, bool state)
653 if(addr==address || (addr==address+1 && loco_type.get_max_function()>4))
658 functions |= 1<<func;
660 functions &= ~(1<<func);
662 signal_function_changed.emit(func, state);
666 void Train::sensor_event(unsigned addr, bool state)
670 // Find the first sensor block from our reserved blocks that isn't this sensor
671 BlockList::iterator end;
673 for(end=cur_blocks_end; end!=blocks.end(); ++end)
674 if((*end)->get_sensor_id())
676 if((*end)->get_sensor_id()!=addr)
691 // Compute speed and update related state
692 float travel_time_secs = (Time::now()-last_entry_time)/Time::sec;
698 RealSpeed &rs = real_speed[current_speed];
699 rs.add(travel_dist/travel_time_secs, travel_time_secs);
701 set_status(format("Traveling %d kmh", get_travel_speed()));
705 for(BlockList::iterator j=cur_blocks_end; j!=end; ++j)
707 travel_dist += (*j)->get_path_length(j->entry());
709 if((*j)->get_sensor_id()==addr && !advancing)
711 TrackIter track = j->track_iter();
714 track = track.flip();
715 vehicles.back()->place(*track, track.entry(), 0, Vehicle::BACK_AXLE);
718 vehicles.front()->place(*track, track.entry(), 0, Vehicle::FRONT_AXLE);
721 last_entry_time = Time::now();
723 accurate_position = true;
726 // Check if we've reached the next route
729 const set<Track *> &rtracks = (++routes.begin())->route->get_tracks();
730 for(BlockList::iterator j=cur_blocks_end; j!=end; ++j)
731 if(rtracks.count((*j)->get_endpoints()[j->entry()].track))
735 signal_route_changed.emit(routes.front().route);
740 // Move blocks up to the next sensor to our current blocks
741 cur_blocks_end = end;
743 // Try to get more blocks if we're moving
748 layout.emergency("Sensor for "+name+" triggered out of order");
752 const Vehicle &veh = *(reverse ? vehicles.front() : vehicles.back());
754 // Find the first sensor in our current blocks that's still active
755 BlockList::iterator end = blocks.begin();
756 for(BlockList::iterator i=blocks.begin(); i!=cur_blocks_end; ++i)
758 if((*i)->has_track(*veh.get_track()))
760 if((*i)->get_sensor_id())
762 if(layout.get_driver().get_sensor((*i)->get_sensor_id()))
772 if(end!=blocks.begin() && end!=cur_blocks_end)
773 // Free blocks up to the last inactive sensor
774 release_blocks(blocks.begin(), end);
778 void Train::turnout_event(unsigned addr, bool)
780 if(pending_block && (!pending_block->get_train() || pending_block->get_train()==this))
782 unsigned pending_addr = pending_block->get_turnout_id();
783 bool double_addr = (*pending_block->get_tracks().begin())->get_type().is_double_address();
784 if(addr==pending_addr || (double_addr && addr==pending_addr+1))
794 void Train::halt_event(bool h)
797 accurate_position = false;
800 void Train::block_reserved(const Block &block, const Train *train)
802 if(&block==pending_block && !train && !reserving)
806 unsigned Train::reserve_more()
808 if(!active || blocks.empty())
811 BlockIter start = blocks.back();
815 // See how many sensor blocks and how much track we already have
818 for(BlockList::const_iterator i=cur_blocks_end; i!=blocks.end(); ++i)
820 if((*i)->get_sensor_id())
823 dist += (*i)->get_path_length(i->entry());
829 list<RouteRef>::iterator cur_route = routes.begin();
830 advance_route(cur_route, *start.track_iter());
832 float approach_margin = 50*layout.get_catalogue().get_scale();
833 float min_dist = controller->get_braking_distance()*1.3+approach_margin*2;
835 BlockIter block = start;
836 BlockIter good = start;
837 Track *divert_track = 0;
838 bool try_divert = false;
839 unsigned good_sens = nsens;
840 float good_dist = dist;
841 Train *blocking_train = 0;
842 BlockList contested_blocks;
844 SetFlag setf(reserving);
846 while(good_sens<3 || good_dist<min_dist || !contested_blocks.empty())
848 BlockIter last = block;
849 block = block.next(cur_route!=routes.end() ? cur_route->route : 0);
853 TrackIter track = block.track_iter();
855 if(cur_route!=routes.end())
857 if(!advance_route(cur_route, *track))
859 // Keep the blocks if we arrived at the end of the route
870 else if(!routes.empty() && routes.front().route->has_track(*track))
871 cur_route = routes.begin();
873 if(block->get_endpoints().size()<2)
886 if(block->get_train()!=blocking_train)
888 if(blocking_train->free_block(*contested_blocks.back()))
890 // Roll back and start actually reserving the blocks
891 block = blocks.back();
892 cur_route = routes.begin();
893 advance_route(cur_route, *block.track_iter().track());
894 if(blocking_train->get_priority()==priority)
895 blocking_train->yield_to(*this);
901 yield_to(*blocking_train);
902 pending_block = contested_blocks.front().block();
903 try_divert = divert_track;
909 contested_blocks.push_back(block);
914 bool reserved = block->reserve(this);
917 /* We've found another train. If it wants to exit the block from the
918 same endpoint we're trying to enter from or the other way around,
919 treat it as coming towards us. Otherwise treat it as going in the
921 Train *other_train = block->get_train();
922 int other_entry = other_train->get_entry_to_block(*block);
924 throw LogicError("Block reservation inconsistency");
926 unsigned exit = block.reverse().entry();
927 unsigned other_exit = BlockIter(block.block(), other_entry).reverse().entry();
928 bool entry_conflict = (block.entry()==other_exit);
929 bool exit_conflict = (exit==static_cast<unsigned>(other_entry));
930 if(!entry_conflict && !last->get_turnout_id())
932 /* The other train is not coming to the blocks we're holding, so we
939 int other_prio = other_train->get_priority();
941 if(!entry_conflict && !exit_conflict && other_prio<priority)
943 /* Ask a lesser priority train going to the same direction to free
945 if(other_train->free_block(*block))
946 reserved = block->reserve(this);
948 else if(other_train!=yielding_to && (other_prio<priority || (other_prio==priority && entry_conflict)))
950 /* A lesser priority train is coming at us, we must ask it to free
951 enough blocks to get clear of it to avoid a potential deadlock */
952 blocking_train = other_train;
953 contested_blocks.clear();
954 contested_blocks.push_back(block);
957 else if(divert_track && (entry_conflict || exit_conflict))
958 // We are blocked, but there's a diversion possibility
963 pending_block = &*block;
968 if(block->get_turnout_id())
970 const TrackType::Endpoint &track_ep = track->get_type().get_endpoints()[track.entry()];
971 bool multiple_paths = (track_ep.paths&(track_ep.paths-1));
973 if(multiple_paths || !last->get_turnout_id())
975 /* We can keep the blocks reserved so far if we are facing the
976 points or if there was no turnout immediately before this one.
977 With multiple successive turnouts (as is common in crossovers) it's
978 best to hold at one we can divert from. */
984 // Figure out what path we'd like to take on the turnout
986 for(list<RouteRef>::iterator i=cur_route; (path<0 && i!=routes.end()); ++i)
987 path = i->route->get_turnout(block->get_turnout_id());
989 path = track->get_active_path();
990 if(!(track_ep.paths&(1<<path)))
992 for(unsigned i=0; track_ep.paths>>i; ++i)
993 if(track_ep.paths&(1<<i))
997 if(path!=static_cast<int>(track->get_active_path()))
999 // The turnout is set to wrong path - switch and wait for it
1000 pending_block = &*block;
1001 track->set_active_path(path);
1009 if(multiple_paths && cur_route!=routes.end() && cur_route->diversion!=block->get_turnout_id())
1010 /* There's multiple paths to be taken and we are on a route - take
1011 note of the diversion possibility */
1012 divert_track = &*track;
1015 if(!contested_blocks.empty() && contested_blocks.front()==block)
1016 contested_blocks.pop_front();
1018 blocks.push_back(block);
1019 if(cur_blocks_end==blocks.end())
1021 if(block->get_sensor_id())
1024 dist += block->get_path_length(block.entry());
1027 // Unreserve blocks that were not good
1028 while(blocks.end()!=cur_blocks_end && blocks.back()!=good)
1030 if(--blocks.end()==cur_blocks_end)
1031 cur_blocks_end = blocks.end();
1032 blocks.back()->reserve(0);
1036 if(!blocks.back()!=start)
1037 // We got some new blocks, so no longer need to yield
1040 // Make any sensorless blocks at the beginning immediately current
1041 while(cur_blocks_end!=blocks.end() && !(*cur_blocks_end)->get_sensor_id())
1044 if(try_divert && divert(*divert_track))
1045 return reserve_more();
1050 float Train::get_reserved_distance_until(const Block *until_block, bool back) const
1055 Vehicle &veh = *(reverse!=back ? vehicles.back() : vehicles.front());
1056 const VehicleType &vtype = veh.get_type();
1058 TrackIter track(veh.get_track(), veh.get_entry());
1059 if(!track) // XXX Probably unnecessary
1062 BlockList::const_iterator block = blocks.begin();
1063 while(block!=blocks.end() && !(*block)->has_track(*track))
1065 if(block==blocks.end() || &**block==until_block)
1068 float result = veh.get_offset();
1070 track = track.reverse();
1072 result = track->get_type().get_path_length(track->get_active_path())-result;
1073 result -= vtype.get_length()/2;
1077 track = track.next();
1081 if(!(*block)->has_track(*track))
1085 if(block==blocks.begin())
1092 if(block==blocks.end())
1096 if(&**block==until_block)
1100 result += track->get_type().get_path_length(track->get_active_path());
1106 float Train::get_real_speed(unsigned i) const
1108 if(real_speed[i].weight)
1109 return real_speed[i].speed;
1113 for(low=i; low>0; --low)
1114 if(real_speed[low].weight)
1116 for(high=i; high<14; ++high)
1117 if(real_speed[high].weight)
1120 if(real_speed[high].weight)
1122 if(real_speed[low].weight)
1124 float f = float(i-low)/(high-low);
1125 return real_speed[low].speed*(1-f)+real_speed[high].speed*f;
1128 return real_speed[high].speed*float(i)/high;
1130 else if(real_speed[low].weight)
1131 return real_speed[low].speed*float(i)/low;
1136 unsigned Train::find_speed(float real) const
1138 if(real<=real_speed[0].speed)
1144 for(unsigned i=0; (!high && i<=14); ++i)
1145 if(real_speed[i].weight)
1148 if(real_speed[i].speed<real)
1162 return min(min(static_cast<unsigned>(low*real/real_speed[low].speed), 14U), last+3);
1165 float f = (real-real_speed[low].speed)/(real_speed[high].speed-real_speed[low].speed);
1166 return static_cast<unsigned>(low*(1-f)+high*f+0.5);
1169 float Train::get_travel_speed() const
1171 float speed = get_real_speed(current_speed);
1172 float scale = layout.get_catalogue().get_scale();
1173 return static_cast<int>(round(speed/scale*3.6/5))*5;
1176 void Train::set_status(const string &s)
1179 signal_status_changed.emit(s);
1182 void Train::release_blocks()
1184 release_blocks(blocks.begin(), blocks.end());
1187 void Train::release_blocks(BlockList::iterator begin, BlockList::iterator end)
1191 if(begin==cur_blocks_end)
1192 cur_blocks_end = end;
1194 Block &block = **begin;
1195 blocks.erase(begin++);
1200 void Train::reverse_blocks(BlockList &blks) const
1203 for(BlockList::iterator i=blks.begin(); i!=blks.end(); ++i)
1207 bool Train::advance_route(list<RouteRef>::iterator &iter, Track &track)
1209 while(iter!=routes.end() && !iter->route->has_track(track))
1211 if(iter==routes.end())
1214 list<RouteRef>::iterator next = iter;
1216 if(next!=routes.end() && next->diversion && next->route->has_track(track))
1222 Route *Train::create_lead_route(Route *lead, const Route *target)
1226 lead = new Route(layout);
1227 lead->set_name("Lead");
1228 lead->set_temporary(true);
1231 set<Track *> tracks;
1232 for(BlockList::iterator i=blocks.begin(); i!=blocks.end(); ++i)
1234 const set<Track *> &btracks = (*i)->get_tracks();
1235 for(set<Track *>::const_iterator j=btracks.begin(); j!=btracks.end(); ++j)
1236 if(!target || !target->has_track(**j))
1240 lead->add_tracks(tracks);
1245 bool Train::is_valid_diversion(const Route &diversion, const TrackIter &from)
1247 float diversion_len = 0;
1248 TrackLoopIter track1 = from;
1249 while(diversion.has_track(*track1))
1251 unsigned path = diversion.get_path(*track1);
1252 diversion_len += track1->get_type().get_path_length(path);
1254 track1 = track1.next(path);
1260 list<RouteRef>::iterator route = routes.begin();
1261 if(!advance_route(route, *from))
1264 float route_len = 0;
1265 TrackLoopIter track2 = from;
1268 unsigned path = route->route->get_path(*track2);
1269 route_len += track2->get_type().get_path_length(path);
1271 bool ok = (track2!=from && diversion.has_track(*track2));
1273 track2 = track2.next(path);
1281 if(!advance_route(route, *track2))
1285 // Must end up at the same place through both routes
1289 return diversion_len<route_len*1.2;
1293 Train::RouteRef::RouteRef(const Route *r, unsigned d):
1299 Train::RealSpeed::RealSpeed():
1304 void Train::RealSpeed::add(float s, float w)
1306 speed = (speed*weight+s*w)/(weight+w);
1307 weight = min(weight+w, 300.0f);
1311 Train::Loader::Loader(Train &t):
1312 DataFile::BasicLoader<Train>(t),
1316 add("block", &Loader::block);
1317 add("block_hint", &Loader::block_hint);
1318 add("name", &Loader::name);
1319 add("priority", &Train::priority);
1320 add("real_speed", &Loader::real_speed);
1321 add("route", &Loader::route);
1322 add("timetable", &Loader::timetable);
1323 add("vehicle", &Loader::vehicle);
1326 void Train::Loader::finish()
1328 if(!obj.blocks.empty())
1330 TrackIter track = obj.blocks.front().track_iter();
1331 float offset = 2*obj.layout.get_catalogue().get_scale();
1332 obj.vehicles.back()->place(*track, track.entry(), offset, Vehicle::BACK_BUFFER);
1334 obj.set_status("Stopped");
1338 void Train::Loader::block(unsigned id)
1346 blk = &obj.layout.get_block(id);
1348 catch(const KeyError &)
1350 blocks_valid = false;
1356 entry = blk->get_endpoint_by_link(*prev_block);
1361 obj.blocks.push_back(BlockIter(blk, entry));
1363 if(blk->get_sensor_id())
1364 obj.layout.get_driver().set_sensor(blk->get_sensor_id(), true);
1369 void Train::Loader::block_hint(unsigned id)
1373 prev_block = &obj.layout.get_block(id);
1375 catch(const KeyError &)
1377 blocks_valid = false;
1381 void Train::Loader::name(const string &n)
1386 void Train::Loader::real_speed(unsigned i, float speed, float weight)
1388 obj.real_speed[i].speed = speed;
1389 obj.real_speed[i].weight = weight;
1392 void Train::Loader::route(const string &n)
1394 obj.set_route(&obj.layout.get_route(n));
1397 void Train::Loader::timetable()
1400 throw InvalidState("A timetable has already been loaded");
1402 obj.timetable = new Timetable(obj);
1403 load_sub(*obj.timetable);
1406 void Train::Loader::vehicle(ArticleNumber art_nr)
1408 const VehicleType &vtype = obj.layout.get_catalogue().get_vehicle(art_nr);
1409 Vehicle *veh = new Vehicle(obj.layout, vtype);
1410 obj.vehicles.back()->attach_back(*veh);
1411 obj.vehicles.push_back(veh);
1414 } // namespace Marklin