/// <summary> /// Called when the loc has reached the given to-block of the current route. /// </summary> public void BlockReached(ILocState loc, IBlockState block) { if (block == targetBlock) { TargetReached(loc); } }
/// <summary> /// Try to move the given loc to a new block. /// </summary> /// <returns>True if the loc has moved, false if there are no routes available.</returns> private bool MoveLoc(FutureAlternativeSet genSet, ILocState loc) { // Get possible routes var currentBlock = state.GetCurrentBlock(loc); var currentBlockEnterSide = state.GetCurrentBlockEnterSide(loc); var locDirection = currentBlockEnterSide.Invert(); var avoidDirectionChanges = (loc.ChangeDirection == ChangeDirection.Avoid); var routeFromFromBlock = state.RailwayState.GetAllPossibleNonClosedRoutesFromBlock(currentBlock); var routeOptions = routeFromFromBlock.Select(x => state.IsAvailableFor(x, loc, locDirection, avoidDirectionChanges, 0)).ToList(); var possibleRoutes = routeOptions.Where(x => x.IsPossible).Select(x => x.Route).ToList(); // If no routes, then return if (!possibleRoutes.Any()) { return(false); } if (possibleRoutes.Count > 1) { // Create alternate generations foreach (var route in possibleRoutes.Skip(1)) { var alternateState = new FutureRouteAvailabilityTester(state); alternateState.TakeRoute(route, loc); genSet.Add(new FutureAlternative(alternateState, generation)); } } // Take the first route var firstRoute = possibleRoutes[0]; state.TakeRoute(firstRoute, loc); return(true); }
/// <summary> /// Select the given loc. /// </summary> public void SelectLoc(ILocState loc) { if (selectLoc != null) { selectLoc(loc); } }
/// <summary> /// Execute this action on the given loc. /// </summary> protected override void Execute(ILocState loc, IActionContext context) { IStateProperty <bool> property; switch (Entity.Function) { case LocFunction.Light: property = loc.F0; break; default: if (!loc.TryGetFunctionState(Entity.Function, out property)) { return; } break; } switch (Entity.Command) { case LocFunctionCommand.On: property.Requested = true; break; case LocFunctionCommand.Off: property.Requested = false; break; case LocFunctionCommand.Toggle: property.Requested = !property.Requested; break; } }
/// <summary> /// Is the given loc allowed to leave its current block? /// </summary> internal static bool CanLeaveCurrentBlock(this ILocState loc) { var currentBlock = loc.CurrentBlock.Actual; var currentBlockGroup = (currentBlock != null) ? currentBlock.BlockGroup : null; return((currentBlockGroup == null) || (currentBlockGroup.FirstLocCanLeave)); }
public bool AllFree(ILocState allowedLoc) { // Check routes if (routes.Any(x => x.IsLocked())) { return(false); } // Check blocks in the routes foreach (var route in routes) { var toBlock = route.To; var loc = toBlock.LockedBy; if ((loc != null) && (loc != allowedLoc) && (loc.CurrentBlockEnterSide.Actual == route.ToBlockSide)) { // If the loc occupying the blokc cannot change direction, we may not enter this critical section. if (loc.ChangeDirection == ChangeDirection.Avoid) { return(false); } // If changing direction in the blockis not allowed, we may not enter this critical section. if (toBlock.ChangeDirection == ChangeDirection.Avoid) { return(false); } } } return(true); }
/// <summary> /// The given loc has waited at it's destination. /// Let's assign a new route. /// </summary> /// <returns>The timespan before this method wants to be called again.</returns> private TimeSpan OnWaitingForDestinationGroupMinimum(ILocState loc, int generationDelta) { // Check state var state = loc.AutomaticState.Actual; if (state != AutoLocState.WaitingForDestinationGroupMinimum) { log.Warn("Loc {0} in valid state ({1}) at destination timeout.", loc, loc.AutomaticState); return(TimeSpan.MaxValue); } // Log state log.Trace("OnWaitingForDestinationGroupMinimum {0}", loc); // Do we still control this loc? if (!ShouldControlAutomatically(loc)) { RemoveLocFromAutomaticControl(loc); return(TimeSpan.MaxValue); } // Timeout reached? if (loc.CanLeaveCurrentBlock() || overrideLeaveCurrentBlock(generationDelta)) { // Yes, let's assign a new route loc.AutomaticState.Actual = AutoLocState.AssignRoute; return(TimeSpan.Zero); } // Nothing changed return(TimeSpan.MaxValue); }
/// <summary> /// Select one of the given possible routes. /// Returns null if no route should be taken. /// </summary> /// <param name="possibleRoutes">A list of routes to choose from</param> /// <param name="loc">The loc to choose for</param> /// <param name="fromBlock">The block from which the next route will leave</param> /// <param name="locDirection">The direction the loc is facing in the <see cref="fromBlock"/>.</param> public IRouteState SelectRoute(IList <IRouteState> possibleRoutes, ILocState loc, IBlockState fromBlock, BlockSide locDirection) { // Look for a direct match var directRoutes = possibleRoutes.Where(x => x.To == targetBlock).ToList(); if (directRoutes.Any()) { return(new DefaultRouteSelector().SelectRoute(directRoutes, loc, fromBlock, locDirection)); } // Look for routes that get us closer to the target block. var sequences = FindRouteSequences(fromBlock, locDirection.Invert(), targetBlock).ToList(); // Filter sequences to start with possible routes var possibleSequences = sequences.Where(x => possibleRoutes.Contains(x.First)).OrderBy(x => x.Length).ToList(); // Any sequences left? if (possibleSequences.Count == 0) { return(null); } // Take the first possible sequence. return(possibleSequences[0].First); }
/// <summary> /// Check that the designated route is ready and if so, start the loc. /// </summary> /// <returns>The timespan before this method wants to be called again.</returns> private TimeSpan OnWaitingForRouteReady(ILocState loc) { // Check state if (loc.AutomaticState.Actual != AutoLocState.WaitingForAssignedRouteReady) { log.Warn("Cannot start running loc {0} in {1} state.", loc, loc.AutomaticState); return(TimeSpan.MaxValue); } // Log state log.Trace("OnWaitingForRouteReady{0}", loc); // If the route ready? var route = loc.CurrentRoute.Actual; if (!route.Route.IsPrepared) { // Make sure we stop loc.Speed.Requested = 0; // We're not ready yet return(TimeSpan.FromMinutes(1)); } // Set to max speed loc.Speed.Requested = loc.GetMaximumSpeed(route.Route); // Update state loc.AutomaticState.Actual = AutoLocState.Running; // Already look for a next route ChooseNextRoute(loc); return(TimeSpan.MaxValue); }
/// <summary> /// Should the given loc be controlled automatically? /// </summary> private bool ShouldControlAutomatically(ILocState loc) { return (loc.ControlledAutomatically.Requested && !Stopping && !disposing); }
/// <summary> /// Choose an available route from the given block. /// </summary> /// <param name="loc">The loc a route should be choosen for</param> /// <param name="fromBlock">The starting block of the route</param> /// <param name="locDirection">The direction the loc is facing in the <see cref="fromBlock"/>.</param> /// <param name="avoidDirectionChanges">If true, routes requiring a direction change will not be considered, unless there is no alternative.</param> /// <returns>Null if not route is available.</returns> private IRouteState ChooseRoute(ILocState loc, IBlockState fromBlock, BlockSide locDirection, bool avoidDirectionChanges, int generationDelta) { // Gather possible routes. var routeFromFromBlock = railwayState.GetAllPossibleNonClosedRoutesFromBlock(fromBlock).ToList(); var routeOptions = routeFromFromBlock.Select(x => routeAvailabilityTester.IsAvailableFor(x, loc, locDirection, avoidDirectionChanges, generationDelta)).ToList(); var possibleRoutes = routeOptions.Where(x => x.IsPossible).Select(x => x.Route).ToList(); loc.LastRouteOptions.Actual = routeOptions.ToArray(); if (possibleRoutes.Any()) { // Use the route selector to choose a next route var selector = loc.RouteSelector; var selected = selector.SelectRoute(possibleRoutes, loc, fromBlock, locDirection); if (selected == null) { log.Info("No route selected for {0} [from {1} {2}]. Available routes: {3}", loc, fromBlock, locDirection, string.Join(", ", possibleRoutes.Select(x => x.ToString()).ToArray())); } else { log.Info("Selected route {0} for {1} [from {2} {3}]", selected, loc, fromBlock, locDirection); } return(selected); } log.Info("No possible routes for {0} [from {1} {2}]", loc, fromBlock, locDirection); // No available routes return(null); }
/// <summary> /// Try to choose a next route, unless the target block of the current route /// is set to wait. /// </summary> /// <returns>True if a next route has been chosen.</returns> private bool ChooseNextRoute(ILocState loc) { // Should we wait in the destination block? var route = loc.CurrentRoute.Actual; if ((route == null) || (loc.WaitAfterCurrentRoute.Actual) || (loc.HasNextRoute()) || !ShouldControlAutomatically(loc)) { // No need to choose a next route return(false); } // The loc can continue (if a free route is found) var nextRoute = ChooseRoute(loc, route.Route.To, route.Route.ToBlockSide.Invert(), true, 0); if (nextRoute == null) { // No route available return(false); } // We have a next route nextRoute.Lock(loc); loc.NextRoute.Actual = nextRoute; // Set all junctions correct nextRoute.Prepare(); return(true); }
/// <summary> /// Send the loc direction and F1-F4 of the given loc towards the railway. /// </summary> /// <return>True on success</return> public bool SendLocDirectionAndFunctions(ILocState loc) { log.Trace("SendLocDirectionAndFunctions: {0}", loc); var slot = RequestSlot(loc); if (slot == null) { log.Error("No slot available for {0}", loc); return(false); } // Send loc direction log.Trace("Send: LocoDirFuncRequest: slot={0}, direction={1}", slot.SlotNumber, loc.Direction.Requested); var forward = (loc.Direction.Requested == LocDirection.Forward); var f1 = loc.GetFunctionRequestedState(LocFunction.F1); var f2 = loc.GetFunctionRequestedState(LocFunction.F2); var f3 = loc.GetFunctionRequestedState(LocFunction.F3); var f4 = loc.GetFunctionRequestedState(LocFunction.F4); var dirMsg = new LocoDirFuncRequest(slot.SlotNumber, forward, loc.F0.Requested, f1, f2, f3, f4); dirMsg.Execute(lb); slot.Touch(); return(true); }
/// <summary> /// Fire the actions attached to the entering destination trigger. /// </summary> public void FireEnteringDestinationTrigger(ILocState loc) { if (!enteringDestinationTrigger.IsEmpty) { RailwayState.Dispatcher.PostAction(() => enteringDestinationTrigger.Execute(new ActionContext(loc))); } }
/// <summary> /// Fire the actions attached to the destination reached trigger. /// </summary> public void FireDestinationReachedTrigger(ILocState loc) { if (!destinationReachedTrigger.IsEmpty) { RailwayState.Dispatcher.PostAction(() => destinationReachedTrigger.Execute(new ActionContext(loc))); } }
/// <summary> /// Gets the medium speed of the given loc on the given route. /// </summary> public static int GetMediumSpeed(this ILocState loc, IRouteState route) { var speed = loc.MediumSpeed; var routeSpeed = route.Speed / 100.0; return(Math.Max((int)(speed * routeSpeed), loc.SlowSpeed)); }
/// <summary> /// Gets/sets the slot for the given loc. /// </summary> /// <returns>Null if not found</returns> public Slot this[ILocState loc] { get { if (loc == null) { throw new ArgumentNullException("loc"); } var entry = slots.FirstOrDefault(x => (x != null) && (x.Loc == loc)); return((entry != null) ? entry.Slot : null); } set { if (value == null) { for (var i = 0; i < slots.Length; i++) { if ((slots[i] != null) && (slots[i].Loc == loc)) { slots[i] = null; } } } else { slots[value.SlotNumber] = new SlotWithLoc(value, loc); } } }
/// <summary> /// Can the given route be taken by the given loc? /// </summary> /// <param name="route">The route being investigated</param> /// <param name="loc">The loc a route should be choosen for</param> /// <param name="locDirection">The direction the loc is facing in the From block of the given <see cref="route"/>.</param> /// <param name="avoidDirectionChanges">If true, the route is considered not available if a direction change is needed.</param> /// <param name="generationDelta">If larger than 0, look this number of generation less far in the future.</param> /// <returns>True if the route can be locked and no sensor in the route is active (outside current route).</returns> public override IRouteOption IsAvailableFor(IRouteState route, ILocState loc, Model.BlockSide locDirection, bool avoidDirectionChanges, int generationDelta) { // Perform standard testing first var result = base.IsAvailableFor(route, loc, locDirection, avoidDirectionChanges, generationDelta); if (!result.IsPossible) { return(result); } // If last resort, we're ok when critical section is empty. if (generationDelta > 0) { if (route.CriticalSection.IsEmpty) { return(result); } } // Now test future possibilities of the entire railway to detect possible deadlocks. var genSet = new FutureAlternativeSet(this, route, loc); if (!genSet.Test(generationDelta)) { // Not Ok return(new RouteOption(route, RouteImpossibleReason.DeadLock)); } return(result); }
/// <summary> /// Remove the given loc from the list of automatically controlled locs. /// </summary> private void RemoveLocFromAutomaticControl(ILocState loc) { log.Info("Removing {0} from auto-loc-list", loc); autoLocs.Remove(loc); loc.AutomaticState.Actual = AutoLocState.AssignRoute; loc.ControlledAutomatically.Actual = false; loc.PossibleDeadlock.Actual = false; }
/// <summary> /// Can the given underlying entity be locked by the intended owner? /// </summary> protected override bool CanLockUnderlyingEntity(ILockableState entity, ILocState owner, out ILocState lockedBy) { if (base.CanLockUnderlyingEntity(entity, owner, out lockedBy)) { return(true); } return((entity == From) && entity.IsLockedBy(owner)); }
public void SelectLoc(ILocState value) { var item = lvLocs.Items.Cast <LocItem>().FirstOrDefault(x => x.LocState == value); if (item != null) { item.Selected = true; } }
/// <summary> /// SetFunctionActualState set the actual state of a function in the given loc. /// </summary> public static void SetFunctionActualState(this ILocState loc, LocFunction function, bool value) { IStateProperty <bool> state; if (loc.TryGetFunctionState(function, out state)) { state.Actual = value; } }
/// <summary> /// Loc has been reset. /// </summary> private void OnAfterResetLoc(ILocState loc) { // Loc could have "occupied" sensors which have now been released. // Update sensors. foreach (var sensor in railwayState.SensorStates) { OnSensorActiveChanged(sensor); } }
/// <summary> /// Send the speed and direction of the given loc towards the railway. /// </summary> protected override void OnSendLocSpeedAndDirection(ILocState loc) { Log.Trace("OnSendLocSpeedAndDirection: {0}", loc); try { var forward = (loc.Direction.Requested == LocDirection.Forward); var f1 = loc.GetFunctionRequestedState(LocFunction.F1); var f2 = loc.GetFunctionRequestedState(LocFunction.F2); var f3 = loc.GetFunctionRequestedState(LocFunction.F3); var f4 = loc.GetFunctionRequestedState(LocFunction.F4); client.LocCommand(loc.Address.ValueAsInt, loc.SpeedInSteps.Requested, forward, loc.F0.Requested, f1, f2, f3, f4); loc.Direction.Actual = loc.Direction.Requested; loc.Speed.Actual = loc.Speed.Requested; loc.F0.Actual = loc.F0.Requested; loc.SetFunctionActualState(LocFunction.F1, f1); loc.SetFunctionActualState(LocFunction.F2, f2); loc.SetFunctionActualState(LocFunction.F3, f3); loc.SetFunctionActualState(LocFunction.F4, f4); var f5 = loc.GetFunctionRequestedState(LocFunction.F5); var f6 = loc.GetFunctionRequestedState(LocFunction.F6); var f7 = loc.GetFunctionRequestedState(LocFunction.F7); var f8 = loc.GetFunctionRequestedState(LocFunction.F8); client.LocFunctions(loc.Address.ValueAsInt, f1, f2, f3, f4, f5, f6, f7, f8); loc.SetFunctionActualState(LocFunction.F1, f1); loc.SetFunctionActualState(LocFunction.F2, f2); loc.SetFunctionActualState(LocFunction.F3, f3); loc.SetFunctionActualState(LocFunction.F4, f4); loc.SetFunctionActualState(LocFunction.F5, f5); loc.SetFunctionActualState(LocFunction.F6, f6); loc.SetFunctionActualState(LocFunction.F7, f7); loc.SetFunctionActualState(LocFunction.F8, f8); var f9 = loc.GetFunctionRequestedState(LocFunction.F9); var f10 = loc.GetFunctionRequestedState(LocFunction.F10); var f11 = loc.GetFunctionRequestedState(LocFunction.F11); var f12 = loc.GetFunctionRequestedState(LocFunction.F12); var f13 = loc.GetFunctionRequestedState(LocFunction.F13); var f14 = loc.GetFunctionRequestedState(LocFunction.F14); var f15 = loc.GetFunctionRequestedState(LocFunction.F15); var f16 = loc.GetFunctionRequestedState(LocFunction.F16); client.LocFunctions2(loc.Address.ValueAsInt, f9, f10, f11, f12, f13, f14, f15, f16); loc.SetFunctionActualState(LocFunction.F9, f9); loc.SetFunctionActualState(LocFunction.F10, f10); loc.SetFunctionActualState(LocFunction.F11, f11); loc.SetFunctionActualState(LocFunction.F12, f12); loc.SetFunctionActualState(LocFunction.F13, f13); loc.SetFunctionActualState(LocFunction.F14, f14); loc.SetFunctionActualState(LocFunction.F15, f15); loc.SetFunctionActualState(LocFunction.F16, f16); } catch (Exception ex) { Log.Warn("Loc command failed: " + ex); } }
/// <summary> /// GetFunctionActualState returns the actual state of a function in the given loc. /// If no such function exists, false is returned. /// </summary> public static bool GetFunctionActualState(this ILocState loc, LocFunction function) { IStateProperty <bool> state; if (loc.TryGetFunctionState(function, out state)) { return(state.Actual); } return(false); }
/// <summary> /// Default ctor /// </summary> public FutureAlternativeSet(LiveRouteAvailabilityTester live, IRouteState route, ILocState loc) { this.testLoc = loc; minGenerations = live.railwayState.BlockStates.Count; var tester = new FutureRouteAvailabilityTester(live.railwayState); tester.TakeRoute(route, loc); alternatives.Add(new FutureAlternative(tester, 0)); autoLocs = live.railwayState.LocStates.Where(x => x.ControlledAutomatically.Actual && (x.CurrentBlock.Actual != null)).ToList(); }
/// <summary> /// Save the current block state of the given loc. /// </summary> public void SetLocState(IRailwayState railwayState, ILocState loc, IBlockState currentBlock, BlockSide currentBlockEnterSide, LocDirection currentDirection) { // Write assignment using (var key = Registry.CurrentUser.CreateSubKey(GetKey(loc))) { key.SetValue(CurrentBlock, (currentBlock != null) ? currentBlock.EntityId : string.Empty); key.SetValue(CurrentBlockEnterSide, (currentBlockEnterSide == BlockSide.Back) ? 0 : 1); key.SetValue(CurrentDirection, (currentDirection == LocDirection.Forward) ? 0 : 1); } }
/// <summary> /// Send the speed and direction of the given loc towards the railway. /// </summary> protected override void OnSendLocSpeedAndDirection(ILocState loc) { Log.Trace("OnSendLocSpeedAndDirection: {0}", loc); var direction = (loc.Direction.Requested == LocDirection.Forward); var packet = Packets.CreateSpeedAndDirection(loc.Address.ValueAsInt, (byte)loc.SpeedInSteps.Requested, direction, loc.SpeedSteps); var data = PacketTranslater.Translate(packet); sender.SendSpeedAndDirection(loc.Address.ValueAsInt, data); loc.Direction.Actual = loc.Direction.Requested; loc.Speed.Actual = loc.Speed.Requested; }
/// <summary> /// Put the data from the loc state into this slot. /// </summary> protected void UpdateSlotFromLoc(Slot slot, ILocState l) { slot.Address = l.Address.ValueAsInt; var spd = l.SpeedInSteps.Actual; slot.Speed = (spd == 1) ? 2 : spd; var dirf = DirFunc.None; if (l.Direction.Actual == LocDirection.Reverse) { dirf |= DirFunc.Direction; } if (l.F0.Actual) { dirf |= DirFunc.F0; } if (l.GetFunctionActualState(LocFunction.F1)) { dirf |= DirFunc.F1; } if (l.GetFunctionActualState(LocFunction.F2)) { dirf |= DirFunc.F2; } if (l.GetFunctionActualState(LocFunction.F3)) { dirf |= DirFunc.F3; } if (l.GetFunctionActualState(LocFunction.F4)) { dirf |= DirFunc.F4; } slot.DirF = dirf; var snd = Sound.None; if (l.GetFunctionActualState(LocFunction.F5)) { snd |= Sound.Snd1; } if (l.GetFunctionActualState(LocFunction.F6)) { snd |= Sound.Snd2; } if (l.GetFunctionActualState(LocFunction.F7)) { snd |= Sound.Snd3; } if (l.GetFunctionActualState(LocFunction.F8)) { snd |= Sound.Snd4; } slot.Sound = snd; }
/// <summary> /// Prepare this state for use in a live railway. /// Make sure all relevant connections to other state objects are resolved. /// </summary> /// <returns>True if the entity is now ready for use in a live railway, false otherwise.</returns> protected override bool TryPrepareForUse(IStateUserInterface ui, IStatePersistence statePersistence) { if (Entity.Loc != null) { loc = RailwayState.LocStates[Entity.Loc]; if (loc == null) { return(false); } } return(true); }