]> git.tdb.fi Git - r2c2.git/blobdiff - source/libmarklin/train.cpp
Support taking trains off the tracks
[r2c2.git] / source / libmarklin / train.cpp
index 0c1748ac7d4286a2d0f220fdbaa3d0a4c8bf259a..d6ed044954286ba549c0da0c0e75d363e300861e 100644 (file)
@@ -288,17 +288,38 @@ void Train::place(Block &block, unsigned entry)
                const Block::Endpoint &bep = block.get_endpoints()[exit];
                Track *track = bep.track->get_link(bep.track_ep);
                unsigned ep = track->get_endpoint_by_link(*bep.track);
-               vehicles.front()->place(track, ep, 0, Vehicle::FRONT_BUFFER);
+               vehicles.front()->place(*track, ep, 0, Vehicle::FRONT_BUFFER);
        }
        else
        {
                const Block::Endpoint &bep = block.get_endpoints()[entry];
-               vehicles.back()->place(bep.track, bep.track_ep, 0, Vehicle::BACK_BUFFER);
+               vehicles.back()->place(*bep.track, bep.track_ep, 0, Vehicle::BACK_BUFFER);
        }
 }
 
+void Train::unplace()
+{
+       if(controller->get_speed())
+               throw InvalidState("Must be stopped before unplacing");
+
+       release_blocks(rsv_blocks);
+       release_blocks(cur_blocks);
+
+       set_active(false);
+       accurate_position = false;
+
+       for(vector<Vehicle *>::iterator i=vehicles.begin(); i!=vehicles.end(); ++i)
+               (*i)->unplace();
+
+       set_status("Unplaced");
+}
+
 bool Train::free_block(Block &block)
 {
+       float margin = 10*layout.get_catalogue().get_scale();
+       if(get_reserved_distance_until(&block, false)<controller->get_braking_distance()*1.3+margin)
+               return false;
+
        unsigned nsens = 0;
        for(list<BlockRef>::iterator i=rsv_blocks.begin(); i!=rsv_blocks.end(); ++i)
        {
@@ -329,52 +350,7 @@ int Train::get_entry_to_block(Block &block) const
 
 float Train::get_reserved_distance() const
 {
-       if(cur_blocks.empty())
-               return 0;
-
-       Vehicle &veh = *(reverse ? vehicles.back() : vehicles.front());
-       const VehicleType &vtype = veh.get_type();
-
-       Track *track = veh.get_track();
-       if(!track)
-               return 0;
-       unsigned entry = veh.get_entry();
-
-       float result = -vtype.get_length()/2;
-       if(reverse)
-       {
-               entry = track->traverse(entry);
-               result += veh.get_offset();
-       }
-       else
-               result -= veh.get_offset();
-
-       bool first = true;
-       list<BlockRef>::const_iterator block = cur_blocks.begin();
-       while(1)
-       {
-               if(!first || !reverse)
-                       result += track->get_type().get_path_length(track->get_active_path());
-               first = false;
-
-               if(track->get_type().get_endpoints().size()<2)
-                       return result;
-
-               unsigned exit = track->traverse(entry);
-               Track *next = track->get_link(exit);
-
-               while(!block->block->get_tracks().count(next))
-               {
-                       ++block;
-                       if(block==cur_blocks.end())
-                               block = rsv_blocks.begin();
-                       if(block==rsv_blocks.end())
-                               return result;
-               }
-
-               entry = next->get_endpoint_by_link(*track);
-               track = next;
-       }
+       return get_reserved_distance_until(0, false);
 }
 
 void Train::tick(const Time::TimeStamp &t, const Time::TimeDelta &dt)
@@ -453,47 +429,9 @@ void Train::tick(const Time::TimeStamp &t, const Time::TimeDelta &dt)
 
        if(!cur_blocks.empty() && !cur_blocks.front().block->get_sensor_id())
        {
-               Vehicle &veh = *(reverse ? vehicles.front() : vehicles.back());
-
-               list<BlockRef>::iterator i = cur_blocks.begin();
-               const Block::Endpoint &bep = i->block->get_endpoints()[i->entry];
-
-               Track *track = bep.track;
-               unsigned entry = bep.track_ep;
-
-               bool found = false;
-               float dist = veh.get_offset();
-               if(reverse)
-                       dist = veh.get_track()->get_type().get_path_length(veh.get_track()->get_active_path())-dist;
-               dist -= veh.get_type().get_length()/2;
-               while(1)
-               {
-                       if(track==veh.get_track())
-                       {
-                               found = true;
-                               break;
-                       }
-
-                       if(i!=cur_blocks.begin())
-                       {
-                               float path_len = track->get_type().get_path_length(track->get_active_path());
-                               dist += path_len;
-                       }
-
-                       unsigned exit = track->traverse(entry);
-                       Track *next = track->get_link(exit);
-                       entry = next->get_endpoint_by_link(*track);
-                       track = next;
-
-                       if(!i->block->get_tracks().count(track))
-                       {
-                               ++i;
-                               if(i==cur_blocks.end())
-                                       break;
-                       }
-               }
+               float dist = get_reserved_distance_until(cur_blocks.front().block, true);
 
-               if(found && i!=cur_blocks.begin() && dist>10*layout.get_catalogue().get_scale())
+               if(dist>10*layout.get_catalogue().get_scale())
                {
                        cur_blocks.front().block->reserve(0);
                        cur_blocks.erase(cur_blocks.begin());
@@ -626,10 +564,10 @@ void Train::sensor_event(unsigned addr, bool state)
                                        {
                                                Track *track = bep.track->get_link(bep.track_ep);
                                                unsigned ep = track->get_endpoint_by_link(*bep.track);
-                                               vehicles.back()->place(track, ep, 0, Vehicle::BACK_AXLE);
+                                               vehicles.back()->place(*track, ep, 0, Vehicle::BACK_AXLE);
                                        }
                                        else
-                                               vehicles.front()->place(bep.track, bep.track_ep, 0, Vehicle::FRONT_AXLE);
+                                               vehicles.front()->place(*bep.track, bep.track_ep, 0, Vehicle::FRONT_AXLE);
                                }
                        }
                        last_entry_time = Time::now();
@@ -664,9 +602,14 @@ void Train::sensor_event(unsigned addr, bool state)
        }
        else
        {
+               const Vehicle &veh = *(reverse ? vehicles.front() : vehicles.back());
+
                // Find the first sensor in our current blocks that's still active
                list<BlockRef>::iterator end = cur_blocks.begin();
                for(list<BlockRef>::iterator i=cur_blocks.begin(); i!=cur_blocks.end(); ++i)
+               {
+                       if(i->block->get_tracks().count(veh.get_track()))
+                               break;
                        if(i->block->get_sensor_id())
                        {
                                if(layout.get_driver().get_sensor(i->block->get_sensor_id()))
@@ -677,6 +620,7 @@ void Train::sensor_event(unsigned addr, bool state)
                                        ++end;
                                }
                        }
+               }
                
                if(end!=cur_blocks.begin() && end!=cur_blocks.end())
                        // Free blocks up to the last inactive sensor
@@ -727,11 +671,20 @@ unsigned Train::reserve_more()
 
        pending_block = 0;
 
-       // See how many sensor blocks we already have
+       // See how many sensor blocks and how much track we already have
        unsigned nsens = 0;
+       float dist = 0;
        for(list<BlockRef>::const_iterator i=rsv_blocks.begin(); i!=rsv_blocks.end(); ++i)
+       {
                if(i->block->get_sensor_id())
                        ++nsens;
+               if(nsens>0)
+               {
+                       float length = 0;
+                       i->block->traverse(i->entry, &length);
+                       dist += length;
+               }
+       }
        
        if(end_of_route)
                return nsens;
@@ -749,17 +702,23 @@ unsigned Train::reserve_more()
                }
        }
 
