]> git.tdb.fi Git - r2c2.git/blobdiff - source/libr2c2/trainrouteplanner.cpp
Allow direction to be specified for routing waypoints
[r2c2.git] / source / libr2c2 / trainrouteplanner.cpp
index 8126ec5a48270ab65f224cd4f7fb2ee91869e034..75d746b3c6dd103cd148ae233f90361a189c2328 100644 (file)
@@ -1,3 +1,5 @@
+#include <msp/core/maputils.h>
+#include <msp/time/utils.h>
 #include "catalogue.h"
 #include "layout.h"
 #include "route.h"
@@ -12,49 +14,133 @@ using namespace Msp;
 
 namespace R2C2 {
 
-TrainRoutePlanner::TrainRoutePlanner(Layout &layout)
+TrainRoutePlanner::TrainRoutePlanner(Layout &layout):
+       goal(0),
+       timeout(10*Time::sec),
+       result(PENDING),
+       thread(0)
 {
        const map<unsigned, Train *> &trains = layout.get_trains();
        for(map<unsigned, Train *>::const_iterator i=trains.begin(); i!=trains.end(); ++i)
        {
                TrainRoutingInfo info(*i->second);
-               if(info.router && info.router->get_destination())
+               if(!info.waypoints.empty())
                        routed_trains.push_back(info);
        }
 }
 
-void TrainRoutePlanner::plan()
+TrainRoutePlanner::~TrainRoutePlanner()
+{
+       if(thread)
+       {
+               thread->join();
+               delete thread;
+       }
+}
+
+void TrainRoutePlanner::set_timeout(const Time::TimeDelta &t)
+{
+       timeout = t;
+}
+
+TrainRoutePlanner::Result TrainRoutePlanner::plan()
+{
+       prepare_plan();
+       create_plan();
+       if(result==PENDING)
+               finalize_plan();
+
+       return result;
+}
+
+void TrainRoutePlanner::plan_async()
+{
+       if(thread)
+               throw logic_error("already planning");
+
+       prepare_plan();
+       thread = new PlanningThread(*this);
+}
+
+TrainRoutePlanner::Result TrainRoutePlanner::check()
+{
+       if(result==PENDING && goal)
+       {
+               if(thread)
+               {
+                       thread->join();
+                       delete thread;
+                       thread = 0;
+               }
+               finalize_plan();
+       }
+
+       return result;
+}
+
+const list<Route *> &TrainRoutePlanner::get_routes_for(const Train &train) const
+{
+       return get_train_info(train).routes;
+}
+
+const list<TrainRouter::SequencePoint> &TrainRoutePlanner::get_sequence_for(const Train &train) const
+{
+       return get_train_info(train).sequence;
+}
+
+const TrainRoutePlanner::TrainRoutingInfo &TrainRoutePlanner::get_train_info(const Train &train) const
+{
+       for(vector<TrainRoutingInfo>::const_iterator i=routed_trains.begin(); i!=routed_trains.end(); ++i)
+               if(i->train==&train)
+                       return *i;
+
+       throw key_error(train.get_name());
+}
+
+const TrainRoutePlanner::RoutingStep &TrainRoutePlanner::get_step()
+{
+       steps.splice(steps.end(), queue, queue.begin());
+       return steps.back();
+}
+
+void TrainRoutePlanner::prepare_plan()
 {
        steps.clear();
        queue.clear();
+       goal = 0;
+       result = PENDING;
 
        queue.push_back(RoutingStep());
        RoutingStep &start = queue.back();
        for(vector<TrainRoutingInfo>::iterator i=routed_trains.begin(); i!=routed_trains.end(); ++i)
                start.trains.push_back(TrainRoutingState(*i));
        start.update_estimate();
+}
 
-       const RoutingStep *goal = 0;
+void TrainRoutePlanner::create_plan()
+{
+       Time::TimeStamp timeout_stamp = Time::now()+timeout;
+       unsigned count = 0;
        while(!queue.empty())
        {
                const RoutingStep &step = get_step();
                if(step.is_goal())
                {
                        goal = &step;
-                       break;
+                       return;
                }
 
                add_steps(step);
-       }
 
-       if(goal)
-               create_routes(*goal);
-}
+               if(++count>=1000)
+               {
+                       if(Time::now()>timeout_stamp)
+                               break;
+                       count = 0;
+               }
+       }
 
