3 This file is part of R²C²
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"
35 SetFlag(bool &f): flag(f) { flag = true; }
36 ~SetFlag() { flag = false; }
44 Train::Train(Layout &l, const VehicleType &t, unsigned a, const string &p):
52 cur_blocks_end(blocks.end()),
53 clear_blocks_end(blocks.end()),
57 controller(new AIControl(*this, new SimpleController)),
60 current_speed_step(0),
61 speed_changing(false),
68 real_speed(layout.get_driver().get_protocol_speed_steps(protocol)+1),
69 accurate_position(false),
72 if(!loco_type.is_locomotive())
73 throw InvalidParameterValue("Initial vehicle must be a locomotive");
75 vehicles.push_back(new Vehicle(layout, loco_type));
77 layout.add_train(*this);
79 layout.get_driver().add_loco(address, protocol, loco_type);
80 layout.get_driver().signal_loco_speed.connect(sigc::mem_fun(this, &Train::loco_speed_event));
81 layout.get_driver().signal_loco_function.connect(sigc::mem_fun(this, &Train::loco_func_event));
83 layout.signal_block_reserved.connect(sigc::mem_fun(this, &Train::block_reserved));
84 layout.get_driver().signal_sensor.connect(sigc::mem_fun(this, &Train::sensor_event));
86 layout.get_driver().signal_halt.connect(sigc::mem_fun(this, &Train::halt_event));
88 const set<Track *> &tracks = layout.get_tracks();
89 for(set<Track *>::const_iterator i=tracks.begin(); i!=tracks.end(); ++i)
90 if((*i)->get_turnout_id())
91 (*i)->signal_path_changed.connect(sigc::hide(sigc::bind(sigc::mem_fun(this, &Train::turnout_path_changed), sigc::ref(**i))));
93 controller->signal_control_changed.connect(sigc::mem_fun(this, &Train::control_changed));
100 for(vector<Vehicle *>::iterator i=vehicles.begin(); i!=vehicles.end(); ++i)
102 layout.remove_train(*this);
105 void Train::set_name(const string &n)
109 signal_name_changed.emit(name);
112 void Train::set_priority(int p)
117 void Train::yield_to(const Train &t)
122 void Train::add_vehicle(const VehicleType &vt)
124 Vehicle *veh = new Vehicle(layout, vt);
125 vehicles.back()->attach_back(*veh);
126 vehicles.push_back(veh);
129 void Train::remove_vehicle(unsigned i)
131 if(i>=vehicles.size())
132 throw InvalidParameterValue("Vehicle index out of range");
134 throw InvalidParameterValue("Can't remove the locomotive");
136 vehicles.erase(vehicles.begin()+i);
137 if(i<vehicles.size())
138 vehicles[i-1]->attach_back(*vehicles[i]);
141 unsigned Train::get_n_vehicles() const
143 return vehicles.size();
146 Vehicle &Train::get_vehicle(unsigned i)
148 if(i>=vehicles.size())
149 throw InvalidParameterValue("Vehicle index out of range");
153 const Vehicle &Train::get_vehicle(unsigned i) const
155 if(i>=vehicles.size())
156 throw InvalidParameterValue("Vehicle index out of range");
160 void Train::set_control(const string &n, float v)
162 controller->set_control(n, v);
165 void Train::set_active(bool a)
169 if(!a && controller->get_speed())
170 throw InvalidState("Can't deactivate while moving");
175 stop_timeout = Time::TimeStamp();
180 stop_timeout = Time::now()+2*Time::sec;
181 set_status("Stopped");
185 void Train::set_function(unsigned func, bool state)
187 if(!loco_type.get_functions().count(func))
188 throw InvalidParameterValue("Invalid function");
189 layout.get_driver().set_loco_function(address, func, 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::go_to(const Zone &to)
271 for(BlockList::const_iterator i=blocks.begin(); i!=blocks.end(); ++i)
272 tracks.insert((*i)->get_tracks().begin(), (*i)->get_tracks().end());
274 const Zone::TrackSet &ztracks = to.get_tracks();
275 unsigned union_size = 0;
276 for(Zone::TrackSet::const_iterator i=ztracks.begin(); i!=ztracks.end(); ++i)
277 union_size += tracks.count(*i);
279 if(union_size==tracks.size() || union_size==ztracks.size())
281 signal_arrived.emit();
285 free_noncritical_blocks();
287 TrackIter next = blocks.back().next().track_iter();
289 Route *route = Route::find(next, to);
292 create_lead_route(route, route);
293 route->add_tracks(ztracks);
294 return set_route(route);
297 bool Train::divert(Track &from)
299 if(!from.get_turnout_id())
300 throw InvalidParameterValue("Can't divert from a non-turnout");
306 list<RouteRef>::iterator route = routes.begin();
308 // Follow our routes to find out where we're entering the turnout
309 for(TrackLoopIter track = blocks.front().track_iter();;)
311 if(!advance_route(route, *track))
316 Block &block = track->get_block();
317 if(block.get_train()==this && !free_block(block))
320 int route_path = route->route->get_turnout(from.get_turnout_id());
322 // Check that more than one path is available
323 unsigned ep_paths = track.endpoint().paths;
324 if(!(ep_paths&(ep_paths-1)))
327 // Choose some other path
328 for(int i=0; ep_paths>>i; ++i)
329 if((ep_paths&(1<<i)) && i!=route_path)
335 entry = track.entry();
339 track = track.next(route->route->get_path(*track));
341 if(!track || track.looped())
345 TrackIter track = TrackIter(&from, entry).next(path);
350 for(list<RouteRef>::iterator i=routes.begin(); i!=routes.end(); ++i)
351 tracks.insert(i->route->get_tracks().begin(), i->route->get_tracks().end());
352 RefPtr<Route> diversion = Route::find(track, tracks);
356 diversion->set_name("Diversion");
357 diversion->add_track(from);
358 diversion->set_turnout(from.get_turnout_id(), path);
360 if(!is_valid_diversion(*diversion, TrackIter(&from, entry)))
363 // Follow the diversion route until we get back to the original route
364 list<RouteRef>::iterator end = routes.end();
367 for(list<RouteRef>::iterator i=route; (end==routes.end() && i!=routes.end()); ++i)
368 if(i->route->has_track(*track))
371 if(end!=routes.end())
373 else if(!diversion->has_track(*track))
374 throw LogicError("Pathfinder returned a bad route");
376 track = track.next(diversion->get_path(*track));
380 // We are rejoining the same route we diverted from, duplicate it
381 routes.insert(end, *route);
385 routes.erase(route, end);
387 routes.insert(end, RouteRef(diversion.release(), from.get_turnout_id()));
392 const Route *Train::get_route() const
396 return routes.front().route;
399 void Train::place(Block &block, unsigned entry)
401 if(controller->get_speed())
402 throw InvalidState("Must be stopped before placing");
407 accurate_position = false;
409 if(!block.reserve(this))
411 set_status("Unplaced");
415 blocks.push_back(BlockIter(&block, entry));
418 TrackIter track = BlockIter(&block, entry).reverse().track_iter();
419 vehicles.front()->place(*track, track.entry(), 0, Vehicle::FRONT_BUFFER);
423 const Block::Endpoint &bep = block.get_endpoint(entry);
424 vehicles.back()->place(*bep.track, bep.track_ep, 0, Vehicle::BACK_BUFFER);
428 void Train::unplace()
430 if(controller->get_speed())
431 throw InvalidState("Must be stopped before unplacing");
436 accurate_position = false;
438 for(vector<Vehicle *>::iterator i=vehicles.begin(); i!=vehicles.end(); ++i)
441 set_status("Unplaced");
444 bool Train::free_block(Block &block)
446 float margin = 10*layout.get_catalogue().get_scale();
447 if(get_reserved_distance_until(&block, false)<controller->get_braking_distance()*1.3+margin)
451 for(BlockList::iterator i=cur_blocks_end; i!=blocks.end(); ++i)
453 if(i->block()==&block)
457 release_blocks(i, blocks.end());
460 else if((*i)->get_sensor_id())
467 void Train::free_noncritical_blocks()
472 if(controller->get_speed()==0)
474 release_blocks(cur_blocks_end, blocks.end());
478 float margin = 10*layout.get_catalogue().get_scale();
479 float min_dist = controller->get_braking_distance()*1.3+margin;
481 Vehicle &veh = *(reverse ? vehicles.back() : vehicles.front());
483 TrackIter track(veh.get_track(), veh.get_entry());
484 BlockList::iterator block = blocks.begin();
486 while(block!=blocks.end() && !(*block)->has_track(*track))
489 if(block==cur_blocks_end)
493 float dist = veh.get_offset();
497 dist = track->get_type().get_path_length(track->get_active_path())-dist;
498 dist -= veh.get_type().get_length()/2;
503 track = track.next();
505 if(!(*block)->has_track(*track))
508 if(block==cur_blocks_end)
510 if(block==blocks.end())
513 if(dist>min_dist && nsens>0)
515 release_blocks(block, blocks.end());
519 if(in_rsv && (*block)->get_sensor_id())
523 dist += track->get_type().get_path_length(track->get_active_path());
527 int Train::get_entry_to_block(Block &block) const
529 for(BlockList::const_iterator i=blocks.begin(); i!=blocks.end(); ++i)
530 if(i->block()==&block)
535 float Train::get_reserved_distance() const
537 return get_reserved_distance_until(0, false);
540 void Train::tick(const Time::TimeStamp &t, const Time::TimeDelta &dt)
542 if(!active && stop_timeout && t>=stop_timeout)
544 release_blocks(cur_blocks_end, blocks.end());
545 stop_timeout = Time::TimeStamp();
548 Driver &driver = layout.get_driver();
552 controller->tick(dt);
553 float speed = controller->get_speed();
554 unsigned speed_step = find_speed_step(speed);
556 if(controller->get_reverse()!=reverse)
558 reverse = controller->get_reverse();
559 driver.set_loco_reverse(address, reverse);
561 release_blocks(cur_blocks_end, blocks.end());
562 reverse_blocks(blocks);
566 if(speed_step!=current_speed_step && !speed_changing && !driver.is_halted() && driver.get_power())
568 speed_changing = true;
569 driver.set_loco_speed(address, speed_step);
574 set_status(format("Traveling %d kmh", get_travel_speed()));
576 set_status("Waiting");
584 Vehicle &vehicle = *(reverse ? vehicles.back() : vehicles.front());
585 Track *track = vehicle.get_track();
588 for(BlockList::const_iterator i=blocks.begin(); (!ok && i!=cur_blocks_end); ++i)
589 ok = (*i)->has_track(*track);
592 if(real_speed.size()>1)
593 d = get_real_speed(current_speed_step)*(dt/Time::sec);
595 d = speed*(dt/Time::sec);
598 SetFlag setf(advancing);
599 vehicle.advance(reverse ? -d : d);
601 else if(accurate_position)
604 if(overshoot_dist>40*layout.get_catalogue().get_scale())
606 layout.emergency(name+" has not arrived at sensor");
607 accurate_position = false;
611 else if(end_of_route && cur_blocks_end==blocks.end())
614 signal_arrived.emit();
618 if(!blocks.empty() && !blocks.front()->get_sensor_id())
620 float dist = get_reserved_distance_until(&*blocks.front(), true);
622 if(dist>10*layout.get_catalogue().get_scale())
624 blocks.front()->reserve(0);
630 void Train::save(list<DataFile::Statement> &st) const
632 st.push_back((DataFile::Statement("name"), name));
634 st.push_back((DataFile::Statement("priority"), priority));
636 for(vector<Vehicle *>::const_iterator i=vehicles.begin(); i!=vehicles.end(); ++i)
637 if(i!=vehicles.begin())
638 st.push_back((DataFile::Statement("vehicle"), (*i)->get_type().get_article_number()));
640 for(unsigned i=0; i<real_speed.size(); ++i)
641 if(real_speed[i].weight)
642 st.push_back((DataFile::Statement("real_speed"), i, real_speed[i].speed, real_speed[i].weight));
644 if(!blocks.empty() && cur_blocks_end!=blocks.begin())
646 BlockList blks(blocks.begin(), BlockList::const_iterator(cur_blocks_end));
648 reverse_blocks(blks);
650 BlockIter prev = blks.front().flip();
651 st.push_back((DataFile::Statement("block_hint"), prev->get_id()));
653 for(BlockList::const_iterator i=blks.begin(); i!=blks.end(); ++i)
654 st.push_back((DataFile::Statement("block"), (*i)->get_id()));
659 list<RouteRef>::const_iterator i = routes.begin();
660 for(; (i!=routes.end() && i->route->is_temporary()); ++i) ;
662 st.push_back((DataFile::Statement("route"), i->route->get_name()));
667 DataFile::Statement ss("timetable");
668 timetable->save(ss.sub);
673 void Train::control_changed(const Controller::Control &ctrl)
675 signal_control_changed.emit(ctrl.name, ctrl.value);
678 void Train::loco_speed_event(unsigned addr, unsigned speed, bool rev)
682 current_speed_step = speed;
684 layout.get_driver().set_loco_reverse(address, reverse);
685 speed_changing = false;
690 void Train::loco_func_event(unsigned addr, unsigned func, bool state)
695 functions |= 1<<func;
697 functions &= ~(1<<func);
699 signal_function_changed.emit(func, state);
703 void Train::sensor_event(unsigned addr, bool state)
707 // Find the first sensor block from our reserved blocks that isn't this sensor
708 BlockList::iterator end;
710 for(end=cur_blocks_end; end!=blocks.end(); ++end)
711 if((*end)->get_sensor_id())
713 if((*end)->get_sensor_id()!=addr)
728 // Compute speed and update related state
729 float travel_time_secs = (Time::now()-last_entry_time)/Time::sec;
733 if(current_speed_step>0)
735 RealSpeed &rs = real_speed[current_speed_step];
736 rs.add(travel_dist/travel_time_secs, travel_time_secs);
738 set_status(format("Traveling %d kmh", get_travel_speed()));
742 for(BlockList::iterator j=cur_blocks_end; j!=end; ++j)
744 travel_dist += (*j)->get_path_length(j->entry());
746 if((*j)->get_sensor_id()==addr && !advancing)
748 TrackIter track = j->track_iter();
751 track = track.flip();
752 vehicles.back()->place(*track, track.entry(), 0, Vehicle::BACK_AXLE);
755 vehicles.front()->place(*track, track.entry(), 0, Vehicle::FRONT_AXLE);
758 last_entry_time = Time::now();
760 accurate_position = true;
763 // Check if we've reached the next route
766 const Route &route = *(++routes.begin())->route;
767 for(BlockList::iterator j=cur_blocks_end; j!=end; ++j)
768 if(route.has_track(*j->track_iter()))
772 signal_route_changed.emit(routes.front().route);
777 // Move blocks up to the next sensor to our current blocks
778 cur_blocks_end = end;
780 // Try to get more blocks if we're moving
785 layout.emergency("Sensor for "+name+" triggered out of order");
789 const Vehicle &veh = *(reverse ? vehicles.front() : vehicles.back());
791 // Find the first sensor in our current blocks that's still active
792 BlockList::iterator end = blocks.begin();
793 for(BlockList::iterator i=blocks.begin(); i!=cur_blocks_end; ++i)
795 if((*i)->has_track(*veh.get_track()))
797 if((*i)->get_sensor_id())
799 if(layout.get_driver().get_sensor((*i)->get_sensor_id()))
809 if(end!=blocks.begin() && end!=cur_blocks_end)
810 // Free blocks up to the last inactive sensor
811 release_blocks(blocks.begin(), end);
815 void Train::turnout_path_changed(Track &track)
817 for(list<BlockIter>::iterator i=blocks.begin(); i!=blocks.end(); ++i)
818 if((*i)->get_turnout_id()==track.get_turnout_id() && !reserving)
819 check_turnout_paths(false);
822 void Train::halt_event(bool h)
825 accurate_position = false;
828 void Train::block_reserved(const Block &block, const Train *train)
830 if(&block==pending_block && !train && !reserving)
834 void Train::reserve_more()
836 if(!active || blocks.empty() || end_of_route)
839 BlockIter start = blocks.back();
844 // See how many sensor blocks and how much track we already have
847 for(BlockList::const_iterator i=cur_blocks_end; i!=blocks.end(); ++i)
849 if((*i)->get_sensor_id())
852 dist += (*i)->get_path_length(i->entry());
855 list<RouteRef>::iterator cur_route = routes.begin();
856 advance_route(cur_route, *start.track_iter());
858 float approach_margin = 50*layout.get_catalogue().get_scale();
859 float min_dist = controller->get_braking_distance()*1.3+approach_margin*2;
861 BlockIter block = start;
862 list<BlockIter>::iterator good_end = blocks.end();
863 Track *divert_track = 0;
864 bool try_divert = false;
865 Train *blocking_train = 0;
866 BlockList contested_blocks;
868 SetFlag setf(reserving);
872 BlockIter last = block;
873 block = block.next(cur_route!=routes.end() ? cur_route->route : 0);
874 if(!block || block->get_endpoints().size()<2)
878 good_end = blocks.end();
884 TrackIter track = block.track_iter();
886 if(cur_route!=routes.end())
888 if(!advance_route(cur_route, *track))
890 // Keep the blocks if we arrived at the end of the route
893 good_end = blocks.end();
900 if(block->get_turnout_id() && !last->get_turnout_id())
902 /* We can keep the blocks if we arrive at a turnout from a non-turnout
903 block. Having a turnout block as our last reserved block is not good
904 as it would limit our diversion possibilities for little benefit. */
905 good_end = blocks.end();
906 if(nsens>=3 && dist>=min_dist)
912 if(block->get_train()!=blocking_train)
914 if(blocking_train->free_block(*contested_blocks.back()))
916 // Roll back and start actually reserving the blocks
917 block = blocks.back();
918 cur_route = routes.begin();
919 advance_route(cur_route, *block.track_iter().track());
920 if(blocking_train->get_priority()==priority)
921 blocking_train->yield_to(*this);
927 yield_to(*blocking_train);
928 pending_block = contested_blocks.front().block();
929 try_divert = divert_track;
935 contested_blocks.push_back(block);
940 bool reserved = block->reserve(this);
943 /* We've found another train. If it wants to exit the block from the
944 same endpoint we're trying to enter from or the other way around,
945 treat it as coming towards us. Otherwise treat it as going in the
947 Train *other_train = block->get_train();
948 int other_entry = other_train->get_entry_to_block(*block);
950 throw LogicError("Block reservation inconsistency");
952 unsigned exit = block.reverse().entry();
953 unsigned other_exit = BlockIter(block.block(), other_entry).reverse().entry();
954 bool entry_conflict = (block.entry()==other_exit);
955 bool exit_conflict = (exit==static_cast<unsigned>(other_entry));
956 if(!entry_conflict && !last->get_turnout_id())
958 /* The other train is not coming to the blocks we're holding, so we
960 good_end = blocks.end();
962 if(static_cast<unsigned>(other_entry)==block.entry())
963 preceding_train = other_train;
966 int other_prio = other_train->get_priority();
968 if(!entry_conflict && !exit_conflict && other_prio<priority)
970 /* Ask a lesser priority train going to the same direction to free
972 if(other_train->free_block(*block))
973 reserved = block->reserve(this);
975 else if(other_train!=yielding_to && (other_prio<priority || (other_prio==priority && entry_conflict)))
977 /* A lesser priority train is coming at us, we must ask it to free
978 enough blocks to get clear of it to avoid a potential deadlock */
979 blocking_train = other_train;
980 contested_blocks.clear();
981 contested_blocks.push_back(block);
984 else if(divert_track && (entry_conflict || exit_conflict || !other_train->is_active()))
985 // We are blocked, but there's a diversion possibility
990 pending_block = &*block;
995 if(block->get_turnout_id())
997 const TrackType::Endpoint &track_ep = track.endpoint();
998 bool multiple_paths = (track_ep.paths&(track_ep.paths-1));
1000 if(multiple_paths && cur_route!=routes.end() && cur_route->diversion!=block->get_turnout_id())
1001 /* There's multiple paths to be taken and we are on a route - take
1002 note of the diversion possibility */
1003 divert_track = &*track;
1006 if(!contested_blocks.empty() && contested_blocks.front()==block)
1007 contested_blocks.pop_front();
1009 blocks.push_back(block);
1011 if(cur_blocks_end==blocks.end())
1013 if(clear_blocks_end==blocks.end())
1015 if(good_end==blocks.end())
1018 if(block->get_sensor_id())
1021 dist += block->get_path_length(block.entry());
1024 // Unreserve blocks that were not good
1025 release_blocks(good_end, blocks.end());
1027 if(blocks.back()!=start)
1028 // We got some new blocks, so no longer need to yield
1031 check_turnout_paths(true);
1033 // Make any sensorless blocks at the beginning immediately current
1034 while(cur_blocks_end!=clear_blocks_end && !(*cur_blocks_end)->get_sensor_id())
1037 if(try_divert && divert(*divert_track))
1041 void Train::check_turnout_paths(bool set)
1043 if(clear_blocks_end==blocks.end())
1046 for(list<BlockIter>::iterator i=clear_blocks_end; i!=blocks.end(); ++i)
1048 if((*i)->get_turnout_id())
1050 TrackIter track = i->track_iter();
1051 const TrackType::Endpoint &track_ep = track.endpoint();
1054 list<BlockIter>::iterator j = i;
1055 if(++j!=blocks.end())
1057 TrackIter rev = j->track_iter().flip();
1058 unsigned mask = rev.endpoint().paths&track_ep.paths;
1059 for(path=0; mask>1; mask>>=1, ++path) ;
1064 if(path!=track->get_active_path())
1067 track->set_active_path(path);
1069 /* Check again, in case the driver was able to service the request
1071 if(!set || path!=track->get_active_path())
1076 if(i==clear_blocks_end)
1081 float Train::get_reserved_distance_until(const Block *until_block, bool back) const
1086 Vehicle &veh = *(reverse!=back ? vehicles.back() : vehicles.front());
1087 const VehicleType &vtype = veh.get_type();
1089 TrackIter track(veh.get_track(), veh.get_entry());
1090 if(!track) // XXX Probably unnecessary
1093 BlockList::const_iterator block = blocks.begin();
1094 while(block!=clear_blocks_end && !(*block)->has_track(*track))
1096 if(block==clear_blocks_end || &**block==until_block)
1099 float result = veh.get_offset();
1101 track = track.reverse();
1103 result = track->get_type().get_path_length(track->get_active_path())-result;
1104 result -= vtype.get_length()/2;
1108 track = track.next();
1112 if(!(*block)->has_track(*track))
1116 if(block==blocks.begin())
1123 if(block==clear_blocks_end)
1127 if(&**block==until_block)
1131 result += track->get_type().get_path_length(track->get_active_path());
1137 float Train::get_real_speed(unsigned i) const
1141 if(real_speed[i].weight)
1142 return real_speed[i].speed;
1146 for(low=i; low>0; --low)
1147 if(real_speed[low].weight)
1149 for(high=i; high+1<real_speed.size(); ++high)
1150 if(real_speed[high].weight)
1153 if(real_speed[high].weight)
1155 if(real_speed[low].weight)
1157 float f = float(i-low)/(high-low);
1158 return real_speed[low].speed*(1-f)+real_speed[high].speed*f;
1161 return real_speed[high].speed*float(i)/high;
1163 else if(real_speed[low].weight)
1164 return real_speed[low].speed*float(i)/low;
1169 unsigned Train::find_speed_step(float real) const
1171 if(real_speed.size()<=1)
1173 if(real<=real_speed[1].speed*0.5)
1179 for(unsigned i=0; (!high && i<real_speed.size()); ++i)
1180 if(real_speed[i].weight)
1183 if(real_speed[i].speed<real)
1190 unsigned limit = real_speed.size()/5;
1198 return min(min(static_cast<unsigned>(low*real/real_speed[low].speed), real_speed.size()-1), last+limit);
1201 float f = (real-real_speed[low].speed)/(real_speed[high].speed-real_speed[low].speed);
1202 return static_cast<unsigned>(low*(1-f)+high*f+0.5);
1205 float Train::get_travel_speed() const
1207 float speed = get_real_speed(current_speed_step);
1208 float scale = layout.get_catalogue().get_scale();
1209 return static_cast<int>(round(speed/scale*3.6/5))*5;
1212 void Train::set_status(const string &s)
1215 signal_status_changed.emit(s);
1218 void Train::release_blocks()
1220 release_blocks(blocks.begin(), blocks.end());
1223 void Train::release_blocks(BlockList::iterator begin, BlockList::iterator end)
1227 if(begin==cur_blocks_end)
1228 cur_blocks_end = end;
1229 if(begin==clear_blocks_end)
1230 clear_blocks_end = end;
1232 Block &block = **begin;
1233 blocks.erase(begin++);
1236 if(begin==blocks.end())
1237 end_of_route = false;
1241 void Train::reverse_blocks(BlockList &blks) const
1244 for(BlockList::iterator i=blks.begin(); i!=blks.end(); ++i)
1248 bool Train::advance_route(list<RouteRef>::iterator &iter, Track &track)
1250 while(iter!=routes.end() && !iter->route->has_track(track))
1252 if(iter==routes.end())
1255 list<RouteRef>::iterator next = iter;
1257 if(next!=routes.end() && next->diversion && next->route->has_track(track))
1263 Route *Train::create_lead_route(Route *lead, const Route *target)
1267 lead = new Route(layout);
1268 lead->set_name("Lead");
1269 lead->set_temporary(true);
1272 set<Track *> tracks;
1273 for(BlockList::iterator i=blocks.begin(); i!=blocks.end(); ++i)
1275 const set<Track *> &btracks = (*i)->get_tracks();
1276 for(set<Track *>::const_iterator j=btracks.begin(); j!=btracks.end(); ++j)
1277 if(!target || !target->has_track(**j))
1281 lead->add_tracks(tracks);
1286 bool Train::is_valid_diversion(const Route &diversion, const TrackIter &from)
1288 float diversion_len = 0;
1289 TrackLoopIter track1 = from;
1290 while(diversion.has_track(*track1))
1292 unsigned path = diversion.get_path(*track1);
1293 diversion_len += track1->get_type().get_path_length(path);
1295 track1 = track1.next(path);
1301 list<RouteRef>::iterator route = routes.begin();
1302 if(!advance_route(route, *from))
1305 float route_len = 0;
1306 TrackLoopIter track2 = from;
1309 unsigned path = route->route->get_path(*track2);
1310 route_len += track2->get_type().get_path_length(path);
1312 bool ok = (track2!=from && diversion.has_track(*track2));
1314 track2 = track2.next(path);
1322 if(!advance_route(route, *track2))
1326 // Must end up at the same place through both routes
1330 return diversion_len<route_len*1.2;
1334 Train::RouteRef::RouteRef(const Route *r, unsigned d):
1340 Train::RealSpeed::RealSpeed():
1345 void Train::RealSpeed::add(float s, float w)
1347 speed = (speed*weight+s*w)/(weight+w);
1348 weight = min(weight+w, 300.0f);
1352 Train::Loader::Loader(Train &t):
1353 DataFile::BasicLoader<Train>(t),
1357 add("block", &Loader::block);
1358 add("block_hint", &Loader::block_hint);
1359 add("name", &Loader::name);
1360 add("priority", &Train::priority);
1361 add("real_speed", &Loader::real_speed);
1362 add("route", &Loader::route);
1363 add("timetable", &Loader::timetable);
1364 add("vehicle", &Loader::vehicle);
1367 void Train::Loader::finish()
1369 if(!obj.blocks.empty())
1371 TrackIter track = obj.blocks.front().track_iter();
1372 float offset = 2*obj.layout.get_catalogue().get_scale();
1373 obj.vehicles.back()->place(*track, track.entry(), offset, Vehicle::BACK_BUFFER);
1375 obj.set_status("Stopped");
1379 void Train::Loader::block(unsigned id)
1387 blk = &obj.layout.get_block(id);
1389 catch(const KeyError &)
1391 blocks_valid = false;
1397 entry = blk->get_endpoint_by_link(*prev_block);
1402 obj.blocks.push_back(BlockIter(blk, entry));
1404 if(blk->get_sensor_id())
1405 obj.layout.get_driver().set_sensor(blk->get_sensor_id(), true);
1410 void Train::Loader::block_hint(unsigned id)
1414 prev_block = &obj.layout.get_block(id);
1416 catch(const KeyError &)
1418 blocks_valid = false;
1422 void Train::Loader::name(const string &n)
1427 void Train::Loader::real_speed(unsigned i, float speed, float weight)
1429 if(i>=obj.real_speed.size())
1431 obj.real_speed[i].speed = speed;
1432 obj.real_speed[i].weight = weight;
1435 void Train::Loader::route(const string &n)
1437 obj.set_route(&obj.layout.get_route(n));
1440 void Train::Loader::timetable()
1443 throw InvalidState("A timetable has already been loaded");
1445 obj.timetable = new Timetable(obj);
1446 load_sub(*obj.timetable);
1449 void Train::Loader::vehicle(ArticleNumber art_nr)
1451 const VehicleType &vtype = obj.layout.get_catalogue().get_vehicle(art_nr);
1452 Vehicle *veh = new Vehicle(obj.layout, vtype);
1453 obj.vehicles.back()->attach_back(*veh);
1454 obj.vehicles.push_back(veh);