-       SetFlag setf(reserving);
+       float approach_margin = 50*layout.get_catalogue().get_scale();
+       float min_dist = controller->get_braking_distance()*1.3+approach_margin*2;
 
        BlockRef *last = start;
        BlockRef *good = start;
        unsigned good_sens = nsens;
+       float good_dist = dist;
        Train *blocking_train = 0;
        std::list<BlockRef> contested_blocks;
-       while(good_sens<3 || !contested_blocks.empty())
+
+       SetFlag setf(reserving);
+
+       while(good_sens<3 || good_dist<min_dist || !contested_blocks.empty())
        {
                // Traverse to the next block
-               unsigned exit = last->block->traverse(last->entry, cur_route);
+               float length = 0;
+               unsigned exit = last->block->traverse(last->entry, cur_route, &length);
                Block *link = last->block->get_link(exit);
                if(!link)
                        break;
@@ -781,6 +740,7 @@ unsigned Train::reserve_more()
                                {
                                        good = last;
                                        good_sens = nsens;
+                                       good_dist = dist;
                                        end_of_route = true;
                                }
                                break;
@@ -795,6 +755,7 @@ unsigned Train::reserve_more()
                        {
                                good = last;
                                good_sens = nsens;
+                               good_dist = dist;
                        }
                        break;
                }