-const TrainRoutePlanner::RoutingStep &TrainRoutePlanner::get_step()
-{
-       steps.splice(steps.end(), queue, queue.begin());
-       return steps.back();
+       result = FAILED;
 }
 
 void TrainRoutePlanner::add_steps(const RoutingStep &step)
@@ -65,19 +151,19 @@ void TrainRoutePlanner::add_steps(const RoutingStep &step)
        queue.merge(new_steps);
 }
 
-void TrainRoutePlanner::create_routes(const RoutingStep &goal)
+void TrainRoutePlanner::finalize_plan()
 {
        for(vector<TrainRoutingInfo>::iterator i=routed_trains.begin(); i!=routed_trains.end(); ++i)
        {
                i->routes.clear();
                i->sequence.clear();
-               for(unsigned j=0; j<3; ++j)
+               for(unsigned j=0; j<2; ++j)
                        i->track_history[j] = 0;
        }
 
-       map<Track *, SequencingInfo *> sequenced_tracks;
+       map<Track *, TrainRouter::SequencePoint *> sequenced_tracks;
        unsigned sequence = steps.size();
-       for(const RoutingStep *i=&goal; i; i=i->prev)
+       for(const RoutingStep *i=goal; i; i=i->prev)
                for(vector<TrainRoutingState>::const_iterator j=i->trains.begin(); j!=i->trains.end(); ++j)
                {
                        Track **history = j->info->track_history;
@@ -106,74 +192,60 @@ void TrainRoutePlanner::create_routes(const RoutingStep &goal)
                                route = new Route(j->info->train->get_layout());
                                route->set_name("Router");
                                route->set_temporary(true);
-                               for(unsigned k=2; k>0; --k)
-                                       if(history[k])
-                                               route->add_track(*history[k]);
+                               for(unsigned k=0; (k<2 && history[k]); ++k)
+                                       route->add_track(*history[k]);
                                j->info->routes.push_front(route);
                        }
 
-                       if(history[0])
-                               route->add_track(*history[0]);
-                       for(unsigned k=2; k>0; --k)
-                               history[k] = history[k-1];
+                       route->add_track(*j->track.track());
+                       history[1] = history[0];
                        history[0] = j->track.track();
 
                        bool waitable = j->track.endpoint().paths!=j->track->get_type().get_paths();
-                       map<Track *, SequencingInfo *>::iterator k = sequenced_tracks.find(j->track.track());
+                       map<Track *, TrainRouter::SequencePoint *>::iterator k = sequenced_tracks.find(j->track.track());
                        if(k!=sequenced_tracks.end())
                        {
-                               if(!k->second->preceding)
+                               if(!k->second->preceding_train)
                                {
-                                       k->second->preceding = j->info;
+                                       k->second->preceding_train = j->info->train;
                                        k->second->sequence_in = sequence;
                                }
-                               j->info->sequence.push_front(SequencingInfo(j->track.track(), sequence));
+                               j->info->sequence.push_front(TrainRouter::SequencePoint(j->track->get_block(), sequence));
                                if(waitable)
                                        k->second = &j->info->sequence.front();
                                --sequence;
                        }
                        else if(waitable)
                        {
-                               j->info->sequence.push_front(SequencingInfo(j->track.track(), sequence));
+                               j->info->sequence.push_front(TrainRouter::SequencePoint(j->track->get_block(), sequence));
                                sequenced_tracks[j->track.track()] = &j->info->sequence.front();
                                --sequence;
                        }
                }
 
