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()),
50 clear_blocks_end(blocks.end()),
54 controller(new AIControl(*this, new SimpleController)),
58 speed_changing(false),
66 accurate_position(false),
69 if(!loco_type.is_locomotive())
70 throw InvalidParameterValue("Initial vehicle must be a locomotive");
72 vehicles.push_back(new Vehicle(layout, loco_type));
74 layout.add_train(*this);
76 layout.get_driver().add_loco(address);
77 layout.get_driver().signal_loco_speed.connect(sigc::mem_fun(this, &Train::loco_speed_event));
78 layout.get_driver().signal_loco_function.connect(sigc::mem_fun(this, &Train::loco_func_event));
80 layout.signal_block_reserved.connect(sigc::mem_fun(this, &Train::block_reserved));
81 layout.get_driver().signal_sensor.connect(sigc::mem_fun(this, &Train::sensor_event));
83 layout.get_driver().signal_halt.connect(sigc::mem_fun(this, &Train::halt_event));
85 const set<Track *> &tracks = layout.get_tracks();
86 for(set<Track *>::const_iterator i=tracks.begin(); i!=tracks.end(); ++i)
87 if((*i)->get_turnout_id())
88 (*i)->signal_path_changed.connect(sigc::hide(sigc::bind(sigc::mem_fun(this, &Train::turnout_path_changed), sigc::ref(**i))));
90 controller->signal_control_changed.connect(sigc::mem_fun(this, &Train::control_changed));
97 for(vector<Vehicle *>::iterator i=vehicles.begin(); i!=vehicles.end(); ++i)
99 layout.remove_train(*this);
102 void Train::set_name(const string &n)
106 signal_name_changed.emit(name);
109 void Train::set_priority(int p)
114 void Train::yield_to(const Train &t)
119 void Train::add_vehicle(const VehicleType &vt)
121 Vehicle *veh = new Vehicle(layout, vt);
122 vehicles.back()->attach_back(*veh);
123 vehicles.push_back(veh);
126 void Train::remove_vehicle(unsigned i)
128 if(i>=vehicles.size())
129 throw InvalidParameterValue("Vehicle index out of range");
131 throw InvalidParameterValue("Can't remove the locomotive");
133 vehicles.erase(vehicles.begin()+i);
134 if(i<vehicles.size())
135 vehicles[i-1]->attach_back(*vehicles[i]);
138 unsigned Train::get_n_vehicles() const
140 return vehicles.size();
143 Vehicle &Train::get_vehicle(unsigned i)
145 if(i>=vehicles.size())
146 throw InvalidParameterValue("Vehicle index out of range");
150 const Vehicle &Train::get_vehicle(unsigned i) const
152 if(i>=vehicles.size())
153 throw InvalidParameterValue("Vehicle index out of range");
157 void Train::set_control(const string &n, float v)
159 controller->set_control(n, v);
162 void Train::set_active(bool a)
166 if(!a && controller->get_speed())
167 throw InvalidState("Can't deactivate while moving");
172 stop_timeout = Time::TimeStamp();
177 stop_timeout = Time::now()+2*Time::sec;
178 set_status("Stopped");
182 void Train::set_function(unsigned func, bool state)
184 if(!loco_type.get_functions().count(func))
185 throw InvalidParameterValue("Invalid function");
187 layout.get_driver().set_loco_function(address, func, state);
189 layout.get_driver().set_loco_function(address+1, func-4, state);
192 float Train::get_control(const string &ctrl) const
194 return controller->get_control(ctrl).value;
197 float Train::get_speed() const
199 return controller->get_speed();
202 bool Train::get_function(unsigned func) const
204 return (functions>>func)&1;
207 void Train::set_timetable(Timetable *tt)
213 bool Train::set_route(const Route *r)
215 free_noncritical_blocks();
218 if(r && !blocks.empty())
220 TrackIter first = blocks.front().track_iter();
221 TrackIter next = blocks.back().next().track_iter();
222 if(!r->has_track(*next))
224 lead = Route::find(next, *r);
227 create_lead_route(lead, lead);
228 routes.push_front(lead);
230 else if(!r->has_track(*first))
231 lead = create_lead_route(0, r);
236 routes.push_back(lead);
239 end_of_route = false;
243 signal_route_changed.emit(get_route());
248 bool Train::go_to(Track &to)
250 for(BlockList::const_iterator i=blocks.begin(); i!=cur_blocks_end; ++i)
251 if((*i)->has_track(to))
253 signal_arrived.emit();
257 free_noncritical_blocks();
259 TrackIter next = blocks.back().next().track_iter();
261 Route *route = Route::find(next, to);
264 create_lead_route(route, route);
265 return set_route(route);
268 bool Train::divert(Track &from)
270 if(!from.get_turnout_id())
271 throw InvalidParameterValue("Can't divert from a non-turnout");
277 list<RouteRef>::iterator route = routes.begin();
279 // Follow our routes to find out where we're entering the turnout
280 for(TrackLoopIter track = blocks.front().track_iter();;)
282 if(!advance_route(route, *track))
287 Block &block = track->get_block();
288 if(block.get_train()==this && !free_block(block))
291 int route_path = route->route->get_turnout(from.get_turnout_id());
293 // Check that more than one path is available
294 unsigned ep_paths = track->get_type().get_endpoints()[track.entry()].paths;
295 if(!(ep_paths&(ep_paths-1)))
298 // Choose some other path
299 for(int i=0; ep_paths>>i; ++i)
300 if((ep_paths&(1<<i)) && i!=route_path)
306 entry = track.entry();
310 track = track.next(route->route->get_path(*track));
312 if(!track || track.looped())
316 TrackIter track = TrackIter(&from, entry).next(path);
321 for(list<RouteRef>::iterator i=routes.begin(); i!=routes.end(); ++i)
322 tracks.insert(i->route->get_tracks().begin(), i->route->get_tracks().end());
323 RefPtr<Route> diversion = Route::find(track, tracks);
327 diversion->set_name("Diversion");
328 diversion->add_track(from);
329 diversion->set_turnout(from.get_turnout_id(), path);
331 if(!is_valid_diversion(*diversion, TrackIter(&from, entry)))
334 // Follow the diversion route until we get back to the original route
335 list<RouteRef>::iterator end = routes.end();
338 for(list<RouteRef>::iterator i=route; (end==routes.end() && i!=routes.end()); ++i)
339 if(i->route->has_track(*track))
342 if(end!=routes.end())
344 else if(!diversion->has_track(*track))
345 throw LogicError("Pathfinder returned a bad route");
347 track = track.next(diversion->get_path(*track));
351 // We are rejoining the same route we diverted from, duplicate it
352 routes.insert(end, *route);
356 routes.erase(route, end);
358 routes.insert(end, RouteRef(diversion.release(), from.get_turnout_id()));
363 const Route *Train::get_route() const
367 return routes.front().route;
370 void Train::place(Block &block, unsigned entry)
372 if(controller->get_speed())
373 throw InvalidState("Must be stopped before placing");
378 accurate_position = false;
380 if(!block.reserve(this))
382 set_status("Unplaced");
386 blocks.push_back(BlockIter(&block, entry));
389 TrackIter track = BlockIter(&block, entry).reverse().track_iter();
390 vehicles.front()->place(*track, track.entry(), 0, Vehicle::FRONT_BUFFER);
394 const Block::Endpoint &bep = block.get_endpoints()[entry];
395 vehicles.back()->place(*bep.track, bep.track_ep, 0, Vehicle::BACK_BUFFER);
399 void Train::unplace()
401 if(controller->get_speed())
402 throw InvalidState("Must be stopped before unplacing");
407 accurate_position = false;
409 for(vector<Vehicle *>::iterator i=vehicles.begin(); i!=vehicles.end(); ++i)
412 set_status("Unplaced");
415 bool Train::free_block(Block &block)
417 float margin = 10*layout.get_catalogue().get_scale();
418 if(get_reserved_distance_until(&block, false)<controller->get_braking_distance()*1.3+margin)
422 for(BlockList::iterator i=cur_blocks_end; i!=blocks.end(); ++i)
424 if(i->block()==&block)
428 release_blocks(i, blocks.end());
431 else if((*i)->get_sensor_id())
438 void Train::free_noncritical_blocks()
443 if(controller->get_speed()==0)
445 release_blocks(cur_blocks_end, blocks.end());
449 float margin = 10*layout.get_catalogue().get_scale();
450 float min_dist = controller->get_braking_distance()*1.3+margin;
452 Vehicle &veh = *(reverse ? vehicles.back() : vehicles.front());
454 TrackIter track(veh.get_track(), veh.get_entry());
455 BlockList::iterator block = blocks.begin();
457 while(block!=blocks.end() && !(*block)->has_track(*track))
460 if(block==cur_blocks_end)
464 float dist = veh.get_offset();
468 dist = track->get_type().get_path_length(track->get_active_path())-dist;
469 dist -= veh.get_type().get_length()/2;
474 track = track.next();
476 if(!(*block)->has_track(*track))
479 if(block==cur_blocks_end)
481 if(block==blocks.end())
484 if(dist>min_dist && nsens>0)
486 release_blocks(block, blocks.end());
490 if(in_rsv && (*block)->get_sensor_id())
494 dist += track->get_type().get_path_length(track->get_active_path());
498 int Train::get_entry_to_block(Block &block) const
500 for(BlockList::const_iterator i=blocks.begin(); i!=blocks.end(); ++i)
501 if(i->block()==&block)
506 float Train::get_reserved_distance() const
508 return get_reserved_distance_until(0, false);
511 void Train::tick(const Time::TimeStamp &t, const Time::TimeDelta &dt)
513 if(!active && stop_timeout && t>=stop_timeout)
515 release_blocks(cur_blocks_end, blocks.end());
516 stop_timeout = Time::TimeStamp();
519 Driver &driver = layout.get_driver();
523 controller->tick(dt);
524 float speed = controller->get_speed();
525 unsigned speed_notch = find_speed(speed);
527 if(controller->get_reverse()!=reverse)
529 reverse = controller->get_reverse();
530 driver.set_loco_reverse(address, reverse);
532 release_blocks(cur_blocks_end, blocks.end());
533 reverse_blocks(blocks);
537 if(speed_notch!=current_speed && !speed_changing && !driver.is_halted() && driver.get_power())
539 speed_changing = true;
540 driver.set_loco_speed(address, speed_notch);
545 set_status(format("Traveling %d kmh", get_travel_speed()));
547 set_status("Waiting");
555 Vehicle &vehicle = *(reverse ? vehicles.back() : vehicles.front());
556 Track *track = vehicle.get_track();
559 for(BlockList::const_iterator i=blocks.begin(); (!ok && i!=cur_blocks_end); ++i)
560 ok = (*i)->has_track(*track);
562 float d = get_real_speed(current_speed)*(dt/Time::sec);
565 SetFlag setf(advancing);
566 vehicle.advance(reverse ? -d : d);
568 else if(accurate_position)
571 if(overshoot_dist>40*layout.get_catalogue().get_scale())
573 layout.emergency(name+" has not arrived at sensor");
574 accurate_position = false;
578 else if(end_of_route && cur_blocks_end==blocks.end())
581 signal_arrived.emit();
585 if(!blocks.empty() && !blocks.front()->get_sensor_id())
587 float dist = get_reserved_distance_until(&*blocks.front(), true);
589 if(dist>10*layout.get_catalogue().get_scale())
591 blocks.front()->reserve(0);
597 void Train::save(list<DataFile::Statement> &st) const
599 st.push_back((DataFile::Statement("name"), name));
601 st.push_back((DataFile::Statement("priority"), priority));
603 for(vector<Vehicle *>::const_iterator i=vehicles.begin(); i!=vehicles.end(); ++i)
604 if(i!=vehicles.begin())
605 st.push_back((DataFile::Statement("vehicle"), (*i)->get_type().get_article_number()));
607 for(unsigned i=0; i<=14; ++i)
608 if(real_speed[i].weight)
609 st.push_back((DataFile::Statement("real_speed"), i, real_speed[i].speed, real_speed[i].weight));
611 if(!blocks.empty() && cur_blocks_end!=blocks.begin())
613 BlockList blks(blocks.begin(), BlockList::const_iterator(cur_blocks_end));
615 reverse_blocks(blks);
617 BlockIter prev = blks.front().flip();
618 st.push_back((DataFile::Statement("block_hint"), prev->get_id()));
620 for(BlockList::const_iterator i=blks.begin(); i!=blks.end(); ++i)
621 st.push_back((DataFile::Statement("block"), (*i)->get_id()));
626 list<RouteRef>::const_iterator i = routes.begin();
627 for(; (i!=routes.end() && i->route->is_temporary()); ++i) ;
629 st.push_back((DataFile::Statement("route"), i->route->get_name()));
634 DataFile::Statement ss("timetable");
635 timetable->save(ss.sub);
640 void Train::control_changed(const Controller::Control &ctrl)
642 signal_control_changed.emit(ctrl.name, ctrl.value);
645 void Train::loco_speed_event(unsigned addr, unsigned speed, bool)
649 current_speed = speed;
650 speed_changing = false;
655 void Train::loco_func_event(unsigned addr, unsigned func, bool state)
657 if(addr==address || (addr==address+1 && loco_type.get_max_function()>4))
662 functions |= 1<<func;
664 functions &= ~(1<<func);
666 signal_function_changed.emit(func, state);
670 void Train::sensor_event(unsigned addr, bool state)
674 // Find the first sensor block from our reserved blocks that isn't this sensor
675 BlockList::iterator end;
677 for(end=cur_blocks_end; end!=blocks.end(); ++end)
678 if((*end)->get_sensor_id())
680 if((*end)->get_sensor_id()!=addr)
695 // Compute speed and update related state
696 float travel_time_secs = (Time::now()-last_entry_time)/Time::sec;
702 RealSpeed &rs = real_speed[current_speed];
703 rs.add(travel_dist/travel_time_secs, travel_time_secs);
705 set_status(format("Traveling %d kmh", get_travel_speed()));
709 for(BlockList::iterator j=cur_blocks_end; j!=end; ++j)
711 travel_dist += (*j)->get_path_length(j->entry());
713 if((*j)->get_sensor_id()==addr && !advancing)
715 TrackIter track = j->track_iter();
718 track = track.flip();
719 vehicles.back()->place(*track, track.entry(), 0, Vehicle::BACK_AXLE);
722 vehicles.front()->place(*track, track.entry(), 0, Vehicle::FRONT_AXLE);
725 last_entry_time = Time::now();
727 accurate_position = true;
730 // Check if we've reached the next route
733 const set<Track *> &rtracks = (++routes.begin())->route->get_tracks();
734 for(BlockList::iterator j=cur_blocks_end; j!=end; ++j)
735 if(rtracks.count((*j)->get_endpoints()[j->entry()].track))
739 signal_route_changed.emit(routes.front().route);
744 // Move blocks up to the next sensor to our current blocks
745 cur_blocks_end = end;
747 // Try to get more blocks if we're moving
752 layout.emergency("Sensor for "+name+" triggered out of order");
756 const Vehicle &veh = *(reverse ? vehicles.front() : vehicles.back());
758 // Find the first sensor in our current blocks that's still active
759 BlockList::iterator end = blocks.begin();
760 for(BlockList::iterator i=blocks.begin(); i!=cur_blocks_end; ++i)
762 if((*i)->has_track(*veh.get_track()))
764 if((*i)->get_sensor_id())
766 if(layout.get_driver().get_sensor((*i)->get_sensor_id()))
776 if(end!=blocks.begin() && end!=cur_blocks_end)
777 // Free blocks up to the last inactive sensor
778 release_blocks(blocks.begin(), end);
782 void Train::turnout_path_changed(Track &track)
784 for(list<BlockIter>::iterator i=blocks.begin(); i!=blocks.end(); ++i)
785 if((*i)->get_turnout_id()==track.get_turnout_id() && !reserving)
786 check_turnout_paths(false);
789 void Train::halt_event(bool h)
792 accurate_position = false;
795 void Train::block_reserved(const Block &block, const Train *train)
797 if(&block==pending_block && !train && !reserving)
801 void Train::reserve_more()
803 if(!active || blocks.empty() || end_of_route)
806 BlockIter start = blocks.back();
810 // See how many sensor blocks and how much track we already have
813 for(BlockList::const_iterator i=cur_blocks_end; i!=blocks.end(); ++i)
815 if((*i)->get_sensor_id())
818 dist += (*i)->get_path_length(i->entry());
821 list<RouteRef>::iterator cur_route = routes.begin();
822 advance_route(cur_route, *start.track_iter());
824 float approach_margin = 50*layout.get_catalogue().get_scale();
825 float min_dist = controller->get_braking_distance()*1.3+approach_margin*2;
827 BlockIter block = start;
828 list<BlockIter>::iterator good_end = blocks.end();
829 Track *divert_track = 0;
830 bool try_divert = false;
831 Train *blocking_train = 0;
832 BlockList contested_blocks;
834 SetFlag setf(reserving);
838 BlockIter last = block;
839 block = block.next(cur_route!=routes.end() ? cur_route->route : 0);
840 if(!block || block->get_endpoints().size()<2)
843 good_end = blocks.end();
847 TrackIter track = block.track_iter();
849 if(cur_route!=routes.end())
851 if(!advance_route(cur_route, *track))
853 // Keep the blocks if we arrived at the end of the route
856 good_end = blocks.end();
863 if(block->get_turnout_id() && !last->get_turnout_id())
865 /* We can keep the blocks if we arrive at a turnout from a non-turnout
866 block. Having a turnout block as our last reserved block is not good
867 as it would limit our diversion possibilities for little benefit. */
868 good_end = blocks.end();
869 if(nsens>=3 && dist>=min_dist)
875 if(block->get_train()!=blocking_train)
877 if(blocking_train->free_block(*contested_blocks.back()))
879 // Roll back and start actually reserving the blocks
880 block = blocks.back();
881 cur_route = routes.begin();
882 advance_route(cur_route, *block.track_iter().track());
883 if(blocking_train->get_priority()==priority)
884 blocking_train->yield_to(*this);
890 yield_to(*blocking_train);
891 pending_block = contested_blocks.front().block();
892 try_divert = divert_track;
898 contested_blocks.push_back(block);
903 bool reserved = block->reserve(this);
906 /* We've found another train. If it wants to exit the block from the
907 same endpoint we're trying to enter from or the other way around,
908 treat it as coming towards us. Otherwise treat it as going in the
910 Train *other_train = block->get_train();
911 int other_entry = other_train->get_entry_to_block(*block);
913 throw LogicError("Block reservation inconsistency");
915 unsigned exit = block.reverse().entry();
916 unsigned other_exit = BlockIter(block.block(), other_entry).reverse().entry();
917 bool entry_conflict = (block.entry()==other_exit);
918 bool exit_conflict = (exit==static_cast<unsigned>(other_entry));
919 if(!entry_conflict && !last->get_turnout_id())
920 /* The other train is not coming to the blocks we're holding, so we
922 good_end = blocks.end();
924 int other_prio = other_train->get_priority();
926 if(!entry_conflict && !exit_conflict && other_prio<priority)
928 /* Ask a lesser priority train going to the same direction to free
930 if(other_train->free_block(*block))
931 reserved = block->reserve(this);
933 else if(other_train!=yielding_to && (other_prio<priority || (other_prio==priority && entry_conflict)))
935 /* A lesser priority train is coming at us, we must ask it to free
936 enough blocks to get clear of it to avoid a potential deadlock */
937 blocking_train = other_train;
938 contested_blocks.clear();
939 contested_blocks.push_back(block);
942 else if(divert_track && (entry_conflict || exit_conflict))
943 // We are blocked, but there's a diversion possibility
948 pending_block = &*block;
953 if(block->get_turnout_id())
955 const TrackType::Endpoint &track_ep = track->get_type().get_endpoints()[track.entry()];
956 bool multiple_paths = (track_ep.paths&(track_ep.paths-1));
958 if(multiple_paths && cur_route!=routes.end() && cur_route->diversion!=block->get_turnout_id())
959 /* There's multiple paths to be taken and we are on a route - take
960 note of the diversion possibility */
961 divert_track = &*track;
964 if(!contested_blocks.empty() && contested_blocks.front()==block)
965 contested_blocks.pop_front();
967 blocks.push_back(block);
969 if(cur_blocks_end==blocks.end())
971 if(clear_blocks_end==blocks.end())
973 if(good_end==blocks.end())
976 if(block->get_sensor_id())
979 dist += block->get_path_length(block.entry());
982 // Unreserve blocks that were not good
983 release_blocks(good_end, blocks.end());
985 if(blocks.back()!=start)
986 // We got some new blocks, so no longer need to yield
989 check_turnout_paths(true);
991 // Make any sensorless blocks at the beginning immediately current
992 while(cur_blocks_end!=clear_blocks_end && !(*cur_blocks_end)->get_sensor_id())
995 if(try_divert && divert(*divert_track))
999 void Train::check_turnout_paths(bool set)
1001 if(clear_blocks_end==blocks.end())
1004 for(list<BlockIter>::iterator i=clear_blocks_end; i!=blocks.end(); ++i)
1006 if((*i)->get_turnout_id())
1008 TrackIter track = i->track_iter();
1009 const TrackType::Endpoint &track_ep = track->get_type().get_endpoints()[track.entry()];
1012 list<BlockIter>::iterator j = i;
1013 if(++j!=blocks.end())
1015 TrackIter rev = j->track_iter().flip();
1016 unsigned mask = rev->get_type().get_endpoints()[rev.entry()].paths&track_ep.paths;
1017 for(path=0; mask>1; mask>>=1, ++path) ;
1022 if(path!=track->get_active_path())
1025 track->set_active_path(path);
1027 /* Check again, in case the driver was able to service the request
1029 if(!set || path!=track->get_active_path())
1034 if(i==clear_blocks_end)
1039 float Train::get_reserved_distance_until(const Block *until_block, bool back) const
1044 Vehicle &veh = *(reverse!=back ? vehicles.back() : vehicles.front());
1045 const VehicleType &vtype = veh.get_type();
1047 TrackIter track(veh.get_track(), veh.get_entry());
1048 if(!track) // XXX Probably unnecessary
1051 BlockList::const_iterator block = blocks.begin();
1052 while(block!=clear_blocks_end && !(*block)->has_track(*track))
1054 if(block==clear_blocks_end || &**block==until_block)
1057 float result = veh.get_offset();
1059 track = track.reverse();
1061 result = track->get_type().get_path_length(track->get_active_path())-result;
1062 result -= vtype.get_length()/2;
1066 track = track.next();
1070 if(!(*block)->has_track(*track))
1074 if(block==blocks.begin())
1081 if(block==clear_blocks_end)
1085 if(&**block==until_block)
1089 result += track->get_type().get_path_length(track->get_active_path());
1095 float Train::get_real_speed(unsigned i) const
1097 if(real_speed[i].weight)
1098 return real_speed[i].speed;
1102 for(low=i; low>0; --low)
1103 if(real_speed[low].weight)
1105 for(high=i; high<14; ++high)
1106 if(real_speed[high].weight)
1109 if(real_speed[high].weight)
1111 if(real_speed[low].weight)
1113 float f = float(i-low)/(high-low);
1114 return real_speed[low].speed*(1-f)+real_speed[high].speed*f;
1117 return real_speed[high].speed*float(i)/high;
1119 else if(real_speed[low].weight)
1120 return real_speed[low].speed*float(i)/low;
1125 unsigned Train::find_speed(float real) const
1127 if(real<=real_speed[0].speed)
1133 for(unsigned i=0; (!high && i<=14); ++i)
1134 if(real_speed[i].weight)
1137 if(real_speed[i].speed<real)
1151 return min(min(static_cast<unsigned>(low*real/real_speed[low].speed), 14U), last+3);
1154 float f = (real-real_speed[low].speed)/(real_speed[high].speed-real_speed[low].speed);
1155 return static_cast<unsigned>(low*(1-f)+high*f+0.5);
1158 float Train::get_travel_speed() const
1160 float speed = get_real_speed(current_speed);
1161 float scale = layout.get_catalogue().get_scale();
1162 return static_cast<int>(round(speed/scale*3.6/5))*5;
1165 void Train::set_status(const string &s)
1168 signal_status_changed.emit(s);
1171 void Train::release_blocks()
1173 release_blocks(blocks.begin(), blocks.end());
1176 void Train::release_blocks(BlockList::iterator begin, BlockList::iterator end)
1180 if(begin==cur_blocks_end)
1181 cur_blocks_end = end;
1182 if(begin==clear_blocks_end)
1183 clear_blocks_end = end;
1185 Block &block = **begin;
1186 blocks.erase(begin++);
1189 if(begin==blocks.end())
1190 end_of_route = false;
1194 void Train::reverse_blocks(BlockList &blks) const
1197 for(BlockList::iterator i=blks.begin(); i!=blks.end(); ++i)
1201 bool Train::advance_route(list<RouteRef>::iterator &iter, Track &track)
1203 while(iter!=routes.end() && !iter->route->has_track(track))
1205 if(iter==routes.end())
1208 list<RouteRef>::iterator next = iter;
1210 if(next!=routes.end() && next->diversion && next->route->has_track(track))
1216 Route *Train::create_lead_route(Route *lead, const Route *target)
1220 lead = new Route(layout);
1221 lead->set_name("Lead");
1222 lead->set_temporary(true);
1225 set<Track *> tracks;
1226 for(BlockList::iterator i=blocks.begin(); i!=blocks.end(); ++i)
1228 const set<Track *> &btracks = (*i)->get_tracks();
1229 for(set<Track *>::const_iterator j=btracks.begin(); j!=btracks.end(); ++j)
1230 if(!target || !target->has_track(**j))
1234 lead->add_tracks(tracks);
1239 bool Train::is_valid_diversion(const Route &diversion, const TrackIter &from)
1241 float diversion_len = 0;
1242 TrackLoopIter track1 = from;
1243 while(diversion.has_track(*track1))
1245 unsigned path = diversion.get_path(*track1);
1246 diversion_len += track1->get_type().get_path_length(path);
1248 track1 = track1.next(path);
1254 list<RouteRef>::iterator route = routes.begin();
1255 if(!advance_route(route, *from))
1258 float route_len = 0;
1259 TrackLoopIter track2 = from;
1262 unsigned path = route->route->get_path(*track2);
1263 route_len += track2->get_type().get_path_length(path);
1265 bool ok = (track2!=from && diversion.has_track(*track2));
1267 track2 = track2.next(path);
1275 if(!advance_route(route, *track2))
1279 // Must end up at the same place through both routes
1283 return diversion_len<route_len*1.2;
1287 Train::RouteRef::RouteRef(const Route *r, unsigned d):
1293 Train::RealSpeed::RealSpeed():
1298 void Train::RealSpeed::add(float s, float w)
1300 speed = (speed*weight+s*w)/(weight+w);
1301 weight = min(weight+w, 300.0f);
1305 Train::Loader::Loader(Train &t):
1306 DataFile::BasicLoader<Train>(t),
1310 add("block", &Loader::block);
1311 add("block_hint", &Loader::block_hint);
1312 add("name", &Loader::name);
1313 add("priority", &Train::priority);
1314 add("real_speed", &Loader::real_speed);
1315 add("route", &Loader::route);
1316 add("timetable", &Loader::timetable);
1317 add("vehicle", &Loader::vehicle);
1320 void Train::Loader::finish()
1322 if(!obj.blocks.empty())
1324 TrackIter track = obj.blocks.front().track_iter();
1325 float offset = 2*obj.layout.get_catalogue().get_scale();
1326 obj.vehicles.back()->place(*track, track.entry(), offset, Vehicle::BACK_BUFFER);
1328 obj.set_status("Stopped");
1332 void Train::Loader::block(unsigned id)
1340 blk = &obj.layout.get_block(id);
1342 catch(const KeyError &)
1344 blocks_valid = false;
1350 entry = blk->get_endpoint_by_link(*prev_block);
1355 obj.blocks.push_back(BlockIter(blk, entry));
1357 if(blk->get_sensor_id())
1358 obj.layout.get_driver().set_sensor(blk->get_sensor_id(), true);
1363 void Train::Loader::block_hint(unsigned id)
1367 prev_block = &obj.layout.get_block(id);
1369 catch(const KeyError &)
1371 blocks_valid = false;
1375 void Train::Loader::name(const string &n)
1380 void Train::Loader::real_speed(unsigned i, float speed, float weight)
1382 obj.real_speed[i].speed = speed;
1383 obj.real_speed[i].weight = weight;
1386 void Train::Loader::route(const string &n)
1388 obj.set_route(&obj.layout.get_route(n));
1391 void Train::Loader::timetable()
1394 throw InvalidState("A timetable has already been loaded");
1396 obj.timetable = new Timetable(obj);
1397 load_sub(*obj.timetable);
1400 void Train::Loader::vehicle(ArticleNumber art_nr)
1402 const VehicleType &vtype = obj.layout.get_catalogue().get_vehicle(art_nr);
1403 Vehicle *veh = new Vehicle(obj.layout, vtype);
1404 obj.vehicles.back()->attach_back(*veh);
1405 obj.vehicles.push_back(veh);
1408 } // namespace Marklin