Mikko Rasa [Thu, 12 Feb 2015 14:46:45 +0000 (16:46 +0200)]
Fix critical block logic
First noncritical block is not reliable, as it could be outside of the
train's allocated blocks and not guaranteed to set to a consistent path.
Worse, the get_first_noncritical_block function could return a block on
the other side of a turnout which is changing path, and is_block_critical
used completely different logic.
Mikko Rasa [Wed, 11 Feb 2015 18:14:30 +0000 (20:14 +0200)]
Avoid arriving too early if there's an unexpected stop on the last route
If the final block of the route is allocated while the train is stopped,
arrival could be triggered prematurely. I'm not entirely certain if this
can happen in absence of certain quirks of the dummy driver, but better
safe than sorry.
Mikko Rasa [Wed, 11 Feb 2015 15:35:56 +0000 (17:35 +0200)]
Apply all planned routes at once
Letting each router to do it separately in the tick function causes
certain problems with synchronization. In particular, sequence points
are difficult to synchronize unless the route update is atomic.
Mikko Rasa [Wed, 11 Feb 2015 12:43:15 +0000 (14:43 +0200)]
Unify destination and waypoints
Destination is really just a special case of a waypoint, so there's no
point in keeping it separate. It just adds duplicated logic, which was
about to get worse with some upcoming new features.
Mikko Rasa [Wed, 11 Feb 2015 12:06:39 +0000 (14:06 +0200)]
Do not allocate blocks while halt is in effect
A turnout state change signal is emitted even if a failure is detected,
to indicate the end of the operation. Resuming allocation in such a case
would cause the train to stray to an incorrect path. It's better to wait
until the user has resolved the situation and removed the halt state.
Mikko Rasa [Fri, 6 Feb 2015 14:37:46 +0000 (16:37 +0200)]
Make sure Block::signal_reserved is emitted consistently
Sigc++ apparently takes arguments as references, so if someone reached to
a signal with null train by reserving the block again, any remaining slots
of the release emission would also get the new train pointer. This caused
the routing system to malfunction in certain cases as the router saw two
emissions for the same block reservation.
The simplest solution would be to pass t instead of train to emit(), but
then the ordering of the two emissions would be inconsistent. Thus the
additional logic to delay further emissions until the outstanding one is
completed.
Mikko Rasa [Fri, 6 Feb 2015 14:32:18 +0000 (16:32 +0200)]
Consider block deallocation when planning routes
Blocks are not released until a sensor is detriggered. Not taking that
into account could cause situations where the planner tries to drive
trains through a balloon loop which is actually too small for them.
Mikko Rasa [Thu, 5 Feb 2015 14:19:01 +0000 (16:19 +0200)]
Eliminate a possible race condition in thread termination
In the unlikely event that the thread doesn't terminate fast enough after
setting the goal pointer, deleting it would send it a kill signal.
Remove this possibility by explicitly jouning the thread.
Mikko Rasa [Thu, 5 Feb 2015 14:01:13 +0000 (16:01 +0200)]
Allow an undetermined turnout at the beginning of a route
If route plans are regenerated when the train's last vehicle is on the
sensor following a turnout in a points-facing movement, the first track
of the lead route is a turnout with undetermined path. Route_changed
would then try to figure out which route the next allocation will occur
on and fail at the very beginning because advance_to_track refused to
use the undetermined turnout.
Mikko Rasa [Thu, 5 Feb 2015 08:48:48 +0000 (10:48 +0200)]
Penalize steps other than the fastest one
This encourages the planner to follow a single chain of steps and avoids
exploding the state tree too much. In the future this may be replaced by
different speed limits on diverging paths of turnouts (reducing the
ambiguity of fastest path) and an actual heuristic for potential wait time
(giving wait states an implicit penalty).
Mikko Rasa [Tue, 3 Feb 2015 14:23:01 +0000 (16:23 +0200)]
Avoid going past the end of route when a new one is set
Calling stop_at(0) causes the block allocator to immediately resume
allocating blocks. In certain circumstances this would cause an
allocation past the end of the route.
Mikko Rasa [Mon, 2 Feb 2015 17:40:43 +0000 (19:40 +0200)]
Improve accessory switching logic
The off command can be sent as soon as the current monitor indicates that
the solenoid is no longer drawing power. This allows giving multi-bit
accessories more time to accommodate quirks of the 74465 decoder.
Mikko Rasa [Thu, 29 Jan 2015 16:50:52 +0000 (18:50 +0200)]
Ensure that Timetable has necessary helper AIs
Since AIControl and TrainRouter don't emit statements to the state file,
they are not created automatically when loading the state. If they are
missing, the timetable won't be able to operate.
Mikko Rasa [Thu, 8 May 2014 21:39:11 +0000 (00:39 +0300)]
Improve graphics quality with some shaders and effects
Lighting is now carried out in linear colorspace and sRGB conversion
applied in postprocessing. All these effects will probably make the
program really slow on lower-end machines, but I'll fix that later.
Mikko Rasa [Wed, 7 May 2014 20:26:13 +0000 (23:26 +0300)]
Avoid segfault if an exception is thrown while loading
The Layout::add functions are called from object constructors. If an
exception is thrown inside them, the constructor will abort and the
object will be deleted. If that happens, it must be removed from the
Layout as well, or an invalid memory access will occur later.
Mikko Rasa [Sat, 3 May 2014 09:17:07 +0000 (12:17 +0300)]
Fix a bug with attaching ends in ExtendTool
This managed to avoid a segfault despite the null pointer dereference,
because the first thing Track::link_to does is check if the passed-in
reference is a track, and a null pointer isn't.
Mikko Rasa [Sat, 3 May 2014 09:15:22 +0000 (12:15 +0300)]
Move gauge to TrackAppearance
It may eventually be desirable to have multiple gauges in the same layout
(narrow-gauge trams together with a standard-gauge railway, for example),
and this is a necessary step towards that.
Mikko Rasa [Mon, 14 Apr 2014 06:00:30 +0000 (09:00 +0300)]
Simplify sequence handling
Instead of checking all sequence points when another train reserves a
block, only check the first one. The subsequent ones can be checked
when they become relevant.
Mikko Rasa [Fri, 11 Apr 2014 19:12:39 +0000 (22:12 +0300)]
Use a better cost estimator for the route planner
Total time is not good because it doesn't penalize waiting time for
earlier trains. This can cause the state tree to explode as later ones
start moving.
The sum of travel and wait times for each train works much better.
Mikko Rasa [Fri, 11 Apr 2014 18:18:09 +0000 (21:18 +0300)]
Fix lead route generation
Lead routes are now created up to the target, but not past it. This fixes
a problem where a route diverging from the train's reserved blocks caused
the router to attempt to create a lead route with gaps.
Mikko Rasa [Thu, 10 Apr 2014 19:04:03 +0000 (22:04 +0300)]
Use path coercion in track iterators
This enforces common traversal rules in all places. It also fixes a bug
in TrackOffsetIter where moving to a turnout from an endpoint not on the
selected path would potentially cause an out-of-range iterator to be
created.