-       for(vector<TrainRoutingInfo>::iterator i=routed_trains.begin(); i!=routed_trains.end(); ++i)
-       {
-               for(list<Route *>::iterator j=i->routes.begin(); j!=i->routes.end(); ++j)
-               {
-                       if(j==i->routes.begin())
-                               i->router->set_route(*j);
-                       else
-                               i->router->add_route(**j);
-               }
-
-               for(list<SequencingInfo>::iterator j=i->sequence.begin(); j!=i->sequence.end(); ++j)
-               {
-                       if(j->preceding && j->preceding!=&*i)
-                               i->router->add_sequence_point(j->track->get_block(), *j->preceding->train, j->sequence_in, j->sequence_out);
-                       else
-                               i->router->add_sequence_point(j->track->get_block(), j->sequence_out);
-               }
-       }
+       result = COMPLETE;
 }
 
 
-TrainRoutePlanner::SequencingInfo::SequencingInfo(Track *t, unsigned o):
-       track(t),
-       preceding(0),
-       sequence_in(0),
-       sequence_out(o)
-{ }
-
-
 TrainRoutePlanner::TrainRoutingInfo::TrainRoutingInfo(Train &t):
        train(&t),
        speed(train->get_maximum_speed()),
-       router(train->get_ai_of_type<TrainRouter>())
+       first_noncritical(train->get_last_critical_block().next().block()),
+       router(train->get_ai_of_type<TrainRouter>()),
+       has_duration(false)
 {
+       if(unsigned n_wps = router->get_n_waypoints())
+       {
+               waypoints.reserve(n_wps),
+               metrics.reserve(n_wps);
+               for(unsigned i=0; i<n_wps; ++i)
+               {
+                       waypoints.push_back(router->get_waypoint(i));
+                       metrics.push_back(&router->get_metric(i));
+               }
+               has_duration = router->get_trip_duration();
+       }
+
        // If no maximum speed is specified, use a sensible default
        if(!speed)
                speed = 20*train->get_layout().get_catalogue().get_scale();
@@ -211,10 +283,12 @@ TrainRoutePlanner::OccupiedTrack::~OccupiedTrack()
 
 TrainRoutePlanner::TrainRoutingState::TrainRoutingState(TrainRoutingInfo &inf):
        info(&inf),
+       critical(true),
        occupied_tracks(0),
        state(MOVING),
        delay(info->router->get_departure_delay()),
-       waypoint(info->router->get_n_waypoints() ? 0 : -1),
+       duration(info->router->get_trip_duration()),
+       waypoint(0),
        blocked_by(-1)
 {
        const Vehicle *veh = &info->train->get_vehicle(0);
@@ -245,11 +319,13 @@ TrainRoutePlanner::TrainRoutingState::TrainRoutingState(const TrainRoutingState
        info(other.info),
        track(other.track),
        path(other.path),
+       critical(other.critical),
        occupied_tracks(other.occupied_tracks),
        offset(other.offset),
        back_offset(other.back_offset),
        state(other.state),
        delay(other.delay),
+       duration(other.duration),
        waypoint(other.waypoint),
        distance_traveled(other.distance_traveled),
        remaining_estimate(other.remaining_estimate),
@@ -272,6 +348,9 @@ Time::TimeDelta TrainRoutePlanner::TrainRoutingState::get_time_to_next_track() c
 
 bool TrainRoutePlanner::TrainRoutingState::is_occupying(Track &trk) const
 {
+       if(state==ARRIVED && !duration && info->has_duration)
+               return false;
+
        OccupiedTrack *occ = occupied_tracks;
        for(unsigned n=occ->n_tracks; n>0; --n, occ=occ->next)
                if(occ->track==&trk)
@@ -281,20 +360,23 @@ bool TrainRoutePlanner::TrainRoutingState::is_occupying(Track &trk) const
 
 bool TrainRoutePlanner::TrainRoutingState::check_arrival()
 {
-       TrainRouter &router = *info->router;
        TrackIter next_track = track.next(path);
 
-       if(waypoint<0 && router.is_destination(*track) && !router.is_destination(*next_track))
-       {
-               state = ARRIVED;
-               return true;
-       }
-       else if(waypoint>=0 && router.is_waypoint(waypoint, *track) && !router.is_waypoint(waypoint, *next_track))
-       {
-               ++waypoint;
-               if(waypoint>=static_cast<int>(router.get_n_waypoints()))
-                       waypoint = -1;
-       }
+       const TrainRouter::Waypoint &wp = info->waypoints[waypoint];
+       if(wp.chain->has_track(*track) && !wp.chain->has_track(*next_track))
+               if(wp.direction==TrackChain::UNSPECIFIED || track==wp.chain->iter_for(*track, wp.direction))
+               {
+                       if(waypoint+1<info->waypoints.size())
+                               ++waypoint;
+                       else
+                       {
+                               state = ARRIVED;
+                               return true;
+                       }
+               }
+
+       if(info->first_noncritical->has_track(*track))
+               critical = false;
 
        return false;
 }
@@ -304,20 +386,37 @@ void TrainRoutePlanner::TrainRoutingState::advance(float distance)
        offset += distance;
        back_offset += distance;
 
-       OccupiedTrack *last_occ = occupied_tracks;
-       for(unsigned n=occupied_tracks->n_tracks; n>1; --n)
-               last_occ = last_occ->next;
+       unsigned count_to_free = 0;
+       unsigned last_sensor_addr = 0;
+       float distance_after_sensor = 0;
+       OccupiedTrack *occ = occupied_tracks;
+       for(unsigned n=occupied_tracks->n_tracks; n>0; --n)
+       {
+               if(unsigned saddr = occ->track->get_sensor_address())
+               {
+                       if(saddr!=last_sensor_addr)
+                       {
+                               count_to_free = 0;
+                               distance_after_sensor = 0;
+                       }
+                       last_sensor_addr = saddr;
+               }
 
-       // XXX What if there's multiple tracks to remove?
-       if(back_offset>last_occ->path_length)
+               ++count_to_free;
+               distance_after_sensor += occ->path_length;
+
+               occ = occ->next;
+       }
+
+       if(count_to_free && back_offset>distance_after_sensor)
        {
-               back_offset -= last_occ->path_length;
+               back_offset -= distance_after_sensor;
                if(occupied_tracks->refcount>1)
                {
                        --occupied_tracks->refcount;
                        occupied_tracks = new OccupiedTrack(*occupied_tracks);
                }
-               --occupied_tracks->n_tracks;
+               occupied_tracks->n_tracks -= count_to_free;
        }
 
        distance_traveled += distance;
@@ -339,6 +438,9 @@ void TrainRoutePlanner::TrainRoutingState::advance(const Time::TimeDelta &dt)
                delay = Time::zero;
        }
 
+       if(duration)
+               duration = max(duration-secs*Time::sec, Time::zero);
+
        if(state==MOVING)
                advance(info->speed*secs);
        else if(state!=ARRIVED)
@@ -358,9 +460,18 @@ void TrainRoutePlanner::TrainRoutingState::advance_track(unsigned next_path)
 void TrainRoutePlanner::TrainRoutingState::update_estimate()
 {
        TrackIter iter = track.reverse(path);
-       float distance = info->router->get_metric(waypoint).get_distance_from(*iter.track(), iter.entry());
-       distance += track->get_type().get_path_length(path)-offset;
-       remaining_estimate = distance;
+       remaining_estimate = info->metrics[waypoint]->get_distance_from(*iter.track(), iter.entry());
+       if(remaining_estimate>=0)
+               remaining_estimate += track->get_type().get_path_length(path)-offset;
+}
+
+bool TrainRoutePlanner::TrainRoutingState::is_viable() const
+{
+       if(remaining_estimate<0)
+               return false;
+       if(critical && state==BLOCKED)
+               return false;
+       return true;
 }
 
 
@@ -370,6 +481,7 @@ TrainRoutePlanner::RoutingStep::RoutingStep():
 
 TrainRoutePlanner::RoutingStep::RoutingStep(const RoutingStep *p):
        time(p->time),
+       penalty(p->penalty),
        cost_estimate(p->cost_estimate),
        trains(p->trains),
        prev(p)
@@ -402,25 +514,44 @@ void TrainRoutePlanner::RoutingStep::create_successors(list<RoutingStep> &new_st
                return;
        }
 
-       TrackIter next_track = train.track.next(train.path);
        train.advance_track(0);
 
-       const TrackType::Endpoint &next_entry_ep = next_track.endpoint();
-       for(unsigned i=0; next_entry_ep.paths>>i; ++i)
-               if(next_entry_ep.has_path(i))
-               {
-                       train.path = i;
-                       train.update_estimate();
-                       next.update_estimate();
-                       if(next.is_viable())
-                               new_steps.push_back(next);
-               }
+       const TrackType::Endpoint &entry_ep = train.track.endpoint();
+       if(train.critical)
+       {
+               train.path = train.track->get_type().coerce_path(train.track.entry(), train.track->get_active_path());
+               train.update_estimate();
+               next.update_estimate();
+               if(next.is_viable())
+                       new_steps.push_back(next);
+       }
+       else
+       {
+               for(unsigned i=0; entry_ep.paths>>i; ++i)
+                       if(entry_ep.has_path(i))
+                       {
+                               train.path = i;
+                               train.update_estimate();
+                               next.update_estimate();
+                               if(next.is_viable())
+                                       new_steps.push_back(next);
+                       }
+       }
 
-       if(next_entry_ep.paths!=next_track->get_type().get_paths())
+       new_steps.sort();
+       for(list<RoutingStep>::iterator i=new_steps.begin(); ++i!=new_steps.end(); )
+       {
+               i->penalty += 5*Time::sec;
+               i->update_estimate();
+       }
+
+       if(entry_ep.paths!=train.track->get_type().get_paths() && !train.critical)
        {
                RoutingStep wait(this);
                wait.advance(dt);
                wait.trains[train_index].state = WAITING;
+               wait.penalty += 15*Time::sec;
+               wait.update_estimate();
                if(wait.is_viable())
                        new_steps.push_back(wait);
        }
@@ -441,7 +572,11 @@ bool TrainRoutePlanner::RoutingStep::update_states()
                {
                        i->blocked_by = get_occupant(*next_track);
                        if(i->blocked_by>=0)
+                       {
+                               if(i->info->first_noncritical->has_track(*next_track))
+                                       i->critical = false;
                                i->state = BLOCKED;
+                       }
                        else if(i->state==BLOCKED)
                                i->state = MOVING;
                }
@@ -516,7 +651,7 @@ void TrainRoutePlanner::RoutingStep::advance(const Time::TimeDelta &dt)
 
 void TrainRoutePlanner::RoutingStep::update_estimate()
 {
-       cost_estimate = Time::zero;
+       cost_estimate = penalty;
        for(vector<TrainRoutingState>::const_iterator i=trains.begin(); i!=trains.end(); ++i)
                if(i->remaining_estimate>=0)
                        cost_estimate += i->wait_time+((i->distance_traveled+i->remaining_estimate)/i->info->speed)*Time::sec;
@@ -525,7 +660,7 @@ void TrainRoutePlanner::RoutingStep::update_estimate()
 bool TrainRoutePlanner::RoutingStep::is_viable() const
 {
        for(vector<TrainRoutingState>::const_iterator i=trains.begin(); i!=trains.end(); ++i)
-               if(i->remaining_estimate<0)
+               if(!i->is_viable())
                        return false;
 
        for(vector<TrainRoutingState>::const_iterator i=trains.begin(); i!=trains.end(); ++i)
@@ -548,4 +683,16 @@ bool TrainRoutePlanner::RoutingStep::operator<(const RoutingStep &other) const
        return cost_estimate<other.cost_estimate;
 }
 
+
+TrainRoutePlanner::PlanningThread::PlanningThread(TrainRoutePlanner &p):
+       planner(p)
+{
+       launch();
+}
+
+void TrainRoutePlanner::PlanningThread::main()
+{
+       planner.create_plan();
+}
+
 } // namespace R2C2