@@ -849,6 +810,7 @@ unsigned Train::reserve_more()
                                other train to pass */
                                good = last;
                                good_sens = nsens;
+                               good_dist = dist;
 
                                // Ask a lesser priority train to free the block for us
                                if(other_train->get_priority()<priority)
@@ -880,6 +842,7 @@ unsigned Train::reserve_more()
                        // Keep the blocks reserved so far, as either us or the other train can diverge
                        good = last;
                        good_sens = nsens;
+                       good_dist = dist;
 
                        // Figure out what path we'd like to take on the turnout
                        int path = -1;
@@ -914,6 +877,8 @@ unsigned Train::reserve_more()
                last = &rsv_blocks.back();
                if(last->block->get_sensor_id())
                        ++nsens;
+               if(nsens>0)
+                       dist += length;
        }
 
        // Unreserve blocks that were not good
@@ -936,6 +901,78 @@ unsigned Train::reserve_more()
        return good_sens;
 }
 
+float Train::get_reserved_distance_until(const Block *until_block, bool back) const
+{
+       if(cur_blocks.empty())
+               return 0;
+
+       Vehicle &veh = *(reverse!=back ? vehicles.back() : vehicles.front());
+       const VehicleType &vtype = veh.get_type();
+
+       Track *track = veh.get_track();
+       if(!track)
+               return 0;
+
+       list<BlockRef>::const_iterator block = cur_blocks.begin();
+       while(block!=rsv_blocks.end() && !block->block->get_tracks().count(track))
+       {
+               ++block;
+               if(block==cur_blocks.end())
+               {
+                       if(back)
+                               return 0;
+                       block = rsv_blocks.begin();
+               }
+       }
+       if(block==rsv_blocks.end() || block->block==until_block)
+               return 0;
+
+       unsigned entry = veh.get_entry();
+
+       float result = veh.get_offset();
+       if(reverse!=back)
+               entry = track->traverse(entry);
+       else
+               result = track->get_type().get_path_length(track->get_active_path())-result;
+       result -= vtype.get_length()/2;
+
+       while(1)
+       {
+               if(track->get_type().get_endpoints().size()<2)
+                       break;
+
+               Track *next = track->get_link(track->traverse(entry));
+
+               if(!block->block->get_tracks().count(next))
+               {
+                       if(back)
+                       {
+                               if(block==cur_blocks.begin())
+                                       break;
+                               --block;
+                       }
+                       else
+                       {
+                               ++block;
+                               if(block==cur_blocks.end())
+                                       block = rsv_blocks.begin();
+                               if(block==rsv_blocks.end())
+                                       break;
+                       }
+
+                       if(block->block==until_block)
+                               break;
+               }
+
+               entry = next->get_endpoint_by_link(*track);
+               track = next;
+
+               result += track->get_type().get_path_length(track->get_active_path());
+       }
+
+       return result;
+}
+
 float Train::get_real_speed(unsigned i) const
 {
        if(real_speed[i].weight)
@@ -973,9 +1010,11 @@ unsigned Train::find_speed(float real) const
 
        unsigned low = 0;
        unsigned high = 0;
+       unsigned last = 0;
        for(unsigned i=0; (!high && i<=14); ++i)
                if(real_speed[i].weight)
                {
+                       last = i;
                        if(real_speed[i].speed<real)
                                low = i;
                        else
@@ -990,7 +1029,7 @@ unsigned Train::find_speed(float real) const
                        else
                                return 0;
                }
-               return min(static_cast<unsigned>(low*real/real_speed[low].speed), 14U);
+               return min(min(static_cast<unsigned>(low*real/real_speed[low].speed), 14U), last+3);
        }
 
        float f = (real-real_speed[low].speed)/(real_speed[high].speed-real_speed[low].speed);
@@ -1085,7 +1124,7 @@ void Train::Loader::finish()
        {
                const BlockRef &blkref = obj.cur_blocks.front();
                const Block::Endpoint &bep = blkref.block->get_endpoints()[blkref.entry];
-               obj.vehicles.back()->place(bep.track, bep.track_ep, 0, Vehicle::BACK_BUFFER);
+               obj.vehicles.back()->place(*bep.track, bep.track_ep, 0, Vehicle::BACK_BUFFER);
 
                obj.set_status("Stopped");
        }