From c549f7364dda52adcb73b091c4ac512f1b80a584 Mon Sep 17 00:00:00 2001 From: Stephan Richter Date: Sun, 28 Feb 2021 18:00:34 +0100 Subject: [PATCH 01/26] moved Thrads to separate package --- .../de/srsoftware/web4rail/BaseClass.java | 4 +- .../java/de/srsoftware/web4rail/Plan.java | 10 +- .../java/de/srsoftware/web4rail/Route.java | 152 ++---------------- .../srsoftware/web4rail/actions/Action.java | 6 - .../actions/AddRemoveDestination.java | 2 +- .../web4rail/actions/DelayedAction.java | 2 +- .../actions/DetermineTrainInBlock.java | 2 +- .../web4rail/actions/DisableEnableBlock.java | 2 +- .../web4rail/actions/EngageDecoupler.java | 2 +- .../web4rail/actions/SetContextTrain.java | 2 +- .../web4rail/actions/SetDisplayText.java | 2 +- .../srsoftware/web4rail/actions/SetPower.java | 2 +- .../web4rail/actions/SetRelayOrSwitch.java | 2 +- .../web4rail/actions/SetTurnout.java | 2 +- .../web4rail/actions/WaitForContact.java | 2 +- .../web4rail/conditions/BlockFree.java | 2 +- .../web4rail/conditions/RouteEndBlock.java | 2 +- .../web4rail/conditions/SwitchIsOn.java | 2 +- .../de/srsoftware/web4rail/moving/Car.java | 6 - .../de/srsoftware/web4rail/moving/Train.java | 56 +------ .../web4rail/threads/Autopilot.java | 68 ++++++++ .../web4rail/threads/BrakeProcessor.java | 145 +++++++++++++++++ .../web4rail/{ => threads}/ControlUnit.java | 6 +- .../{ => threads}/DelayedExecution.java | 4 +- .../de/srsoftware/web4rail/tiles/Bridge.java | 2 +- .../de/srsoftware/web4rail/tiles/Contact.java | 2 +- 26 files changed, 260 insertions(+), 229 deletions(-) create mode 100644 src/main/java/de/srsoftware/web4rail/threads/Autopilot.java create mode 100644 src/main/java/de/srsoftware/web4rail/threads/BrakeProcessor.java rename src/main/java/de/srsoftware/web4rail/{ => threads}/ControlUnit.java (98%) rename src/main/java/de/srsoftware/web4rail/{ => threads}/DelayedExecution.java (83%) diff --git a/src/main/java/de/srsoftware/web4rail/BaseClass.java b/src/main/java/de/srsoftware/web4rail/BaseClass.java index 56ab24b..a098ab8 100644 --- a/src/main/java/de/srsoftware/web4rail/BaseClass.java +++ b/src/main/java/de/srsoftware/web4rail/BaseClass.java @@ -39,7 +39,7 @@ import de.srsoftware.web4rail.tiles.Tile; * @author Stephan Richter, SRSoftware 2020…2021 */ public abstract class BaseClass implements Constants{ - protected static Plan plan; // the track layout in use + public static Plan plan; // the track layout in use public static final Random random = new Random(); public static String speedUnit = DEFAULT_SPEED_UNIT; public static String lengthUnit = DEFAULT_LENGTH_UNIT; @@ -511,7 +511,7 @@ public abstract class BaseClass implements Constants{ } - protected static String t(String txt, Object...fills) { + public static String t(String txt, Object...fills) { if (isSet(fills)) for (int i=0; i Tile.load(object, plan)); if (json.has(LENGTH_UNIT)) lengthUnit = json.getString(LENGTH_UNIT); if (json.has(SPEED_UNIT)) speedUnit = json.getString(SPEED_UNIT); - if (json.has(FINAL_SPEED)) Route.defaultEndSpeed = json.getInt(FINAL_SPEED); + if (json.has(FINAL_SPEED)) BrakeProcessor.defaultEndSpeed = json.getInt(FINAL_SPEED); if (json.has(FREE_BEHIND_TRAIN)) Route.freeBehindTrain = json.getBoolean(FREE_BEHIND_TRAIN); try { @@ -982,7 +984,7 @@ public class Plan extends BaseClass{ if (params.containsKey(LENGTH_UNIT)) lengthUnit = params.get(LENGTH_UNIT); if (params.containsKey(SPEED_UNIT)) speedUnit = params.get(SPEED_UNIT); - if (params.containsKey(FINAL_SPEED)) Route.defaultEndSpeed = Integer.parseInt(params.get(FINAL_SPEED)); + if (params.containsKey(FINAL_SPEED)) BrakeProcessor.defaultEndSpeed = Integer.parseInt(params.get(FINAL_SPEED)); Route.freeBehindTrain = "on".equalsIgnoreCase(params.get(FREE_BEHIND_TRAIN)); return t("Plan updated."); diff --git a/src/main/java/de/srsoftware/web4rail/Route.java b/src/main/java/de/srsoftware/web4rail/Route.java index 3ddb42e..7b6afd0 100644 --- a/src/main/java/de/srsoftware/web4rail/Route.java +++ b/src/main/java/de/srsoftware/web4rail/Route.java @@ -18,7 +18,6 @@ import org.json.JSONTokener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import de.keawe.tools.translations.Translation; import de.srsoftware.tools.Tag; import de.srsoftware.web4rail.Plan.Direction; import de.srsoftware.web4rail.actions.Action; @@ -38,6 +37,7 @@ import de.srsoftware.web4rail.tags.Fieldset; import de.srsoftware.web4rail.tags.Input; import de.srsoftware.web4rail.tags.Table; import de.srsoftware.web4rail.tags.Window; +import de.srsoftware.web4rail.threads.BrakeProcessor; import de.srsoftware.web4rail.tiles.Block; import de.srsoftware.web4rail.tiles.BlockContact; import de.srsoftware.web4rail.tiles.Contact; @@ -71,7 +71,6 @@ public class Route extends BaseClass { static final String SIGNALS = "signals"; static final String TURNOUTS = "turnouts"; private State state = State.FREE; - public static int defaultEndSpeed = 10; public static boolean freeBehindTrain = true; private static final String ROUTE_START = "route_start"; @@ -79,138 +78,6 @@ public class Route extends BaseClass { private static final String ROUTE_SETUP = "route_setup"; private static HashMap names = new HashMap(); // maps id to name. needed to keep names during plan.analyze() - - private class BrakeProcessor extends Thread { - private long latestTick; - private static final int SPEED_STEP = 5; - private Integer timeStep; - private Route route; - private Train train; - private String brakeId; - private long estimatedDistance; // Unit: s*km/h "km/h-Sekunden" - private int startSpeed,endSpeed; - private boolean aborted,modified,finished; - - public BrakeProcessor(Route route, Train train) { - this.train = train; - this.route = route; - - aborted = false; - modified = false; - finished = false; - brakeId = train.brakeId(); - startSpeed = train.speed; - endSpeed = defaultEndSpeed; - - timeStep = brakeTimes.get(brakeId); - - if (isNull(timeStep) || timeStep>1000000) timeStep = 256; // if no brake time is available for this train - setName(Application.threadName("BrakeProcessor("+train+")")); - start(); - } - - protected void abort() { - aborted = true; - } - - private long calcDistance(Integer ts) { - long dist = 0; - int s = startSpeed; - while (s > defaultEndSpeed) { - s -= SPEED_STEP; - dist += s*ts; - } - LOG.debug("Estimated distamce with {} ms timestep: {}",ts,dist); - return dist; - } - - private void checkNextRoute() { - Route nextRoute = train.nextRoute(); - if (isSet(nextRoute) && nextRoute.state == Route.State.PREPARED) { // auf Startgeschwindigkeit der Nachfolgeroute bremsen - Integer nextRouteStartSpeed = nextRoute.startSpeed(); - if (isSet(nextRouteStartSpeed)) { - LOG.debug("updating target velocity from {} to {}!",endSpeed,nextRouteStartSpeed); - endSpeed = nextRouteStartSpeed; - modified = true; - if (endSpeed > train.speed) train.setSpeed(endSpeed); - } - } - } - - /** - * This is called from route.finish when train came to stop - */ - public void finish() { - LOG.debug("BrakeProcessor.finish()"); - finished = true; - if (aborted || modified) return; - increaseDistance(); - train.setSpeed(0); - LOG.debug("Estimated distance: {}",estimatedDistance); - - if (startSpeed <= endSpeed) return; - if (timeStep<0) timeStep = 100; - Integer newTimeStep = timeStep; - long calculated; - int step = 32*newTimeStep; - for (int i=0; i<20; i++) { - step = step/2; - if (step<1) step = 1; - calculated = calcDistance(newTimeStep); - LOG.debug("Calculated distance for step = {} ms: {}",newTimeStep,calculated); - LOG.debug("Update step: {}",step); - newTimeStep = newTimeStep + (calculated > estimatedDistance ? -step : step); - } - - if (!newTimeStep.equals(timeStep)) { - route.brakeTimes.put(brakeId,newTimeStep); - calculated = calcDistance(newTimeStep); - LOG.debug("Corrected brake timestep for {} @ {} from {} to {} ms.",train,route,timeStep,newTimeStep); - LOG.debug("Differemce from estimated distance: {} ({}%)",estimatedDistance-calculated,100*(estimatedDistance-calculated)/(float)estimatedDistance); - } - } - - private void increaseDistance(){ - long tick = timestamp(); - estimatedDistance += train.speed * (3+tick-latestTick); - latestTick = tick; - } - - @Override - public void run() { - setName(Application.threadName("BreakeProcessor("+train+")")); - LOG.debug("started BrakeProcessor ({} → {}) for {} with timestep = {} ms.",train.speed,endSpeed,train,timeStep); - estimatedDistance = 0; - latestTick = timestamp(); - while (train.speed > endSpeed) { - if (finished || aborted) return; - increaseDistance(); - LOG.debug("BrakeProcessor({}) setting Speed of {}.",route,train); - train.setSpeed(Math.max(train.speed - SPEED_STEP,endSpeed)); - if (!modified) checkNextRoute(); - try { - sleep(timeStep); - } catch (InterruptedException e) { - LOG.warn("BrakeProcessor interrupted!", e); - } - } - - while (!finished && !aborted && !modified) { - try { - sleep(1000); - } catch (InterruptedException e) { - LOG.warn("BrakeProcessor interrupted!", e); - } - checkNextRoute(); - } - } - - public void setEndSpeed(Integer newEndSpeed) { - if (isNull(newEndSpeed)) return; - endSpeed = newEndSpeed; - modified = true; - } - } private BrakeProcessor brakeProcessor = null; private HashMap brakeTimes = new HashMap(); @@ -367,6 +234,14 @@ public class Route extends BaseClass { return fieldset; } + public Integer brakeTime(String brakeId) { + return brakeTimes.get(brakeId); + } + + public void brakeTime(String brakeId, Integer newTimeStep) { + brakeTimes.put(brakeId,newTimeStep); + } + private Fieldset brakeTimes() { Fieldset fieldset = new Fieldset(t("Brake time table")); Table table = new Table(); @@ -1066,11 +941,6 @@ public class Route extends BaseClass { return isSet(startActions) ? startActions.getSpeed(context) : null; } - - protected static String t(String txt, Object...fills) { - return Translation.get(Application.class, txt, fills); - } - @Override public String toString() { return getClass().getSimpleName()+"("+(isSet(train)?train+":":"")+name()+")"; @@ -1136,8 +1006,4 @@ public class Route extends BaseClass { super.update(params); return properties(); } - - public Integer brakeTime(String brakeId) { - return brakeTimes.get(brakeId); - } } diff --git a/src/main/java/de/srsoftware/web4rail/actions/Action.java b/src/main/java/de/srsoftware/web4rail/actions/Action.java index 84ba186..d24afbf 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/Action.java +++ b/src/main/java/de/srsoftware/web4rail/actions/Action.java @@ -10,9 +10,7 @@ import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import de.keawe.tools.translations.Translation; import de.srsoftware.tools.Tag; -import de.srsoftware.web4rail.Application; import de.srsoftware.web4rail.BaseClass; import de.srsoftware.web4rail.tags.Button; import de.srsoftware.web4rail.tags.Fieldset; @@ -159,10 +157,6 @@ public abstract class Action extends BaseClass { return select.addTo(new Label(t("Action type")+COL)); } - protected static String t(String tex,Object...fills) { - return Translation.get(Application.class, tex, fills); - } - @Override public String toString() { return t(getClass().getSimpleName()); diff --git a/src/main/java/de/srsoftware/web4rail/actions/AddRemoveDestination.java b/src/main/java/de/srsoftware/web4rail/actions/AddRemoveDestination.java index e01bb85..3552aed 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/AddRemoveDestination.java +++ b/src/main/java/de/srsoftware/web4rail/actions/AddRemoveDestination.java @@ -9,11 +9,11 @@ import org.json.JSONObject; import de.srsoftware.tools.Tag; import de.srsoftware.web4rail.BaseClass; -import de.srsoftware.web4rail.DelayedExecution; import de.srsoftware.web4rail.moving.Train; import de.srsoftware.web4rail.tags.Checkbox; import de.srsoftware.web4rail.tags.Fieldset; import de.srsoftware.web4rail.tags.Window; +import de.srsoftware.web4rail.threads.DelayedExecution; import de.srsoftware.web4rail.tiles.Block; import de.srsoftware.web4rail.tiles.Tile; diff --git a/src/main/java/de/srsoftware/web4rail/actions/DelayedAction.java b/src/main/java/de/srsoftware/web4rail/actions/DelayedAction.java index 9630284..c03f321 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/DelayedAction.java +++ b/src/main/java/de/srsoftware/web4rail/actions/DelayedAction.java @@ -7,10 +7,10 @@ import org.json.JSONObject; import de.srsoftware.tools.Tag; import de.srsoftware.web4rail.BaseClass; -import de.srsoftware.web4rail.DelayedExecution; import de.srsoftware.web4rail.tags.Fieldset; import de.srsoftware.web4rail.tags.Input; import de.srsoftware.web4rail.tags.Window; +import de.srsoftware.web4rail.threads.DelayedExecution; public class DelayedAction extends ActionList { diff --git a/src/main/java/de/srsoftware/web4rail/actions/DetermineTrainInBlock.java b/src/main/java/de/srsoftware/web4rail/actions/DetermineTrainInBlock.java index 2671dad..c73b5aa 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/DetermineTrainInBlock.java +++ b/src/main/java/de/srsoftware/web4rail/actions/DetermineTrainInBlock.java @@ -7,9 +7,9 @@ import java.util.Map; import org.json.JSONObject; import de.srsoftware.web4rail.BaseClass; -import de.srsoftware.web4rail.DelayedExecution; import de.srsoftware.web4rail.tags.Fieldset; import de.srsoftware.web4rail.tags.Window; +import de.srsoftware.web4rail.threads.DelayedExecution; import de.srsoftware.web4rail.tiles.Block; import de.srsoftware.web4rail.tiles.Tile; diff --git a/src/main/java/de/srsoftware/web4rail/actions/DisableEnableBlock.java b/src/main/java/de/srsoftware/web4rail/actions/DisableEnableBlock.java index e68723e..6aee81f 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/DisableEnableBlock.java +++ b/src/main/java/de/srsoftware/web4rail/actions/DisableEnableBlock.java @@ -8,10 +8,10 @@ import org.json.JSONObject; import de.srsoftware.tools.Tag; import de.srsoftware.web4rail.BaseClass; -import de.srsoftware.web4rail.DelayedExecution; import de.srsoftware.web4rail.tags.Fieldset; import de.srsoftware.web4rail.tags.Radio; import de.srsoftware.web4rail.tags.Window; +import de.srsoftware.web4rail.threads.DelayedExecution; import de.srsoftware.web4rail.tiles.Block; import de.srsoftware.web4rail.tiles.Shadow; import de.srsoftware.web4rail.tiles.Tile; diff --git a/src/main/java/de/srsoftware/web4rail/actions/EngageDecoupler.java b/src/main/java/de/srsoftware/web4rail/actions/EngageDecoupler.java index 5c2f487..f5c21ee 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/EngageDecoupler.java +++ b/src/main/java/de/srsoftware/web4rail/actions/EngageDecoupler.java @@ -7,9 +7,9 @@ import java.util.Map; import org.json.JSONObject; import de.srsoftware.web4rail.BaseClass; -import de.srsoftware.web4rail.DelayedExecution; import de.srsoftware.web4rail.tags.Fieldset; import de.srsoftware.web4rail.tags.Window; +import de.srsoftware.web4rail.threads.DelayedExecution; import de.srsoftware.web4rail.tiles.Decoupler; import de.srsoftware.web4rail.tiles.Tile; diff --git a/src/main/java/de/srsoftware/web4rail/actions/SetContextTrain.java b/src/main/java/de/srsoftware/web4rail/actions/SetContextTrain.java index 36182d1..4ea9109 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/SetContextTrain.java +++ b/src/main/java/de/srsoftware/web4rail/actions/SetContextTrain.java @@ -6,10 +6,10 @@ import java.util.List; import org.json.JSONObject; import de.srsoftware.web4rail.BaseClass; -import de.srsoftware.web4rail.DelayedExecution; import de.srsoftware.web4rail.moving.Train; import de.srsoftware.web4rail.tags.Fieldset; import de.srsoftware.web4rail.tags.Window; +import de.srsoftware.web4rail.threads.DelayedExecution; public class SetContextTrain extends Action { diff --git a/src/main/java/de/srsoftware/web4rail/actions/SetDisplayText.java b/src/main/java/de/srsoftware/web4rail/actions/SetDisplayText.java index 14dbc37..53999f7 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/SetDisplayText.java +++ b/src/main/java/de/srsoftware/web4rail/actions/SetDisplayText.java @@ -7,10 +7,10 @@ import java.util.Map; import org.json.JSONObject; import de.srsoftware.web4rail.BaseClass; -import de.srsoftware.web4rail.DelayedExecution; import de.srsoftware.web4rail.tags.Fieldset; import de.srsoftware.web4rail.tags.Label; import de.srsoftware.web4rail.tags.Window; +import de.srsoftware.web4rail.threads.DelayedExecution; import de.srsoftware.web4rail.tiles.TextDisplay; import de.srsoftware.web4rail.tiles.Tile; diff --git a/src/main/java/de/srsoftware/web4rail/actions/SetPower.java b/src/main/java/de/srsoftware/web4rail/actions/SetPower.java index e51ab3f..21889f6 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/SetPower.java +++ b/src/main/java/de/srsoftware/web4rail/actions/SetPower.java @@ -7,10 +7,10 @@ import org.json.JSONObject; import de.srsoftware.tools.Tag; import de.srsoftware.web4rail.BaseClass; -import de.srsoftware.web4rail.ControlUnit; import de.srsoftware.web4rail.tags.Fieldset; import de.srsoftware.web4rail.tags.Radio; import de.srsoftware.web4rail.tags.Window; +import de.srsoftware.web4rail.threads.ControlUnit; public class SetPower extends Action{ diff --git a/src/main/java/de/srsoftware/web4rail/actions/SetRelayOrSwitch.java b/src/main/java/de/srsoftware/web4rail/actions/SetRelayOrSwitch.java index c8aba80..19eeadb 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/SetRelayOrSwitch.java +++ b/src/main/java/de/srsoftware/web4rail/actions/SetRelayOrSwitch.java @@ -8,10 +8,10 @@ import org.json.JSONObject; import de.srsoftware.tools.Tag; import de.srsoftware.web4rail.BaseClass; -import de.srsoftware.web4rail.DelayedExecution; import de.srsoftware.web4rail.tags.Fieldset; import de.srsoftware.web4rail.tags.Select; import de.srsoftware.web4rail.tags.Window; +import de.srsoftware.web4rail.threads.DelayedExecution; import de.srsoftware.web4rail.tiles.Relay; import de.srsoftware.web4rail.tiles.Switch; import de.srsoftware.web4rail.tiles.Tile; diff --git a/src/main/java/de/srsoftware/web4rail/actions/SetTurnout.java b/src/main/java/de/srsoftware/web4rail/actions/SetTurnout.java index fe5f5fb..78f8fce 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/SetTurnout.java +++ b/src/main/java/de/srsoftware/web4rail/actions/SetTurnout.java @@ -8,10 +8,10 @@ import org.json.JSONObject; import de.srsoftware.tools.Tag; import de.srsoftware.web4rail.BaseClass; -import de.srsoftware.web4rail.DelayedExecution; import de.srsoftware.web4rail.tags.Fieldset; import de.srsoftware.web4rail.tags.Select; import de.srsoftware.web4rail.tags.Window; +import de.srsoftware.web4rail.threads.DelayedExecution; import de.srsoftware.web4rail.tiles.Turnout; import de.srsoftware.web4rail.tiles.Turnout.State; diff --git a/src/main/java/de/srsoftware/web4rail/actions/WaitForContact.java b/src/main/java/de/srsoftware/web4rail/actions/WaitForContact.java index 83df759..754486a 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/WaitForContact.java +++ b/src/main/java/de/srsoftware/web4rail/actions/WaitForContact.java @@ -8,10 +8,10 @@ import org.json.JSONObject; import de.srsoftware.tools.Tag; import de.srsoftware.web4rail.BaseClass; -import de.srsoftware.web4rail.DelayedExecution; import de.srsoftware.web4rail.tags.Fieldset; import de.srsoftware.web4rail.tags.Input; import de.srsoftware.web4rail.tags.Window; +import de.srsoftware.web4rail.threads.DelayedExecution; import de.srsoftware.web4rail.tiles.Contact; import de.srsoftware.web4rail.tiles.Contact.Listener; import de.srsoftware.web4rail.tiles.Tile; diff --git a/src/main/java/de/srsoftware/web4rail/conditions/BlockFree.java b/src/main/java/de/srsoftware/web4rail/conditions/BlockFree.java index 364d60b..f37b042 100644 --- a/src/main/java/de/srsoftware/web4rail/conditions/BlockFree.java +++ b/src/main/java/de/srsoftware/web4rail/conditions/BlockFree.java @@ -7,9 +7,9 @@ import java.util.Map; import org.json.JSONObject; import de.srsoftware.web4rail.BaseClass; -import de.srsoftware.web4rail.DelayedExecution; import de.srsoftware.web4rail.tags.Fieldset; import de.srsoftware.web4rail.tags.Window; +import de.srsoftware.web4rail.threads.DelayedExecution; import de.srsoftware.web4rail.tiles.Block; import de.srsoftware.web4rail.tiles.Tile; diff --git a/src/main/java/de/srsoftware/web4rail/conditions/RouteEndBlock.java b/src/main/java/de/srsoftware/web4rail/conditions/RouteEndBlock.java index 2f6676c..6eec56a 100644 --- a/src/main/java/de/srsoftware/web4rail/conditions/RouteEndBlock.java +++ b/src/main/java/de/srsoftware/web4rail/conditions/RouteEndBlock.java @@ -7,10 +7,10 @@ import java.util.Map; import org.json.JSONObject; import de.srsoftware.web4rail.BaseClass; -import de.srsoftware.web4rail.DelayedExecution; import de.srsoftware.web4rail.Route; import de.srsoftware.web4rail.tags.Fieldset; import de.srsoftware.web4rail.tags.Window; +import de.srsoftware.web4rail.threads.DelayedExecution; import de.srsoftware.web4rail.tiles.Block; import de.srsoftware.web4rail.tiles.Tile; diff --git a/src/main/java/de/srsoftware/web4rail/conditions/SwitchIsOn.java b/src/main/java/de/srsoftware/web4rail/conditions/SwitchIsOn.java index e43879e..dacb2a7 100644 --- a/src/main/java/de/srsoftware/web4rail/conditions/SwitchIsOn.java +++ b/src/main/java/de/srsoftware/web4rail/conditions/SwitchIsOn.java @@ -8,10 +8,10 @@ import org.json.JSONObject; import de.srsoftware.tools.Tag; import de.srsoftware.web4rail.BaseClass; -import de.srsoftware.web4rail.DelayedExecution; import de.srsoftware.web4rail.tags.Fieldset; import de.srsoftware.web4rail.tags.Radio; import de.srsoftware.web4rail.tags.Window; +import de.srsoftware.web4rail.threads.DelayedExecution; import de.srsoftware.web4rail.tiles.Switch; import de.srsoftware.web4rail.tiles.Tile; diff --git a/src/main/java/de/srsoftware/web4rail/moving/Car.java b/src/main/java/de/srsoftware/web4rail/moving/Car.java index b02951e..c46499d 100644 --- a/src/main/java/de/srsoftware/web4rail/moving/Car.java +++ b/src/main/java/de/srsoftware/web4rail/moving/Car.java @@ -18,9 +18,7 @@ import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import de.keawe.tools.translations.Translation; import de.srsoftware.tools.Tag; -import de.srsoftware.web4rail.Application; import de.srsoftware.web4rail.BaseClass; import de.srsoftware.web4rail.Plan; import de.srsoftware.web4rail.tags.Button; @@ -273,10 +271,6 @@ public class Car extends BaseClass implements Comparable{ file.close(); } - protected static String t(String txt, Object...fills) { - return Translation.get(Application.class, txt, fills); - } - public TreeSet tags() { return new TreeSet(tags); } diff --git a/src/main/java/de/srsoftware/web4rail/moving/Train.java b/src/main/java/de/srsoftware/web4rail/moving/Train.java index 891b55a..a40e186 100644 --- a/src/main/java/de/srsoftware/web4rail/moving/Train.java +++ b/src/main/java/de/srsoftware/web4rail/moving/Train.java @@ -39,6 +39,7 @@ import de.srsoftware.web4rail.tags.Label; import de.srsoftware.web4rail.tags.Select; import de.srsoftware.web4rail.tags.Table; import de.srsoftware.web4rail.tags.Window; +import de.srsoftware.web4rail.threads.Autopilot; import de.srsoftware.web4rail.tiles.Block; import de.srsoftware.web4rail.tiles.Contact; import de.srsoftware.web4rail.tiles.Tile; @@ -88,55 +89,13 @@ public class Train extends BaseClass implements Comparable { private Vector lastBlocks = new Vector(); public int speed = 0; - private Autopilot autopilot = null; + public Autopilot autopilot = null; private Route nextRoute; private static final String SHUNTING = "shunting"; private boolean shunting = false; private boolean reserving; // used to prevent recursive calls of reserveNext - private class Autopilot extends Thread{ - boolean stop = false; - int waitTime = 100; - - public Autopilot() { - setName(Application.threadName("Autopilot("+Train.this+")")); - start(); - } - - @Override - public void run() { - try { - stop = false; - while (true) { - if (isNull(route)) { - Thread.sleep(waitTime); - if (waitTime > 100) waitTime /=2; - if (stop) break; - if (isNull(route)) { // may have been set by start action in between - String message = Train.this.start(); - if (isSet(route)) { - LOG.debug("{}.start called, route now is {}",Train.this,route); - plan.stream(message); - //if (isSet(destination)) Thread.sleep(1000); // limit load on PathFinder - } else { - LOG.debug(message); - waitTime = 1000; // limit load on PathFinder - } - } - } else { - if (stop) break; - Thread.sleep(250); - } - } - } catch (Exception e) { - e.printStackTrace(); - } - autopilot = null; - if (isSet(currentBlock)) plan.place(currentBlock); - } - } - public static Object action(HashMap params, Plan plan) throws IOException { String action = params.get(ACTION); if (isNull(action)) return t("No action passed to Train.action!"); @@ -220,7 +179,7 @@ public class Train extends BaseClass implements Comparable { public String automatic() { if (isNull(autopilot)) { - autopilot = new Autopilot(); + autopilot = new Autopilot(this); if (isSet(currentBlock)) plan.place(currentBlock); } return t("{} now in auto-mode",this); @@ -727,7 +686,7 @@ public class Train extends BaseClass implements Comparable { nextRoute = null; } if (isSet(autopilot)) { - autopilot.stop = true; + autopilot.doStop(); autopilot = null; if (isSet(currentBlock)) plan.place(currentBlock); return t("{} stopping at next block.",this); @@ -925,11 +884,8 @@ public class Train extends BaseClass implements Comparable { } public void setWaitTime(Range waitTime) { - if (isNull(autopilot)) return; - autopilot.waitTime = waitTime.random(); - String msg = t("{} waiting {} secs...",this,autopilot.waitTime/1000d); - LOG.debug(msg); - plan.stream(msg); + if (isNull(autopilot)) return; + autopilot.waitTime(waitTime); } private Tag slower(int steps) { diff --git a/src/main/java/de/srsoftware/web4rail/threads/Autopilot.java b/src/main/java/de/srsoftware/web4rail/threads/Autopilot.java new file mode 100644 index 0000000..999ff24 --- /dev/null +++ b/src/main/java/de/srsoftware/web4rail/threads/Autopilot.java @@ -0,0 +1,68 @@ +package de.srsoftware.web4rail.threads; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import de.srsoftware.web4rail.Application; +import de.srsoftware.web4rail.BaseClass; +import de.srsoftware.web4rail.Range; +import de.srsoftware.web4rail.moving.Train; + +public class Autopilot extends Thread{ + + private static final Logger LOG = LoggerFactory.getLogger(Autopilot.class); + + boolean stop = false; + private Train train; + int waitTime = 100; + + public Autopilot(Train train) { + this.train = train; + setName(Application.threadName("Autopilot("+train+")")); + start(); + } + + public void doStop() { + stop = true; + } + + @Override + public void run() { + try { + stop = false; + while (true) { + if (BaseClass.isNull(train.route())) { + Thread.sleep(waitTime); + if (waitTime > 100) waitTime /=2; + if (stop) break; + if (BaseClass.isNull(train.route())) { // may have been set by start action in between + String message = train.start(); + if (BaseClass.isSet(train.route())) { + LOG.debug("{}.start called, route now is {}",train,train.route()); + BaseClass.plan.stream(message); + //if (isSet(destination)) Thread.sleep(1000); // limit load on PathFinder + } else { + LOG.debug(message); + waitTime = 1000; // limit load on PathFinder + } + } + } else { + if (stop) break; + Thread.sleep(250); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + train.autopilot = null; + if (BaseClass.isSet(train.currentBlock())) BaseClass.plan.place(train.currentBlock()); + } + + public void waitTime(Range wt) { + this.waitTime = wt.random(); + String msg = BaseClass.t("{} waiting {} secs...",this,waitTime/1000d); + LOG.debug(msg); + BaseClass.plan.stream(msg); + + } +} diff --git a/src/main/java/de/srsoftware/web4rail/threads/BrakeProcessor.java b/src/main/java/de/srsoftware/web4rail/threads/BrakeProcessor.java new file mode 100644 index 0000000..86f2c0d --- /dev/null +++ b/src/main/java/de/srsoftware/web4rail/threads/BrakeProcessor.java @@ -0,0 +1,145 @@ +package de.srsoftware.web4rail.threads; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import de.srsoftware.web4rail.Application; +import de.srsoftware.web4rail.BaseClass; +import de.srsoftware.web4rail.Route; +import de.srsoftware.web4rail.moving.Train; + +public class BrakeProcessor extends Thread { + public static final Logger LOG = LoggerFactory.getLogger(BrakeProcessor.class); + + + private long latestTick; + private static final int SPEED_STEP = 5; + private Integer timeStep; + private Route route; + private Train train; + private String brakeId; + private long estimatedDistance; // Unit: s*km/h "km/h-Sekunden" + private int startSpeed,endSpeed; + private boolean aborted,modified,finished; + public static int defaultEndSpeed = 10; + + public BrakeProcessor(Route route, Train train) { + this.train = train; + this.route = route; + + aborted = false; + modified = false; + finished = false; + brakeId = train.brakeId(); + startSpeed = train.speed; + endSpeed = defaultEndSpeed; + + timeStep = route.brakeTime(brakeId); + + if (BaseClass.isNull(timeStep) || timeStep>1000000) timeStep = 256; // if no brake time is available for this train + setName(Application.threadName("BrakeProcessor("+train+")")); + start(); + } + + public void abort() { + aborted = true; + } + + private long calcDistance(Integer ts) { + long dist = 0; + int s = startSpeed; + while (s > defaultEndSpeed) { + s -= SPEED_STEP; + dist += s*ts; + } + LOG.debug("Estimated distamce with {} ms timestep: {}",ts,dist); + return dist; + } + + private void checkNextRoute() { + Route nextRoute = train.nextRoute(); + if (BaseClass.isSet(nextRoute) && nextRoute.state() == Route.State.PREPARED) { // auf Startgeschwindigkeit der Nachfolgeroute bremsen + Integer nextRouteStartSpeed = nextRoute.startSpeed(); + if (BaseClass.isSet(nextRouteStartSpeed)) { + LOG.debug("updating target velocity from {} to {}!",endSpeed,nextRouteStartSpeed); + endSpeed = nextRouteStartSpeed; + modified = true; + if (endSpeed > train.speed) train.setSpeed(endSpeed); + } + } + } + + /** + * This is called from route.finish when train came to stop + */ + public void finish() { + LOG.debug("BrakeProcessor.finish()"); + finished = true; + if (aborted || modified) return; + increaseDistance(); + train.setSpeed(0); + LOG.debug("Estimated distance: {}",estimatedDistance); + + if (startSpeed <= endSpeed) return; + if (timeStep<0) timeStep = 100; + Integer newTimeStep = timeStep; + long calculated; + int step = 32*newTimeStep; + for (int i=0; i<20; i++) { + step = step/2; + if (step<1) step = 1; + calculated = calcDistance(newTimeStep); + LOG.debug("Calculated distance for step = {} ms: {}",newTimeStep,calculated); + LOG.debug("Update step: {}",step); + newTimeStep = newTimeStep + (calculated > estimatedDistance ? -step : step); + } + + if (!newTimeStep.equals(timeStep)) { + route.brakeTime(brakeId,newTimeStep); + calculated = calcDistance(newTimeStep); + LOG.debug("Corrected brake timestep for {} @ {} from {} to {} ms.",train,route,timeStep,newTimeStep); + LOG.debug("Differemce from estimated distance: {} ({}%)",estimatedDistance-calculated,100*(estimatedDistance-calculated)/(float)estimatedDistance); + } + } + + private void increaseDistance(){ + long tick = BaseClass.timestamp(); + estimatedDistance += train.speed * (3+tick-latestTick); + latestTick = tick; + } + + @Override + public void run() { + setName(Application.threadName("BreakeProcessor("+train+")")); + LOG.debug("started BrakeProcessor ({} → {}) for {} with timestep = {} ms.",train.speed,endSpeed,train,timeStep); + estimatedDistance = 0; + latestTick = BaseClass.timestamp(); + while (train.speed > endSpeed) { + if (finished || aborted) return; + increaseDistance(); + LOG.debug("BrakeProcessor({}) setting Speed of {}.",route,train); + train.setSpeed(Math.max(train.speed - SPEED_STEP,endSpeed)); + if (!modified) checkNextRoute(); + try { + sleep(timeStep); + } catch (InterruptedException e) { + LOG.warn("BrakeProcessor interrupted!", e); + } + } + + while (!finished && !aborted && !modified) { + try { + sleep(1000); + } catch (InterruptedException e) { + LOG.warn("BrakeProcessor interrupted!", e); + } + checkNextRoute(); + } + } + + public void setEndSpeed(Integer newEndSpeed) { + if (BaseClass.isNull(newEndSpeed)) return; + endSpeed = newEndSpeed; + modified = true; + } +} diff --git a/src/main/java/de/srsoftware/web4rail/ControlUnit.java b/src/main/java/de/srsoftware/web4rail/threads/ControlUnit.java similarity index 98% rename from src/main/java/de/srsoftware/web4rail/ControlUnit.java rename to src/main/java/de/srsoftware/web4rail/threads/ControlUnit.java index b5f6878..c93cb19 100644 --- a/src/main/java/de/srsoftware/web4rail/ControlUnit.java +++ b/src/main/java/de/srsoftware/web4rail/threads/ControlUnit.java @@ -1,4 +1,4 @@ -package de.srsoftware.web4rail; +package de.srsoftware.web4rail.threads; import java.io.BufferedReader; import java.io.BufferedWriter; @@ -17,6 +17,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import de.keawe.tools.translations.Translation; +import de.srsoftware.web4rail.Application; +import de.srsoftware.web4rail.Command; +import de.srsoftware.web4rail.Constants; +import de.srsoftware.web4rail.Plan; import de.srsoftware.web4rail.tags.Button; import de.srsoftware.web4rail.tags.Fieldset; import de.srsoftware.web4rail.tags.Form; diff --git a/src/main/java/de/srsoftware/web4rail/DelayedExecution.java b/src/main/java/de/srsoftware/web4rail/threads/DelayedExecution.java similarity index 83% rename from src/main/java/de/srsoftware/web4rail/DelayedExecution.java rename to src/main/java/de/srsoftware/web4rail/threads/DelayedExecution.java index 2610e52..fb95d01 100644 --- a/src/main/java/de/srsoftware/web4rail/DelayedExecution.java +++ b/src/main/java/de/srsoftware/web4rail/threads/DelayedExecution.java @@ -1,4 +1,6 @@ -package de.srsoftware.web4rail; +package de.srsoftware.web4rail.threads; + +import de.srsoftware.web4rail.Application; public abstract class DelayedExecution extends Thread { private int delay; diff --git a/src/main/java/de/srsoftware/web4rail/tiles/Bridge.java b/src/main/java/de/srsoftware/web4rail/tiles/Bridge.java index f5c05e4..b5e3c8b 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/Bridge.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/Bridge.java @@ -9,11 +9,11 @@ import org.json.JSONObject; import de.srsoftware.tools.Tag; import de.srsoftware.web4rail.BaseClass; import de.srsoftware.web4rail.Connector; -import de.srsoftware.web4rail.DelayedExecution; import de.srsoftware.web4rail.Route; import de.srsoftware.web4rail.moving.Train; import de.srsoftware.web4rail.tags.Fieldset; import de.srsoftware.web4rail.tags.Window; +import de.srsoftware.web4rail.threads.DelayedExecution; public abstract class Bridge extends Tile { private static final String COUNTERPART = "counterpart"; diff --git a/src/main/java/de/srsoftware/web4rail/tiles/Contact.java b/src/main/java/de/srsoftware/web4rail/tiles/Contact.java index 7c4b2cc..889302e 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/Contact.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/Contact.java @@ -16,7 +16,6 @@ import org.slf4j.LoggerFactory; import de.srsoftware.tools.Tag; import de.srsoftware.web4rail.Application; import de.srsoftware.web4rail.BaseClass; -import de.srsoftware.web4rail.DelayedExecution; import de.srsoftware.web4rail.Route; import de.srsoftware.web4rail.actions.Action; import de.srsoftware.web4rail.actions.ActionList; @@ -24,6 +23,7 @@ import de.srsoftware.web4rail.tags.Fieldset; import de.srsoftware.web4rail.tags.Input; import de.srsoftware.web4rail.tags.Select; import de.srsoftware.web4rail.tags.Window; +import de.srsoftware.web4rail.threads.DelayedExecution; public class Contact extends Tile{ private static Logger LOG = LoggerFactory.getLogger(Contact.class); From 4cade0a12eab6eda4629e30536d492d581563b74 Mon Sep 17 00:00:00 2001 From: Stephan Richter Date: Sun, 7 Mar 2021 23:05:50 +0100 Subject: [PATCH 02/26] removed route managing code: preparing to re-implement --- pom.xml | 2 +- .../java/de/srsoftware/web4rail/Plan.java | 11 +- .../java/de/srsoftware/web4rail/Route.java | 114 +--------- .../de/srsoftware/web4rail/moving/Train.java | 200 ++---------------- .../web4rail/threads/Autopilot.java | 68 ------ .../web4rail/threads/BrakeProcessor.java | 145 ------------- .../de/srsoftware/web4rail/tiles/Block.java | 2 +- 7 files changed, 26 insertions(+), 516 deletions(-) delete mode 100644 src/main/java/de/srsoftware/web4rail/threads/Autopilot.java delete mode 100644 src/main/java/de/srsoftware/web4rail/threads/BrakeProcessor.java diff --git a/pom.xml b/pom.xml index 7af5c8e..c6655c1 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 de.srsoftware web4rail - 1.3.50 + 1.3.51 Web4Rail jar Java Model Railway Control diff --git a/src/main/java/de/srsoftware/web4rail/Plan.java b/src/main/java/de/srsoftware/web4rail/Plan.java index e03f542..f6a3fdf 100644 --- a/src/main/java/de/srsoftware/web4rail/Plan.java +++ b/src/main/java/de/srsoftware/web4rail/Plan.java @@ -36,7 +36,6 @@ import de.srsoftware.web4rail.tags.Input; import de.srsoftware.web4rail.tags.Label; import de.srsoftware.web4rail.tags.Table; import de.srsoftware.web4rail.tags.Window; -import de.srsoftware.web4rail.threads.BrakeProcessor; import de.srsoftware.web4rail.threads.ControlUnit; import de.srsoftware.web4rail.tiles.Block; import de.srsoftware.web4rail.tiles.BlockContact; @@ -153,7 +152,7 @@ public class Plan extends BaseClass{ private static final String SPEED_UNIT = "speed_unit"; private static final String LENGTH_UNIT = "length_unit"; private static final String CONFIRM = "confirm"; - private static final String FINAL_SPEED = "final_speed"; +// private static final String FINAL_SPEED = "final_speed"; // TODO private static final String FREE_BEHIND_TRAIN = "free_behind_train"; private static final String RENAME = "rename"; private String name = DEFAULT_NAME; @@ -357,7 +356,7 @@ public class Plan extends BaseClass{ new Input(ACTION,ACTION_UPDATE).hideIn(form); new Input(LENGTH_UNIT, lengthUnit).addTo(new Label(t("Length unit")+COL)).addTo(form); new Input(SPEED_UNIT, speedUnit).addTo(new Label(t("Speed unit")+COL)).addTo(form); - new Input(FINAL_SPEED, BrakeProcessor.defaultEndSpeed).addTo(new Label(t("Lower speed limit")+COL)).attr("title", t("Final speed after breaking, before halting")).addTo(form); + //new Input(FINAL_SPEED, BrakeProcessor.defaultEndSpeed).addTo(new Label(t("Lower speed limit")+COL)).attr("title", t("Final speed after breaking, before halting")).addTo(form); // TODO new Checkbox(FREE_BEHIND_TRAIN, t("Free tiles behind train"), Route.freeBehindTrain).attr("title", t("If checked, tiles behind the train are freed according to the length of the train and the tiles. If it is unchecked, tiles will not get free before route is finished.")).addTo(form); new Button(t("Save"), form).addTo(form); form.addTo(fieldset); @@ -491,7 +490,7 @@ public class Plan extends BaseClass{ .forEach(jTiles::put); return new JSONObject() - .put(FINAL_SPEED, BrakeProcessor.defaultEndSpeed) +// .put(FINAL_SPEED, BrakeProcessor.defaultEndSpeed) // TODO .put(FREE_BEHIND_TRAIN, Route.freeBehindTrain) .put(LENGTH_UNIT, lengthUnit) .put(SPEED_UNIT, speedUnit) @@ -525,7 +524,7 @@ public class Plan extends BaseClass{ if (json.has(TILE)) json.getJSONArray(TILE).forEach(object -> Tile.load(object, plan)); if (json.has(LENGTH_UNIT)) lengthUnit = json.getString(LENGTH_UNIT); if (json.has(SPEED_UNIT)) speedUnit = json.getString(SPEED_UNIT); - if (json.has(FINAL_SPEED)) BrakeProcessor.defaultEndSpeed = json.getInt(FINAL_SPEED); +// if (json.has(FINAL_SPEED)) BrakeProcessor.defaultEndSpeed = json.getInt(FINAL_SPEED); // TODOO if (json.has(FREE_BEHIND_TRAIN)) Route.freeBehindTrain = json.getBoolean(FREE_BEHIND_TRAIN); try { @@ -984,7 +983,7 @@ public class Plan extends BaseClass{ if (params.containsKey(LENGTH_UNIT)) lengthUnit = params.get(LENGTH_UNIT); if (params.containsKey(SPEED_UNIT)) speedUnit = params.get(SPEED_UNIT); - if (params.containsKey(FINAL_SPEED)) BrakeProcessor.defaultEndSpeed = Integer.parseInt(params.get(FINAL_SPEED)); +// if (params.containsKey(FINAL_SPEED)) BrakeProcessor.defaultEndSpeed = Integer.parseInt(params.get(FINAL_SPEED)); // TODO Route.freeBehindTrain = "on".equalsIgnoreCase(params.get(FREE_BEHIND_TRAIN)); return t("Plan updated."); diff --git a/src/main/java/de/srsoftware/web4rail/Route.java b/src/main/java/de/srsoftware/web4rail/Route.java index 7b6afd0..b395b05 100644 --- a/src/main/java/de/srsoftware/web4rail/Route.java +++ b/src/main/java/de/srsoftware/web4rail/Route.java @@ -37,7 +37,6 @@ import de.srsoftware.web4rail.tags.Fieldset; import de.srsoftware.web4rail.tags.Input; import de.srsoftware.web4rail.tags.Table; import de.srsoftware.web4rail.tags.Window; -import de.srsoftware.web4rail.threads.BrakeProcessor; import de.srsoftware.web4rail.tiles.Block; import de.srsoftware.web4rail.tiles.BlockContact; import de.srsoftware.web4rail.tiles.Contact; @@ -79,7 +78,7 @@ public class Route extends BaseClass { private static HashMap names = new HashMap(); // maps id to name. needed to keep names during plan.analyze() - private BrakeProcessor brakeProcessor = null; +// private BrakeProcessor brakeProcessor = null; private HashMap brakeTimes = new HashMap(); private ConditionList conditions; private Vector contacts; @@ -270,7 +269,7 @@ public class Route extends BaseClass { public void brakeStart() { if (isNull(train)) return; - brakeProcessor = new BrakeProcessor(this,train); +// brakeProcessor = new BrakeProcessor(this,train); } protected Route clone() { @@ -388,51 +387,9 @@ public class Route extends BaseClass { public void finish() { LOG.debug("{}.finish()",this); - if (isSet(train)) { - Route nextRoute = train.nextRoute(); - if (isSet(nextRoute)) { - LOG.debug("{} has next route: {}",train,nextRoute); - if (isSet(brakeProcessor)) brakeProcessor.abort(); - } else { - LOG.debug("{} has no next route.",train); - if (isSet(brakeProcessor)) { - brakeProcessor.finish(); - } else train.setSpeed(0); - } - } - brakeProcessor = null; - - free(); - - if (isSet(train)) { - moveTrainToEndBlock(); - if (train.route() == this) train.route(null); - train = null; - } - - state = State.FREE; + // TODO } - /** - * sets all signals of this route to RED, - * frees all tiles occupied by this route - */ - private void free() { - LOG.debug("{}.free()",this); - context.clear(); // prevent delayed actions from firing after route has finished - - setSignals(Signal.RED); - for (Tile tile : path) try { // remove route from tiles on path - tile.unset(this); - } catch (IllegalArgumentException e) {} - -/* Tile lastTile = path.lastElement(); - if (lastTile instanceof Contact) { - lastTile.setTrain(null); - if (isSet(train)) train.removeChild(lastTile); - }*/ - } - private String generateName() { StringBuilder sb = new StringBuilder(); for (int i=0; i0; i--) { - switch (destId.charAt(i)) { - case Train.FLAG_SEPARATOR: - destId = destId.substring(0,i); - i=0; - break; - case Train.TURN_FLAG: - turn = true; - LOG.debug("Turn flag is set!"); - break; - } - } - if (destId.equals(endBlock.id().toString())) { - if (turn) train.turn(); - - // update destination tag: remove and add altered tag: - train.removeTag(destTag); - destTag = destTag.substring(parts[1].length()+1); - if (destTag.isEmpty()) { // no further destinations - destTag = null; - } else train.addTag(destTag); - } - } - - if (isNull(destTag)) { - train.quitAutopilot(); - plan.stream(t("{} reached it`s destination!",train)); - } - } else train.setWaitTime(endBlock.getWaitTime(train,train.direction())); - if (startBlock.train() == train && !train.onTrace(startBlock)) startBlock.setTrain(null); // withdraw train from start block only if trace does not go back there - } - public List multiply(int size) { Vector routes = new Vector(); for (int i=0; i preForm, FormInput formInputs, List
postForm) { @@ -853,15 +754,8 @@ public class Route extends BaseClass { public boolean reset() { LOG.debug("{}.reset()",this); - free(); - if (isSet(brakeProcessor)) brakeProcessor.abort(); - if (isSet(train)) { - train.set(startBlock); - train.heading(startDirection); - if (train.route() == this) train.route(null); - train = null; - } + // TODO state = State.FREE; return true; } diff --git a/src/main/java/de/srsoftware/web4rail/moving/Train.java b/src/main/java/de/srsoftware/web4rail/moving/Train.java index a40e186..937102c 100644 --- a/src/main/java/de/srsoftware/web4rail/moving/Train.java +++ b/src/main/java/de/srsoftware/web4rail/moving/Train.java @@ -23,12 +23,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import de.srsoftware.tools.Tag; -import de.srsoftware.web4rail.Application; import de.srsoftware.web4rail.BaseClass; -import de.srsoftware.web4rail.PathFinder; import de.srsoftware.web4rail.Plan; import de.srsoftware.web4rail.Plan.Direction; -import de.srsoftware.web4rail.Range; import de.srsoftware.web4rail.Route; import de.srsoftware.web4rail.tags.Button; import de.srsoftware.web4rail.tags.Checkbox; @@ -39,9 +36,7 @@ import de.srsoftware.web4rail.tags.Label; import de.srsoftware.web4rail.tags.Select; import de.srsoftware.web4rail.tags.Table; import de.srsoftware.web4rail.tags.Window; -import de.srsoftware.web4rail.threads.Autopilot; import de.srsoftware.web4rail.tiles.Block; -import de.srsoftware.web4rail.tiles.Contact; import de.srsoftware.web4rail.tiles.Tile; /** @@ -89,13 +84,9 @@ public class Train extends BaseClass implements Comparable { private Vector lastBlocks = new Vector(); public int speed = 0; - public Autopilot autopilot = null; - private Route nextRoute; private static final String SHUNTING = "shunting"; private boolean shunting = false; - private boolean reserving; // used to prevent recursive calls of reserveNext - public static Object action(HashMap params, Plan plan) throws IOException { String action = params.get(ACTION); if (isNull(action)) return t("No action passed to Train.action!"); @@ -178,11 +169,8 @@ public class Train extends BaseClass implements Comparable { } public String automatic() { - if (isNull(autopilot)) { - autopilot = new Autopilot(this); - if (isSet(currentBlock)) plan.place(currentBlock); - } - return t("{} now in auto-mode",this); + return "not implemented"; + } private Fieldset blockHistory() { @@ -349,38 +337,8 @@ public class Train extends BaseClass implements Comparable { return properties(); } - public Block destination() { - //LOG.debug("{}.destination()",this); - if (isNull(destination)) { - String destTag = destinationTag(); - //LOG.debug("→ processing \"{}\"...",destTag); - if (isSet(destTag)) { - destTag = destTag.split(DESTINATION_PREFIX)[1]; - //LOG.debug("....processing \"{}\"…",destTag); - for (int i=destTag.length()-1; i>0; i--) { - switch (destTag.charAt(i)) { - case FLAG_SEPARATOR: - destTag = destTag.substring(0,i); - i=0; - break; - case SHUNTING_FLAG: - //LOG.debug("....enabled shunting option"); - shunting = true; - break; - } - } - - Block block = BaseClass.get(new Id(destTag)); - if (isSet(block)) destination(block); - } - }// else LOG.debug("→ heading towards {}",destination); - return destination; - } - - public Train destination(Block dest) { - LOG.debug("destination({})",dest); - destination = dest; - return this; + public Block destination(){ + return null; // TODO } public String destinationTag() { @@ -393,7 +351,7 @@ public class Train extends BaseClass implements Comparable { public String directedName() { String result = name(); - String mark = isSet(autopilot) ? "ⓐ" : ""; + String mark = ""; //isSet(autopilot) ? "ⓐ" : ""; if (isNull(direction)) return result; switch (direction) { case NORTH: @@ -553,8 +511,8 @@ public class Train extends BaseClass implements Comparable { String.join(", ", train.tags()), train.route, isSet(train.currentBlock) ? train.currentBlock.link() : null, - train.destination(), - t(isSet(train.autopilot)?"On":"Off") + null, // TODO: show destination here! + null // TODO: show state of autopilot here ); }); table.addTo(win); @@ -617,10 +575,6 @@ public class Train extends BaseClass implements Comparable { return this; } - public Route nextRoute() { - return nextRoute; - } - public boolean onTrace(Tile t) { return trace.contains(t); } @@ -681,16 +635,8 @@ public class Train extends BaseClass implements Comparable { } public Object quitAutopilot() { - if (isSet(nextRoute)) { - nextRoute.reset(); - nextRoute = null; - } - if (isSet(autopilot)) { - autopilot.doStop(); - autopilot = null; - if (isSet(currentBlock)) plan.place(currentBlock); - return t("{} stopping at next block.",this); - } else return t("autopilot not active."); + // TODO Auto-generated method stub + return "not implemented"; } @Override @@ -711,7 +657,7 @@ public class Train extends BaseClass implements Comparable { public void removeChild(BaseClass child) { LOG.debug("{}.removeChild({})",this,child); if (child == route) route = null; - if (child == nextRoute) nextRoute = null; + //if (child == nextRoute) nextRoute = null; // TODO if (child == currentBlock) currentBlock = null; if (child == destination) destination = null; cars.remove(child); @@ -725,35 +671,8 @@ public class Train extends BaseClass implements Comparable { } public void reserveNext() { - if (reserving) return; - LOG.debug("{}.reserveNext()",this); - if (isSet(nextRoute)) { - LOG.debug("Train already has next route: {}",nextRoute); - return; - } - Context context = new Context(this).route(route).block(route.endBlock()).direction(route.endDirection); - Route newRoute = PathFinder.chooseRoute(context); - if (isNull(newRoute)) { - LOG.debug("{}.reserveNext() found no available route!",this); - return; - } - reserving = true; - LOG.debug("next route: {}",newRoute); - newRoute.set(context); - boolean error = !newRoute.lockIgnoring(route); - error = error || !newRoute.prepare(); - - if (error) { - newRoute.reset(); // may unlock tiles belonging to the current route. - LOG.debug("failed to prepare new route {}",newRoute); - route.lock(); // corrects unlocked tiles of nextRoute - } else { - nextRoute = newRoute; - LOG.debug("prepared next route: {}",nextRoute); - } - reserving = false; + // TODO } - /** * This turns the train as if it went through a loop. Example: * before: CabCar→ MiddleCar→ Loco→ @@ -782,12 +701,6 @@ public class Train extends BaseClass implements Comparable { return route; } - public Train route(Route newRoute) { - route = newRoute; - if (isNull(route)) shunting = false; // quit shunting on finish route - return this; - } - public static void saveAll(String filename) throws IOException { BufferedWriter file = new BufferedWriter(new FileWriter(filename)); for (Train train:BaseClass.listElements(Train.class)) file.write(train.json()+"\n"); @@ -831,7 +744,7 @@ public class Train extends BaseClass implements Comparable { if (isNull(tile)) return t("Tile {} not known!",dest); if (tile instanceof Block) { destination = (Block) tile; - automatic(); + start(); return t("{} now heading for {}",this,destination); } return t("{} is not a block!",tile); @@ -883,11 +796,6 @@ public class Train extends BaseClass implements Comparable { LOG.debug("new trace of {}: {}",this,trace); } - public void setWaitTime(Range waitTime) { - if (isNull(autopilot)) return; - autopilot.waitTime(waitTime); - } - private Tag slower(int steps) { setSpeed(speed-steps); return properties(); @@ -921,44 +829,8 @@ public class Train extends BaseClass implements Comparable { } - public String start() throws IOException { - LOG.debug("{}.start()",this); - if (isNull(currentBlock)) return t("{} not in a block",this); - if (maxSpeed() == 0) return t("Train has maximum speed of 0 {}, cannot go!",speedUnit); - if (isSet(route)) route.reset(); // reset route previously chosen - - String error = null; - if (isSet(nextRoute)) { - LOG.debug("{}.nextRoute = {}",this,nextRoute); - route = nextRoute; - if (!route.lock()) return t("Was not able to lock {}",route); - nextRoute = null; - route.set(new Context(this).block(currentBlock).direction(direction)); - } else { - if (reserving) return t("Route chooser already active"); - Context context = new Context(this).block(currentBlock).direction(direction); - route = PathFinder.chooseRoute(context); - if (isNull(route)) return t("No free routes from {}",currentBlock); - LOG.debug("Chosen route: {}",route); - if (!route.lock()) error = t("Was not able to lock {}",route); - route.set(context); - if (isNull(error) && !route.prepare()) error = t("Was not able to fire all setup actions of route!"); - } - if (isNull(route)) return t("Route cancelled"); // route may have been canceled in between - if (isNull(error) && direction != route.startDirection) turn(); - - if (isNull(error) && !route.start(this)) error = t("Was not able to assign {} to {}!",this,route); - if (isSet(error)) { - LOG.debug("{}.start:error = {}",this,error); - route.reset(); - route = null; - return error; - } - startSimulation(); - - String res = t("Started {}",this); - plan.stream(res); - return res; + public String start() { + return "not implemented, yet"; } public static void startAll() { @@ -966,49 +838,7 @@ public class Train extends BaseClass implements Comparable { for (Train train : BaseClass.listElements(Train.class)) LOG.info(train.automatic()); } - private void startSimulation() { - LOG.debug("{}.startSimulation()",this); - for (Contact contact : route.contacts()) { - if (contact.addr() != 0) { - LOG.debug("{}.startSimulation aborted!",this); - return; // simulate train only when all contacts are non-physical - } - } - try { - Thread.sleep(1000); - plan.stream(t("Simulating movement of {}...",this)); - Thread simulation = new Thread() { - public void run() { - for (Tile tile : route.path()) { - if (isNull(route)) break; - try { - if (tile instanceof Contact) { - Contact contact = (Contact) tile; - contact.activate(true); - sleep(200); - contact.activate(false); - } - sleep(1000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - }; - }; - simulation.setName(Application.threadName("Simulation("+Train.this+")")); - simulation.start(); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - public Object stopNow() { - quitAutopilot(); - if (isSet(route)) { - route.reset(); - route = null; - } - setSpeed(0); return properties(); } @@ -1073,6 +903,6 @@ public class Train extends BaseClass implements Comparable { } public boolean usesAutopilot() { - return isSet(autopilot); + return false; // TODO } } diff --git a/src/main/java/de/srsoftware/web4rail/threads/Autopilot.java b/src/main/java/de/srsoftware/web4rail/threads/Autopilot.java deleted file mode 100644 index 999ff24..0000000 --- a/src/main/java/de/srsoftware/web4rail/threads/Autopilot.java +++ /dev/null @@ -1,68 +0,0 @@ -package de.srsoftware.web4rail.threads; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import de.srsoftware.web4rail.Application; -import de.srsoftware.web4rail.BaseClass; -import de.srsoftware.web4rail.Range; -import de.srsoftware.web4rail.moving.Train; - -public class Autopilot extends Thread{ - - private static final Logger LOG = LoggerFactory.getLogger(Autopilot.class); - - boolean stop = false; - private Train train; - int waitTime = 100; - - public Autopilot(Train train) { - this.train = train; - setName(Application.threadName("Autopilot("+train+")")); - start(); - } - - public void doStop() { - stop = true; - } - - @Override - public void run() { - try { - stop = false; - while (true) { - if (BaseClass.isNull(train.route())) { - Thread.sleep(waitTime); - if (waitTime > 100) waitTime /=2; - if (stop) break; - if (BaseClass.isNull(train.route())) { // may have been set by start action in between - String message = train.start(); - if (BaseClass.isSet(train.route())) { - LOG.debug("{}.start called, route now is {}",train,train.route()); - BaseClass.plan.stream(message); - //if (isSet(destination)) Thread.sleep(1000); // limit load on PathFinder - } else { - LOG.debug(message); - waitTime = 1000; // limit load on PathFinder - } - } - } else { - if (stop) break; - Thread.sleep(250); - } - } - } catch (Exception e) { - e.printStackTrace(); - } - train.autopilot = null; - if (BaseClass.isSet(train.currentBlock())) BaseClass.plan.place(train.currentBlock()); - } - - public void waitTime(Range wt) { - this.waitTime = wt.random(); - String msg = BaseClass.t("{} waiting {} secs...",this,waitTime/1000d); - LOG.debug(msg); - BaseClass.plan.stream(msg); - - } -} diff --git a/src/main/java/de/srsoftware/web4rail/threads/BrakeProcessor.java b/src/main/java/de/srsoftware/web4rail/threads/BrakeProcessor.java deleted file mode 100644 index 86f2c0d..0000000 --- a/src/main/java/de/srsoftware/web4rail/threads/BrakeProcessor.java +++ /dev/null @@ -1,145 +0,0 @@ -package de.srsoftware.web4rail.threads; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import de.srsoftware.web4rail.Application; -import de.srsoftware.web4rail.BaseClass; -import de.srsoftware.web4rail.Route; -import de.srsoftware.web4rail.moving.Train; - -public class BrakeProcessor extends Thread { - public static final Logger LOG = LoggerFactory.getLogger(BrakeProcessor.class); - - - private long latestTick; - private static final int SPEED_STEP = 5; - private Integer timeStep; - private Route route; - private Train train; - private String brakeId; - private long estimatedDistance; // Unit: s*km/h "km/h-Sekunden" - private int startSpeed,endSpeed; - private boolean aborted,modified,finished; - public static int defaultEndSpeed = 10; - - public BrakeProcessor(Route route, Train train) { - this.train = train; - this.route = route; - - aborted = false; - modified = false; - finished = false; - brakeId = train.brakeId(); - startSpeed = train.speed; - endSpeed = defaultEndSpeed; - - timeStep = route.brakeTime(brakeId); - - if (BaseClass.isNull(timeStep) || timeStep>1000000) timeStep = 256; // if no brake time is available for this train - setName(Application.threadName("BrakeProcessor("+train+")")); - start(); - } - - public void abort() { - aborted = true; - } - - private long calcDistance(Integer ts) { - long dist = 0; - int s = startSpeed; - while (s > defaultEndSpeed) { - s -= SPEED_STEP; - dist += s*ts; - } - LOG.debug("Estimated distamce with {} ms timestep: {}",ts,dist); - return dist; - } - - private void checkNextRoute() { - Route nextRoute = train.nextRoute(); - if (BaseClass.isSet(nextRoute) && nextRoute.state() == Route.State.PREPARED) { // auf Startgeschwindigkeit der Nachfolgeroute bremsen - Integer nextRouteStartSpeed = nextRoute.startSpeed(); - if (BaseClass.isSet(nextRouteStartSpeed)) { - LOG.debug("updating target velocity from {} to {}!",endSpeed,nextRouteStartSpeed); - endSpeed = nextRouteStartSpeed; - modified = true; - if (endSpeed > train.speed) train.setSpeed(endSpeed); - } - } - } - - /** - * This is called from route.finish when train came to stop - */ - public void finish() { - LOG.debug("BrakeProcessor.finish()"); - finished = true; - if (aborted || modified) return; - increaseDistance(); - train.setSpeed(0); - LOG.debug("Estimated distance: {}",estimatedDistance); - - if (startSpeed <= endSpeed) return; - if (timeStep<0) timeStep = 100; - Integer newTimeStep = timeStep; - long calculated; - int step = 32*newTimeStep; - for (int i=0; i<20; i++) { - step = step/2; - if (step<1) step = 1; - calculated = calcDistance(newTimeStep); - LOG.debug("Calculated distance for step = {} ms: {}",newTimeStep,calculated); - LOG.debug("Update step: {}",step); - newTimeStep = newTimeStep + (calculated > estimatedDistance ? -step : step); - } - - if (!newTimeStep.equals(timeStep)) { - route.brakeTime(brakeId,newTimeStep); - calculated = calcDistance(newTimeStep); - LOG.debug("Corrected brake timestep for {} @ {} from {} to {} ms.",train,route,timeStep,newTimeStep); - LOG.debug("Differemce from estimated distance: {} ({}%)",estimatedDistance-calculated,100*(estimatedDistance-calculated)/(float)estimatedDistance); - } - } - - private void increaseDistance(){ - long tick = BaseClass.timestamp(); - estimatedDistance += train.speed * (3+tick-latestTick); - latestTick = tick; - } - - @Override - public void run() { - setName(Application.threadName("BreakeProcessor("+train+")")); - LOG.debug("started BrakeProcessor ({} → {}) for {} with timestep = {} ms.",train.speed,endSpeed,train,timeStep); - estimatedDistance = 0; - latestTick = BaseClass.timestamp(); - while (train.speed > endSpeed) { - if (finished || aborted) return; - increaseDistance(); - LOG.debug("BrakeProcessor({}) setting Speed of {}.",route,train); - train.setSpeed(Math.max(train.speed - SPEED_STEP,endSpeed)); - if (!modified) checkNextRoute(); - try { - sleep(timeStep); - } catch (InterruptedException e) { - LOG.warn("BrakeProcessor interrupted!", e); - } - } - - while (!finished && !aborted && !modified) { - try { - sleep(1000); - } catch (InterruptedException e) { - LOG.warn("BrakeProcessor interrupted!", e); - } - checkNextRoute(); - } - } - - public void setEndSpeed(Integer newEndSpeed) { - if (BaseClass.isNull(newEndSpeed)) return; - endSpeed = newEndSpeed; - modified = true; - } -} diff --git a/src/main/java/de/srsoftware/web4rail/tiles/Block.java b/src/main/java/de/srsoftware/web4rail/tiles/Block.java index 6c449ea..5bf4d4e 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/Block.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/Block.java @@ -407,7 +407,7 @@ public abstract class Block extends StretchableTile{ Train newTrain = Train.get(trainId); if (isSet(newTrain) && newTrain != train) { newTrain.dropTrace(); - if (connections(newTrain.direction()).isEmpty()) newTrain.heading(null); + if (connections(newTrain.direction()).isEmpty()) newTrain.heading(null); newTrain.set(this); } } From 966c5340ba3fdb5d74f74ec66b8ab442f740dc77 Mon Sep 17 00:00:00 2001 From: Stephan Richter Date: Mon, 8 Mar 2021 00:00:00 +0100 Subject: [PATCH 03/26] started reimplementation of route manager --- pom.xml | 2 +- .../de/srsoftware/web4rail/BaseClass.java | 7 +++ .../java/de/srsoftware/web4rail/Route.java | 16 ++++--- .../de/srsoftware/web4rail/moving/Train.java | 46 ++++++++++++++---- .../web4rail/{ => threads}/PathFinder.java | 47 +++++++++++++++++-- .../de/srsoftware/web4rail/tiles/Tile.java | 2 +- 6 files changed, 99 insertions(+), 21 deletions(-) rename src/main/java/de/srsoftware/web4rail/{ => threads}/PathFinder.java (84%) diff --git a/pom.xml b/pom.xml index c6655c1..b337397 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 de.srsoftware web4rail - 1.3.51 + 1.3.52 Web4Rail jar Java Model Railway Control diff --git a/src/main/java/de/srsoftware/web4rail/BaseClass.java b/src/main/java/de/srsoftware/web4rail/BaseClass.java index a098ab8..0c75070 100644 --- a/src/main/java/de/srsoftware/web4rail/BaseClass.java +++ b/src/main/java/de/srsoftware/web4rail/BaseClass.java @@ -510,6 +510,13 @@ public abstract class BaseClass implements Constants{ customFieldNames = new HashMap, Set>(); } + public void sleep(long ms) { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } public static String t(String txt, Object...fills) { if (isSet(fills)) for (int i=0; i ignoredPath = new HashSet(); if (isSet(ignoredRoute)) ignoredPath.addAll(ignoredRoute.path); - boolean success = true; for (Tile tile : path) { if (ignoredPath.contains(tile)) continue; try { tile.setRoute(this); } catch (IllegalStateException e) { LOG.debug("{}.lockIgnoring(...) failed at {}, rolling back",this,tile); - success = false; - break; + for (Tile lockedTile : path) { // unlock the same tiles that have been locked before, until we encounter the unlockable tile + if (lockedTile == tile) return false; + lockedTile.unset(this); + } + return false; } } - if (success) state = State.LOCKED; - return success; + state = State.LOCKED; + return true; } public List multiply(int size) { diff --git a/src/main/java/de/srsoftware/web4rail/moving/Train.java b/src/main/java/de/srsoftware/web4rail/moving/Train.java index 937102c..d957fd8 100644 --- a/src/main/java/de/srsoftware/web4rail/moving/Train.java +++ b/src/main/java/de/srsoftware/web4rail/moving/Train.java @@ -36,6 +36,7 @@ import de.srsoftware.web4rail.tags.Label; import de.srsoftware.web4rail.tags.Select; import de.srsoftware.web4rail.tags.Table; import de.srsoftware.web4rail.tags.Window; +import de.srsoftware.web4rail.threads.PathFinder; import de.srsoftware.web4rail.tiles.Block; import de.srsoftware.web4rail.tiles.Tile; @@ -168,9 +169,8 @@ public class Train extends BaseClass implements Comparable { return this; } - public String automatic() { - return "not implemented"; - + public boolean automatic() { + return false; } private Fieldset blockHistory() { @@ -776,6 +776,11 @@ public class Train extends BaseClass implements Comparable { return properties(); } + protected Route setRoute(Route newRoute) { + route = newRoute; + return route; + } + public void setSpeed(int newSpeed) { LOG.debug("{}.setSpeed({})",this,newSpeed); speed = Math.min(newSpeed,maxSpeed()); @@ -829,15 +834,40 @@ public class Train extends BaseClass implements Comparable { } - public String start() { - return "not implemented, yet"; + public void start() { + Context context = new Context(this).block(currentBlock).direction(direction); + new PathFinder(context) { + + @Override + public void found(Route r) { + // TODO Auto-generated method stub + LOG.debug("Route {} prepared for {}",r,Train.this); + } + + @Override + public void locked(Route r) { + // TODO Auto-generated method stub + LOG.debug("Route {} locked for {}",r,Train.this); + } + + @Override + public void prepared(Route r) { + LOG.debug("Route {} prepared for {}",r,Train.this); + setRoute(r).start(Train.this); + } + }; } - + public static void startAll() { LOG.debug("Train.startAll()"); - for (Train train : BaseClass.listElements(Train.class)) LOG.info(train.automatic()); + for (Train train : BaseClass.listElements(Train.class)) LOG.info(train.startAutopilot()); } - + + private String startAutopilot() { + // TODO Auto-generated method stub + return null; + } + public Object stopNow() { return properties(); diff --git a/src/main/java/de/srsoftware/web4rail/PathFinder.java b/src/main/java/de/srsoftware/web4rail/threads/PathFinder.java similarity index 84% rename from src/main/java/de/srsoftware/web4rail/PathFinder.java rename to src/main/java/de/srsoftware/web4rail/threads/PathFinder.java index 731ac90..5c27806 100644 --- a/src/main/java/de/srsoftware/web4rail/PathFinder.java +++ b/src/main/java/de/srsoftware/web4rail/threads/PathFinder.java @@ -1,4 +1,4 @@ -package de.srsoftware.web4rail; +package de.srsoftware.web4rail.threads; import java.util.HashSet; import java.util.List; @@ -9,15 +9,27 @@ import java.util.Vector; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import de.srsoftware.web4rail.BaseClass; import de.srsoftware.web4rail.Plan.Direction; +import de.srsoftware.web4rail.Route; import de.srsoftware.web4rail.moving.Train; import de.srsoftware.web4rail.tiles.Block; /** * @author Stephan Richter, SRSoftware 2020-2021 */ -public class PathFinder extends BaseClass{ +public abstract class PathFinder extends BaseClass implements Runnable{ public static final Logger LOG = LoggerFactory.getLogger(PathFinder.class); + private Context context; + private boolean aborted = false; + + public PathFinder(Context context) { + this.context = context; + } + + public void abort() { + aborted = true; + } private static TreeMap> availableRoutes(Context context,HashSet visitedRoutes){ String inset = ""; @@ -108,9 +120,9 @@ public class PathFinder extends BaseClass{ return availableRoutes; } - public static Route chooseRoute(Context context) { - LOG.debug("PathFinder.chooseRoute({})",context); - TreeMap> availableRoutes = PathFinder.availableRoutes(context,new HashSet()); + public Route chooseRoute() { + LOG.debug("PathFinder.chooseRoute()"); + TreeMap> availableRoutes = availableRoutes(context,new HashSet()); while (!availableRoutes.isEmpty()) { LOG.debug("availableRoutes: {}",availableRoutes); Entry> entry = availableRoutes.lastEntry(); @@ -128,4 +140,29 @@ public class PathFinder extends BaseClass{ } return null; } + + @Override + public void run() { + while (true) { + Route route = chooseRoute(); + if (aborted) return; + if (isSet(route)) { + found(route); + if (aborted) return; + if (route.lock()) { + locked(route); + if (aborted) return; + if (route.prepare()) { + prepared(route); + return; + } + } + } + sleep(1000); + } + } + + public abstract void locked(Route r); + public abstract void found(Route r); + public abstract void prepared(Route r); } diff --git a/src/main/java/de/srsoftware/web4rail/tiles/Tile.java b/src/main/java/de/srsoftware/web4rail/tiles/Tile.java index d7d45dc..def0924 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/Tile.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/Tile.java @@ -22,7 +22,6 @@ import org.slf4j.LoggerFactory; import de.srsoftware.tools.Tag; import de.srsoftware.web4rail.BaseClass; import de.srsoftware.web4rail.Connector; -import de.srsoftware.web4rail.PathFinder; import de.srsoftware.web4rail.Plan; import de.srsoftware.web4rail.Plan.Direction; import de.srsoftware.web4rail.actions.AlterDirection; @@ -33,6 +32,7 @@ import de.srsoftware.web4rail.tags.Fieldset; import de.srsoftware.web4rail.tags.Input; import de.srsoftware.web4rail.tags.Radio; import de.srsoftware.web4rail.tags.Window; +import de.srsoftware.web4rail.threads.PathFinder; /** * Base class for all tiles From 830c1863ad83cf6acad499121bc9d02463db39db Mon Sep 17 00:00:00 2001 From: Stephan Richter Date: Wed, 10 Mar 2021 01:21:10 +0100 Subject: [PATCH 04/26] started re-implementing route reservation --- pom.xml | 2 +- resources/css/style.css | 18 +- resources/logback.xml | 2 +- .../java/de/srsoftware/web4rail/Route.java | 113 +++++------- .../web4rail/actions/ActionList.java | 5 +- .../web4rail/conditions/BlockFree.java | 2 +- .../de/srsoftware/web4rail/moving/Train.java | 92 +++++++--- .../web4rail/threads/ControlUnit.java | 4 +- .../web4rail/threads/PathFinder.java | 98 ++++++---- .../de/srsoftware/web4rail/tiles/Block.java | 18 +- .../web4rail/tiles/BlockContact.java | 5 - .../de/srsoftware/web4rail/tiles/Bridge.java | 28 ++- .../de/srsoftware/web4rail/tiles/Contact.java | 11 +- .../de/srsoftware/web4rail/tiles/Tile.java | 167 +++++++----------- .../de/srsoftware/web4rail/tiles/Turnout.java | 2 +- .../srsoftware/web4rail/tiles/TurnoutL.java | 4 +- .../srsoftware/web4rail/tiles/TurnoutR.java | 4 +- 17 files changed, 284 insertions(+), 291 deletions(-) diff --git a/pom.xml b/pom.xml index b337397..019ecca 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 de.srsoftware web4rail - 1.3.52 + 1.3.53 Web4Rail jar Java Model Railway Control diff --git a/resources/css/style.css b/resources/css/style.css index 62fc459..60088db 100644 --- a/resources/css/style.css +++ b/resources/css/style.css @@ -77,14 +77,20 @@ svg.Relay rect{ fill: white; } -svg.locked polygon, -svg.locked rect:not(.sig_a):not(.sig_b){ - fill:lime; +svg.allocated polygon, +svg.allocated rect:not(.sig_a):not(.sig_b){ + fill: yellow; } +svg.locked polygon, +svg.locked rect:not(.sig_a):not(.sig_b){ + fill: lime; +} + +.occupied .block, svg.occupied polygon, svg.occupied rect:not(.sig_a):not(.sig_b){ - fill:yellow; + fill: orange; } svg text{ @@ -232,10 +238,6 @@ svg.straight .right{ fill: #ddd !important; } -.occupied .block{ - fill: yellow; -} - .active circle{ fill: #f57900; } diff --git a/resources/logback.xml b/resources/logback.xml index 3013d46..bf19776 100644 --- a/resources/logback.xml +++ b/resources/logback.xml @@ -29,7 +29,7 @@ - + diff --git a/src/main/java/de/srsoftware/web4rail/Route.java b/src/main/java/de/srsoftware/web4rail/Route.java index 293a355..18a62f1 100644 --- a/src/main/java/de/srsoftware/web4rail/Route.java +++ b/src/main/java/de/srsoftware/web4rail/Route.java @@ -10,6 +10,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Stack; import java.util.Vector; import org.json.JSONArray; @@ -44,6 +45,7 @@ import de.srsoftware.web4rail.tiles.Contact; import de.srsoftware.web4rail.tiles.Shadow; import de.srsoftware.web4rail.tiles.Signal; import de.srsoftware.web4rail.tiles.Tile; +import de.srsoftware.web4rail.tiles.Tile.Status; import de.srsoftware.web4rail.tiles.Turnout; /** * A route is a vector of tiles that leads from one block to another. @@ -53,9 +55,9 @@ import de.srsoftware.web4rail.tiles.Turnout; */ public class Route extends BaseClass { - public enum State { +/* public enum State { FREE, LOCKED, PREPARED, STARTED; - } + }*/ public static final Logger LOG = LoggerFactory.getLogger(Route.class); private static final String ACTIONS = "actions"; @@ -70,7 +72,7 @@ public class Route extends BaseClass { static final String PATH = "path"; static final String SIGNALS = "signals"; static final String TURNOUTS = "turnouts"; - private State state = State.FREE; + //private State state = State.FREE; public static boolean freeBehindTrain = true; private static final String ROUTE_START = "route_start"; @@ -79,11 +81,9 @@ public class Route extends BaseClass { private static HashMap names = new HashMap(); // maps id to name. needed to keep names during plan.analyze() -// private BrakeProcessor brakeProcessor = null; private HashMap brakeTimes = new HashMap(); private ConditionList conditions; private Vector contacts; - private Context context; // this context is passed to actions private boolean disabled = false; private Block endBlock = null; public Direction endDirection; @@ -100,7 +100,7 @@ public class Route extends BaseClass { conditions = new ConditionList(); conditions.parent(this); } - + /** * process commands from the client * @param params @@ -121,12 +121,6 @@ public class Route extends BaseClass { plan.stream(t("Removed {}.",route)); return plan.properties(new HashMap()); case ACTION_PROPS: - return route.properties(); - case ACTION_START: - route.set(new Context(route)); - route.prepare(); - route.context.clear(); - return route.properties(); case ACTION_UPDATE: return route.update(params,plan); @@ -204,6 +198,10 @@ public class Route extends BaseClass { turnouts.put(t, s); } + public boolean allocateFor(Train newTrain) { + return pathState(newTrain,Tile.Status.ALLOCATED); + } + /** * checks, whether the route may be used in a given context * @param context @@ -333,6 +331,7 @@ public class Route extends BaseClass { ActionList actions = triggeredActions.get(contact.trigger()); LOG.debug("Contact has id {} / trigger {} and is assigned with {}",contact.id(),contact.trigger(),isNull(actions)?t("nothing"):actions); if (isNull(actions)) return; + Context context = new Context(this).train(train); actions.fire(context,"Route.Contact("+contact.addr()+")"); } @@ -373,10 +372,6 @@ public class Route extends BaseClass { return win; } - public Context context() { - return context.clone(); - } - public void dropBraketimes(String...brakeIds) { for (String brakeId : brakeIds) brakeTimes.remove(brakeId); } @@ -416,10 +411,10 @@ public class Route extends BaseClass { return disabled; } - public boolean isFreeFor(Context context) { - PathFinder.LOG.debug("{}.isFreeFor({})",this,context); + public boolean isFreeFor(Train newTrain) { + PathFinder.LOG.debug("{}.isFreeFor({})",this,newTrain); for (int i=1; i ignoredPath = new HashSet(); - if (isSet(ignoredRoute)) ignoredPath.addAll(ignoredRoute.path); - for (Tile tile : path) { - if (ignoredPath.contains(tile)) continue; - try { - tile.setRoute(this); - } catch (IllegalStateException e) { - LOG.debug("{}.lockIgnoring(...) failed at {}, rolling back",this,tile); - for (Tile lockedTile : path) { // unlock the same tiles that have been locked before, until we encounter the unlockable tile - if (lockedTile == tile) return false; - lockedTile.unset(this); - } - return false; - } - } - state = State.LOCKED; - return true; - } - + public List multiply(int size) { Vector routes = new Vector(); for (int i=0; i visited = new Stack<>(); + for (Tile t : path) { + if (t.setState(newState,newTrain)) { + visited.push(t); + } else { + while (!visited.isEmpty()) visited.pop().free(); + return false; + } + } + return true; + } + + public boolean prepareFor(Train newTrain) { +// if (state == State.PREPARED || state == State.STARTED) return true; LOG.debug("{}.prepare()",this); ActionList setupActions = triggeredActions.get(ROUTE_SETUP); - if (isSet(setupActions) && !setupActions.fire(context,this+".prepare()")) return false; - state = State.PREPARED; + if (isSet(setupActions) && !setupActions.fire(new Context(newTrain).route(this),this+".prepare()")) return false; +// state = State.PREPARED; + pathState(newTrain,Tile.Status.LOCKED); return true; } @@ -760,7 +743,6 @@ public class Route extends BaseClass { LOG.debug("{}.reset()",this); // TODO - state = State.FREE; return true; } @@ -778,13 +760,6 @@ public class Route extends BaseClass { file.close(); } - public Context set(Context newContext) { - LOG.debug("{}.set({})",this,newContext); - context = newContext; - context.route(this); - return context; - } - public void setLast(Turnout.State state) { if (isNull(state) || state == Turnout.State.UNDEF) return; Tile lastTile = path.lastElement(); @@ -810,20 +785,21 @@ public class Route extends BaseClass { return this; } - public Route.State state(){ - return state; - } - public boolean start(Train newTrain) { - if (state == State.STARTED) return true; +// if (state == State.STARTED) return true; LOG.debug("{}.start()",this); if (isNull(newTrain)) return false; // can't set route's train to null if (isSet(train)) { if (newTrain != train) return false; // can't alter route's train - } else train = newTrain; // set new train + } else train = newTrain.setRoute(this); // set new train + ActionList startActions = triggeredActions.get(ROUTE_START); - if (isSet(startActions) && !startActions.fire(context,this+".start("+train.name()+")")) return false; // start actions failed - state = State.STARTED; + + if (isSet(startActions)) { + Context context = new Context(train).route(this); + if (!startActions.fire(context,this+".start("+train.name()+")")) return false; // start actions failed + } +// state = State.STARTED; triggeredContacts.clear(); return true; } @@ -865,9 +841,8 @@ public class Route extends BaseClass { newTrace.add(tile); remainingLength -= tile.length(); } else if (Route.freeBehindTrain) { - try { - tile.unset(this); - } catch (IllegalArgumentException e) {} + + // TODO } else break; } } diff --git a/src/main/java/de/srsoftware/web4rail/actions/ActionList.java b/src/main/java/de/srsoftware/web4rail/actions/ActionList.java index dc7b17e..27ecfeb 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/ActionList.java +++ b/src/main/java/de/srsoftware/web4rail/actions/ActionList.java @@ -88,7 +88,10 @@ public class ActionList extends Action implements Iterable{ for (Action action : actions) { LOG.debug("firing \"{}\"",action); - if (!action.fire(context,cause)) return false; + if (!action.fire(context,cause)) { + LOG.warn("{} failed",action); + return false; + } } return true; } diff --git a/src/main/java/de/srsoftware/web4rail/conditions/BlockFree.java b/src/main/java/de/srsoftware/web4rail/conditions/BlockFree.java index f37b042..d46794b 100644 --- a/src/main/java/de/srsoftware/web4rail/conditions/BlockFree.java +++ b/src/main/java/de/srsoftware/web4rail/conditions/BlockFree.java @@ -25,7 +25,7 @@ public class BlockFree extends Condition { @Override public boolean fulfilledBy(Context context) { - return block.isFreeFor(null) != inverted; + return block.canNeEnteredBy(null) != inverted; } @Override diff --git a/src/main/java/de/srsoftware/web4rail/moving/Train.java b/src/main/java/de/srsoftware/web4rail/moving/Train.java index d957fd8..ece7fde 100644 --- a/src/main/java/de/srsoftware/web4rail/moving/Train.java +++ b/src/main/java/de/srsoftware/web4rail/moving/Train.java @@ -38,12 +38,23 @@ import de.srsoftware.web4rail.tags.Table; import de.srsoftware.web4rail.tags.Window; import de.srsoftware.web4rail.threads.PathFinder; import de.srsoftware.web4rail.tiles.Block; +import de.srsoftware.web4rail.tiles.Contact; import de.srsoftware.web4rail.tiles.Tile; +import de.srsoftware.web4rail.tiles.Tile.Status; /** * @author Stephan Richter, SRSoftware 2020-2021 * */ public class Train extends BaseClass implements Comparable { + + public interface Listener { + enum Signal { + STOP + } + + public void on(Signal signal); + } + private static final Logger LOG = LoggerFactory.getLogger(Train.class); private static final String CAR_ID = "carId"; @@ -88,6 +99,8 @@ public class Train extends BaseClass implements Comparable { private static final String SHUNTING = "shunting"; private boolean shunting = false; + private HashSet listeners = new HashSet(); + public static Object action(HashMap params, Plan plan) throws IOException { String action = params.get(ACTION); if (isNull(action)) return t("No action passed to Train.action!"); @@ -147,10 +160,18 @@ public class Train extends BaseClass implements Comparable { return t("Unknown action: {}",params.get(ACTION)); } + public Train add(Car car) { + if (isSet(car)) { + cars.add(car); + car.train(this); + } + return this; + } + public void addTag(String tag) { tags.add(tag); } - + private Object addCar(HashMap params) { LOG.debug("addCar({})",params); String carId = params.get(CAR_ID); @@ -161,12 +182,8 @@ public class Train extends BaseClass implements Comparable { return properties(); } - public Train add(Car car) { - if (isSet(car)) { - cars.add(car); - car.train(this); - } - return this; + public void addListener(Listener listener) { + listeners.add(listener); } public boolean automatic() { @@ -299,6 +316,11 @@ public class Train extends BaseClass implements Comparable { return properties(); } + public void contact(Contact contact) { + if (isSet(route)) route.contact(contact); + } + + public void coupleWith(Train parkingTrain,boolean swap) { if (isSet(direction) && isSet(parkingTrain.direction) && parkingTrain.direction != direction) parkingTrain.turn(); if (swap) { @@ -384,7 +406,7 @@ public class Train extends BaseClass implements Comparable { } public void dropTrace() { - while (!trace.isEmpty()) trace.removeFirst().setTrain(null); + while (!trace.isEmpty()) trace.removeFirst().free(); } private Tag faster(int steps) { @@ -485,8 +507,14 @@ public class Train extends BaseClass implements Comparable { if (json.has(DIRECTION)) direction = Direction.valueOf(json.getString(DIRECTION)); if (json.has(NAME)) name = json.getString(NAME); if (json.has(TAGS)) json.getJSONArray(TAGS ).forEach(elem -> { tags.add(elem.toString()); }); - if (json.has(TRACE)) json.getJSONArray(TRACE).forEach(elem -> { trace.add(plan.get(new Id(elem.toString()), false).setTrain(this)); }); - if (json.has(BLOCK)) currentBlock = (Block) plan.get(new Id(json.getString(BLOCK)), false).setTrain(this); // do not move this up! during set, other fields will be referenced! + if (json.has(TRACE)) json.getJSONArray(TRACE).forEach(elem -> { + Tile tile = plan.get(new Id(elem.toString()), false); + if (tile.setState(Status.OCCUPIED,this)) trace.add(tile); + }); + if (json.has(BLOCK)) {// do not move this up! during set, other fields will be referenced! + currentBlock = (Block) plan.get(new Id(json.getString(BLOCK)), false); + currentBlock.setState(Status.OCCUPIED,this); + } if (json.has(LOCOS)) { // for downward compatibility for (Object id : json.getJSONArray(LOCOS)) add(BaseClass.get(new Id(""+id))); } @@ -724,10 +752,10 @@ public class Train extends BaseClass implements Comparable { public void set(Block newBlock) { LOG.debug("{}.set({})",this,newBlock); - if (isSet(currentBlock)) currentBlock.setTrain(null); + if (isSet(currentBlock)) currentBlock.free(); currentBlock = newBlock; if (isSet(currentBlock)) { - currentBlock.setTrain(this); + currentBlock.setState(Status.OCCUPIED,this); lastBlocks.add(newBlock); if (lastBlocks.size()>32) lastBlocks.remove(0); } @@ -776,9 +804,9 @@ public class Train extends BaseClass implements Comparable { return properties(); } - protected Route setRoute(Route newRoute) { + public Train setRoute(Route newRoute) { route = newRoute; - return route; + return this; } public void setSpeed(int newSpeed) { @@ -794,9 +822,9 @@ public class Train extends BaseClass implements Comparable { LOG.debug("old trace: {}",trace); trace.removeAll(newTrace); - for (Tile tile : trace) tile.setTrain(null); + for (Tile tile : trace) tile.free(); trace = newTrace; - for (Tile tile : trace) tile.setTrain(this); + for (Tile tile : trace) tile.setState(Status.OCCUPIED,this); LOG.debug("new trace of {}: {}",this,trace); } @@ -835,27 +863,33 @@ public class Train extends BaseClass implements Comparable { public void start() { - Context context = new Context(this).block(currentBlock).direction(direction); - new PathFinder(context) { + new PathFinder(this,currentBlock,direction) { @Override - public void found(Route r) { + public void aborted() { + LOG.debug("Aborted"); + } + + @Override + public void found(Route newRoute) { // TODO Auto-generated method stub - LOG.debug("Route {} prepared for {}",r,Train.this); + LOG.debug("Found route {} for {}",newRoute,Train.this); } - + @Override - public void locked(Route r) { + public void locked(Route newRoute) { // TODO Auto-generated method stub - LOG.debug("Route {} locked for {}",r,Train.this); + LOG.debug("Locked route {} for {}",newRoute,Train.this); } @Override - public void prepared(Route r) { - LOG.debug("Route {} prepared for {}",r,Train.this); - setRoute(r).start(Train.this); + public void prepared(Route newRoute) { + LOG.debug("Prepared route {} for {}",newRoute,Train.this); + newRoute.start(Train.this); } - }; + + + }.start(); } public static void startAll() { @@ -869,7 +903,8 @@ public class Train extends BaseClass implements Comparable { } public Object stopNow() { - + setSpeed(0); + listeners.forEach(listener -> listener.on(Listener.Signal.STOP)); return properties(); } @@ -911,6 +946,7 @@ public class Train extends BaseClass implements Comparable { */ public Train turn() { LOG.debug("{}.turn()",this); + setSpeed(0); for (Car car : cars) car.turn(); Collections.reverse(cars); return reverse(); diff --git a/src/main/java/de/srsoftware/web4rail/threads/ControlUnit.java b/src/main/java/de/srsoftware/web4rail/threads/ControlUnit.java index c93cb19..97e5054 100644 --- a/src/main/java/de/srsoftware/web4rail/threads/ControlUnit.java +++ b/src/main/java/de/srsoftware/web4rail/threads/ControlUnit.java @@ -321,7 +321,9 @@ public class ControlUnit extends Thread implements Constants{ Thread thread = new Thread() { @Override public void run() { - ControlUnit.this.plan.sensor(addr,active); + set(false); + plan.sensor(addr,active); + set(true); } }; thread.setName(Application.threadName("CU.FeedBack("+addr+")")); diff --git a/src/main/java/de/srsoftware/web4rail/threads/PathFinder.java b/src/main/java/de/srsoftware/web4rail/threads/PathFinder.java index 5c27806..aef3ded 100644 --- a/src/main/java/de/srsoftware/web4rail/threads/PathFinder.java +++ b/src/main/java/de/srsoftware/web4rail/threads/PathFinder.java @@ -18,71 +18,76 @@ import de.srsoftware.web4rail.tiles.Block; /** * @author Stephan Richter, SRSoftware 2020-2021 */ -public abstract class PathFinder extends BaseClass implements Runnable{ +public abstract class PathFinder extends BaseClass implements Runnable, Train.Listener{ public static final Logger LOG = LoggerFactory.getLogger(PathFinder.class); - private Context context; +// private Context context; private boolean aborted = false; + private Direction direction; + private Block startBlock; + private Train train; - public PathFinder(Context context) { - this.context = context; + public PathFinder(Train train, Block start, Direction direction) { + this.train = train; + this.startBlock = start; + this.direction = direction; } public void abort() { aborted = true; + aborted(); + LOG.debug("aborted {}",this); } - private static TreeMap> availableRoutes(Context context,HashSet visitedRoutes){ + private static TreeMap> availableRoutes(Train train, Block start, Direction startDir, HashSet visitedRoutes){ String inset = ""; for (int i=0; i> availableRoutes = new TreeMap>(); + LOG.debug(inset+"PathFinder.availableRoutes({})",visitedRoutes); boolean error = false; - Block block = context.block(); - if (isNull(block)) { - LOG.warn("{} → {}.availableRoutes called without context.block!",inset,Train.class.getSimpleName()); + if (isNull(start)) { + LOG.warn("{} → {}.availableRoutes called without start block!",inset,Train.class.getSimpleName()); error = true; } - Train train = context.train(); if (isNull(train)) { - LOG.warn("{}→ {}.availableRoutes called without context.train!",inset,Train.class.getSimpleName()); + LOG.warn("{}→ {}.availableRoutes called without train!",inset,Train.class.getSimpleName()); error = true; } - if (error) return availableRoutes; + if (error) return new TreeMap>(); - Block destination = train.destination(); - Direction direction = context.direction(); - if (isSet(direction)) { - LOG.debug("{}Looking for {}-bound routes from {}",inset,direction,block); + if (isSet(startDir)) { + LOG.debug("{}Looking for {}-bound routes from {}",inset,startDir,start); } else { - LOG.debug("{}Looking for all routes from {}",inset,block); + LOG.debug("{}Looking for all routes from {}",inset,start); }//*/ + Block destination = train.destination(); if (isSet(destination) && visitedRoutes.isEmpty()) LOG.debug("{}- Destination: {}",inset,destination); - Route currentRoute = context.route(); - - for (Route routeCandidate : block.routes()) { - if (routeCandidate.path().firstElement() != block) continue; // Routen, die nicht vom aktuellen Block starten sind bubu + //Route currentRoute = context.route(); + TreeMap> availableRoutes = new TreeMap>(); + + for (Route routeCandidate : start.routes()) { + if (routeCandidate.path().firstElement() != start) continue; // Routen, die nicht vom aktuellen Block starten sind bubu if (visitedRoutes.contains(routeCandidate)) { LOG.debug("{}→ Candidate {} would create loop, skipping",inset,routeCandidate.shortName()); continue; } - if (!routeCandidate.allowed(context)) { + Context c = new Context(train).block(start).direction(startDir); + if (!routeCandidate.allowed(c)) { if (routeCandidate.endBlock() != destination) { // allowance may be overridden by destination - LOG.debug("{} not allowed for {}",routeCandidate,context); + LOG.debug("{} not allowed for {}",routeCandidate,c); continue; // Zug darf auf Grund einer nicht erfüllten Bedingung nicht auf die Route } - LOG.debug("{} not allowed for {} – overridden by selected destination",routeCandidate,context); + LOG.debug("{} not allowed for {} – overridden by selected destination",routeCandidate,c); } int priority = 0; - if (isSet(direction) && routeCandidate.startDirection != direction) { // Route startet entgegen der aktuellen Fahrtrichtung des Zuges + if (isSet(startDir) && routeCandidate.startDirection != startDir) { // Route startet entgegen der aktuellen Fahrtrichtung des Zuges if (!train.pushPull) continue; // Zug kann nicht wenden - if (!block.turnAllowed) continue; // Wenden im Block nicht gestattet + if (!start.turnAllowed) continue; // Wenden im Block nicht gestattet priority -= 5; } - if (routeCandidate == currentRoute) priority-=10; // möglichst andere Route als zuvor wählen // TODO: den Routen einen "last-used" Zeitstempel hinzufügen, und diesen mit in die Priorisierung einbeziehen + //if (routeCandidate == currentRoute) priority-=10; // möglichst andere Route als zuvor wählen // TODO: den Routen einen "last-used" Zeitstempel hinzufügen, und diesen mit in die Priorisierung einbeziehen if (isSet(destination)) { if (routeCandidate.endBlock() == destination) { // route goes directly to destination @@ -90,9 +95,8 @@ public abstract class PathFinder extends BaseClass implements Runnable{ priority = 1_000_000; } else { LOG.debug("{}- Candidate: {}",inset,routeCandidate.shortName()); - Context forwardContext = new Context(train).block(routeCandidate.endBlock()).route(null).direction(routeCandidate.endDirection); visitedRoutes.add(routeCandidate); - TreeMap> forwardRoutes = availableRoutes(forwardContext,visitedRoutes); + TreeMap> forwardRoutes = availableRoutes(train, routeCandidate.endBlock(), routeCandidate.endDirection, visitedRoutes); visitedRoutes.remove(routeCandidate); if (forwardRoutes.isEmpty()) continue; // the candidate does not lead to a block, from which routes to the destination exist Entry> entry = forwardRoutes.lastEntry(); @@ -110,7 +114,7 @@ public abstract class PathFinder extends BaseClass implements Runnable{ routeSet.add(routeCandidate); if (routeCandidate.endBlock() == destination) break; // direct connection to destination discovered, quit search } - if (!availableRoutes.isEmpty()) LOG.debug("{}→ Routes from {}: {}",inset,block,availableRoutes.isEmpty()?"none":""); + if (!availableRoutes.isEmpty()) LOG.debug("{}→ Routes from {}: {}",inset,start,availableRoutes.isEmpty()?"none":""); for (Entry> entry : availableRoutes.entrySet()) { LOG.debug("{} - Priority {}:",inset,entry.getKey()); for (Route r : entry.getValue()) { @@ -122,19 +126,20 @@ public abstract class PathFinder extends BaseClass implements Runnable{ public Route chooseRoute() { LOG.debug("PathFinder.chooseRoute()"); - TreeMap> availableRoutes = availableRoutes(context,new HashSet()); + HashSet visitedRoutes = new HashSet(); + TreeMap> availableRoutes = availableRoutes(train, startBlock, direction,visitedRoutes); while (!availableRoutes.isEmpty()) { LOG.debug("availableRoutes: {}",availableRoutes); Entry> entry = availableRoutes.lastEntry(); List preferredRoutes = entry.getValue(); LOG.debug("preferredRoutes: {}",preferredRoutes); Route selectedRoute = preferredRoutes.get(random.nextInt(preferredRoutes.size())); - if (selectedRoute.isFreeFor(context.route(selectedRoute))) { + if (selectedRoute.isFreeFor(train)) { LOG.debug("Chose \"{}\" with priority {}.",selectedRoute,entry.getKey()); return selectedRoute; } - LOG.debug("Selected route \"{}\" is not free for {}",selectedRoute,context); + LOG.debug("Selected route \"{}\" is not free for {}",selectedRoute,train); preferredRoutes.remove(selectedRoute); if (preferredRoutes.isEmpty()) availableRoutes.remove(availableRoutes.lastKey()); } @@ -142,17 +147,17 @@ public abstract class PathFinder extends BaseClass implements Runnable{ } @Override - public void run() { + public void run() { while (true) { - Route route = chooseRoute(); if (aborted) return; + Route route = chooseRoute(); if (isSet(route)) { found(route); if (aborted) return; - if (route.lock()) { + if (route.allocateFor(train)) { locked(route); if (aborted) return; - if (route.prepare()) { + if (route.prepareFor(train)) { prepared(route); return; } @@ -162,7 +167,24 @@ public abstract class PathFinder extends BaseClass implements Runnable{ } } + public abstract void aborted(); public abstract void locked(Route r); public abstract void found(Route r); public abstract void prepared(Route r); + + @Override + public void on(Signal signal) { + switch (signal) { + case STOP: + abort(); + break; + } + } + + public void start() { + train.addListener(this); + Thread thread = new Thread(this); + thread.setName("Pathfinder("+train+")"); + thread.start(); + } } diff --git a/src/main/java/de/srsoftware/web4rail/tiles/Block.java b/src/main/java/de/srsoftware/web4rail/tiles/Block.java index 5bf4d4e..166c03e 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/Block.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/Block.java @@ -146,10 +146,17 @@ public abstract class Block extends StretchableTile{ return t("Trigger contact to learn new contact"); } + @Override + public boolean canNeEnteredBy(Train newTrain) { + if (!super.canNeEnteredBy(newTrain)) return false; + if (parkedTrains.isEmpty()) return true; + return isNull(newTrain) ? false : newTrain.isShunting(); // block contains train(s), thus it is only free for shunting train + } + @Override protected HashSet classes() { HashSet classes = super.classes(); - if (!parkedTrains.isEmpty()) classes.add(OCCUPIED); + if (!parkedTrains.isEmpty()) classes.add(Status.OCCUPIED.toString()); return classes; } @@ -229,14 +236,6 @@ public abstract class Block extends StretchableTile{ return 1+internalContacts.indexOf(contact); } - @Override - public boolean isFreeFor(Context context) { - if (!super.isFreeFor(context)) return false; - if (parkedTrains.isEmpty()) return true; - Train t = isSet(context) ? context.train() : null; - return isSet(t) ? t.isShunting() : false; // block contains train(s), thus it is olny free for shunting train - } - @Override public JSONObject json() { JSONObject json = super.json(); @@ -358,7 +357,6 @@ public abstract class Block extends StretchableTile{ super.removeChild(child); internalContacts.remove(child); if (parkedTrains.remove(child)) plan.place(this); - if (train == child) setTrain(null); } public void removeContact(BlockContact blockContact) { diff --git a/src/main/java/de/srsoftware/web4rail/tiles/BlockContact.java b/src/main/java/de/srsoftware/web4rail/tiles/BlockContact.java index fcd8031..8963560 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/BlockContact.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/BlockContact.java @@ -27,11 +27,6 @@ public class BlockContact extends Contact { return new Id(block.name+":"+block.indexOf(this)); } - @Override - public Route route() { - return ((Block)parent()).route(); - } - @Override public Tag tag(Map replacements) throws IOException { return ((Block)parent()).tag(replacements); diff --git a/src/main/java/de/srsoftware/web4rail/tiles/Bridge.java b/src/main/java/de/srsoftware/web4rail/tiles/Bridge.java index b5e3c8b..260f92f 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/Bridge.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/Bridge.java @@ -9,7 +9,6 @@ import org.json.JSONObject; import de.srsoftware.tools.Tag; import de.srsoftware.web4rail.BaseClass; import de.srsoftware.web4rail.Connector; -import de.srsoftware.web4rail.Route; import de.srsoftware.web4rail.moving.Train; import de.srsoftware.web4rail.tags.Fieldset; import de.srsoftware.web4rail.tags.Window; @@ -42,6 +41,12 @@ public abstract class Bridge extends Tile { protected abstract Connector connector(); + @Override + public void free() { + if (isSet(counterpart) && counterpart.train != null) counterpart.free(); + super.free(); + } + @Override public JSONObject json() { JSONObject json = super.json(); @@ -63,16 +68,10 @@ public abstract class Bridge extends Tile { } @Override - public Tile setRoute(Route route) { - super.setRoute(route); - if (isSet(counterpart) && counterpart.route != route) counterpart.setRoute(route); - return this; - } - - public Tile setTrain(Train train) { - super.setTrain(train); - if (isSet(counterpart) && counterpart.train != train) counterpart.setTrain(train); - return this; + public boolean setState(Status newState, Train newTrain) { + if (train == newTrain && is(newState)) return true; + if (!super.setState(newState,newTrain)) return false; + return isNull(counterpart) ? true : counterpart.setState(newState,newTrain); } @Override @@ -109,11 +108,4 @@ public abstract class Bridge extends Tile { if (isNull(counterpart)) tag.clazz(tag.get("class")+" disconnected"); return tag; } - - @Override - public Tile unset(Route oldRoute) { - super.unset(oldRoute); - if (isSet(counterpart) && isSet(counterpart.route)) counterpart.unset(oldRoute); - return this; - } } diff --git a/src/main/java/de/srsoftware/web4rail/tiles/Contact.java b/src/main/java/de/srsoftware/web4rail/tiles/Contact.java index 889302e..2e60e05 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/Contact.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/Contact.java @@ -16,7 +16,6 @@ import org.slf4j.LoggerFactory; import de.srsoftware.tools.Tag; import de.srsoftware.web4rail.Application; import de.srsoftware.web4rail.BaseClass; -import de.srsoftware.web4rail.Route; import de.srsoftware.web4rail.actions.Action; import de.srsoftware.web4rail.actions.ActionList; import de.srsoftware.web4rail.tags.Fieldset; @@ -85,12 +84,12 @@ public class Contact extends Tile{ LOG.debug("{} activated.",this); state = true; if (isSet(timer)) timer.abort(); - Route route = route(); - Context context = isSet(route) ? route.context().contact(this) : new Context(this); - - if (isSet(route)) route.traceTrainFrom(this); + Context context = new Context(this); + if (isSet(train)) { + train.contact(this); + context.train(train); + } actions.fire(context,"Contact("+addr+")"); - if (isSet(route)) route.contact(this); for (Listener listener : listeners) listener.fired("Contact("+addr+")"); diff --git a/src/main/java/de/srsoftware/web4rail/tiles/Tile.java b/src/main/java/de/srsoftware/web4rail/tiles/Tile.java index def0924..9df37f4 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/Tile.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/Tile.java @@ -40,12 +40,27 @@ import de.srsoftware.web4rail.threads.PathFinder; * */ public abstract class Tile extends BaseClass implements Comparable{ + public enum Status{ + FREE("free"), + ALLOCATED("allocated"), + LOCKED("locked"), + OCCUPIED("occupied"); + + private String tx; + + Status(String s) { + tx = s; + } + + @Override + public String toString() { + return tx; + } + } protected static Logger LOG = LoggerFactory.getLogger(Tile.class); private static int DEFAUT_LENGTH = 100; // 10cm private static final String LENGTH = "length"; - private static final String LOCKED = "locked"; - protected static final String OCCUPIED = "occupied"; private static final String ONEW_WAY = "one_way"; private static final String POS = "pos"; private static final String TYPE = "type"; @@ -56,8 +71,8 @@ public abstract class Tile extends BaseClass implements Comparable{ private boolean isTrack = true; private int length = DEFAUT_LENGTH; protected Direction oneWay = null; - protected Route route = null; private TreeSet routes = new TreeSet<>((r1,r2)->r1.toString().compareTo(r2.toString())); + private Status status = Status.FREE; protected Train train = null; public Integer x = null; public Integer y = null; @@ -65,13 +80,38 @@ public abstract class Tile extends BaseClass implements Comparable{ public void add(Route route) { this.routes.add(route); } + + public boolean canNeEnteredBy(Train newTrain) { + PathFinder.LOG.debug("{}.canNeEnteredBy({})",this,newTrain); + if (disabled) { + PathFinder.LOG.debug("{} is disabled!",this); + return false; + } + + if (isNull(train)) { + PathFinder.LOG.debug("→ free"); + return true; + } + + if (newTrain == train) { // during train.reserveNext, we may encounter, parts, that are already reserved by the respective train, but having another route. do not compare routes in that case! + PathFinder.LOG.debug("already reserved by {} → true",train); + return true; + } + + if (isSet(newTrain) && newTrain.isShunting()) { + PathFinder.LOG.debug("occupied by {}. Allowed for shunting {}",train,newTrain); + return true; + } + + PathFinder.LOG.debug("occupied by {} → false",train); + return false; + } protected HashSet classes(){ HashSet classes = new HashSet(); classes.add("tile"); - classes.add(getClass().getSimpleName()); - if (isSet(route)) classes.add(LOCKED); - if (isSet(train)) classes.add(OCCUPIED); + classes.add(getClass().getSimpleName()); + if (!is(Status.FREE)) classes.add(status.toString()); if (disabled) classes.add(DISABLED); return classes; } @@ -96,6 +136,11 @@ public abstract class Tile extends BaseClass implements Comparable{ return new HashMap<>(); } + public void free() { + train = null; + status = Status.FREE; + } + public int height() { return 1; } @@ -115,58 +160,18 @@ public abstract class Tile extends BaseClass implements Comparable{ if (tile instanceof TileWithShadow) ((TileWithShadow)tile).placeShadows(); plan.place(tile); } - - public boolean isFreeFor(Context context) { - PathFinder.LOG.debug("{}.isFreeFor({})",this,context); - if (disabled) { - PathFinder.LOG.debug("{} is disabled!",this); - return false; + + public boolean is(Status...states) { + for (Status s: states) { + if (status == s) return true; } - if (isNull(context)) { - if (isSet(train)) { - PathFinder.LOG.debug("{} is occupied by {}",this,train); - return false; - } - if (isSet(route)) { - PathFinder.LOG.debug("{} is occupied by {}",this,route); - return false; - } - } - if (isSet(train)) { - Train contextTrain = context.train(); - boolean free = train == contextTrain; // during train.reserveNext, we may encounter, parts, that are already reserved by the respective train, but having another route. do not compare routes in that case! - if (free) { - PathFinder.LOG.debug("already reserved by {} → true",train); - } else { - if (isSet(contextTrain) && contextTrain.isShunting()) { - PathFinder.LOG.debug("occupied by {}. Allowed for shunting {}",train,contextTrain); - free = true; - } else PathFinder.LOG.debug("occupied by {} → false",train); - } - return free; - } - - // if we get here, the tile is not occupied by a train, but reserved by a route, yet. thus, the tile is not available for another route - if (isSet(route) && route != context.route()) { - PathFinder.LOG.debug("reserved by other route: {}",route); - if (isSet(route.train())) { - if (route.train() == context.train()) { - PathFinder.LOG.debug("that route is used by {}, which is also requesting this tile → true",route.train()); - return true; - } - } - PathFinder.LOG.debug("{}.route.train = {} → false",this,route.train()); - return false; - } - PathFinder.LOG.debug("free"); - return true; + return false; } - + public JSONObject json() { JSONObject json = super.json(); json.put(TYPE, getClass().getSimpleName()); if (isSet(x) && isSet(y)) json.put(POS, new JSONObject(Map.of(X,x,Y,y))); - if (isSet(route)) json.put(ROUTE, route.id()); if (isSet(oneWay)) json.put(ONEW_WAY, oneWay); if (disabled) json.put(DISABLED, true); if (isSet(train)) json.put(REALM_TRAIN, train.id()); @@ -242,7 +247,7 @@ public abstract class Tile extends BaseClass implements Comparable{ protected void noTrack() { isTrack = false; } - + public Tile position(int x, int y) { this.x = x; this.y = y; @@ -256,16 +261,9 @@ public abstract class Tile extends BaseClass implements Comparable{ @Override protected Window properties(List
preForm, FormInput formInputs, List
postForm) { Fieldset fieldset = null; - - if (isSet(route)) { - fieldset = new Fieldset(t("Route")); - route.link("p",t("Locked by {}",route)).addTo(fieldset); - } if (isSet(train)) { - if (isSet(fieldset)) { - fieldset.children().firstElement().content(" / "+t("Train")); - } else fieldset = new Fieldset(t("Train")); + fieldset = new Fieldset(t("Train")); train.link("span", t("Train")+":"+NBSP+train+NBSP).addTo(fieldset); if (isSet(train.route())) { train.button(t("stop"), Map.of(ACTION,ACTION_STOP)).addTo(fieldset); @@ -352,10 +350,6 @@ public abstract class Tile extends BaseClass implements Comparable{ } return line; } - - public Route route() { - return route; - } public TreeSet routes() { return routes; @@ -370,24 +364,16 @@ public abstract class Tile extends BaseClass implements Comparable{ file.close(); } - public Tile setTrain(Train newTrain) { - LOG.debug("{}.setTrain({})",this,newTrain); - if (newTrain == train) return this; // nothing to update - this.train = newTrain; - return plan.place(this); - } - - public Tile setRoute(Route lockingRoute) { - LOG.debug("{}.setRoute({})",this,lockingRoute); - if (isNull(lockingRoute)) throw new NullPointerException(); - if (isSet(route)) { - if (route == lockingRoute) return this; // nothing changed - throw new IllegalStateException(this.toString()); // tile already locked by other route - } - route = lockingRoute; - return plan.place(this); + public boolean setState(Status newState,Train newTrain) { + if (isNull(newTrain)) return false; + if (isSet(train) && newTrain != train) return false; // already locked by other train + if (is(Status.OCCUPIED,newState)) return true; // do not downgrade occupied tiles, accept current state + train = newTrain; + status = newState; + plan.place(this); + return true; } - + public Tag tag(Map replacements) throws IOException { int width = 100*width(); int height = 100*height(); @@ -477,7 +463,6 @@ public abstract class Tile extends BaseClass implements Comparable{ if (child instanceof Route) routes.remove(child); if (child == train) train = null; - if (child == route) route = null; super.removeChild(child); plan.place(this); } @@ -487,22 +472,6 @@ public abstract class Tile extends BaseClass implements Comparable{ plan.place(this); } - public void unlock() { - route = null; - train = null; - plan.place(this); - } - - public Tile unset(Route oldRoute) { - LOG.debug("{}.unset({})",this,oldRoute); - if (route == null) return this; - if (route == oldRoute) { - route = null; - return plan.place(this); - } - throw new IllegalArgumentException(t("{} not occupied by {}!",this,oldRoute)); - } - public Tile update(HashMap params) { LOG.debug("{}.update({})",getClass().getSimpleName(),params); String oneWayDir = params.get("oneway"); diff --git a/src/main/java/de/srsoftware/web4rail/tiles/Turnout.java b/src/main/java/de/srsoftware/web4rail/tiles/Turnout.java index ee4945c..0b841e6 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/Turnout.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/Turnout.java @@ -159,7 +159,7 @@ public abstract class Turnout extends Tile implements Device{ } public Reply state(State newState) { - if (train != null && newState != state) return new Reply(415, t("{} locked by {}!",this,train)); + if (is(Status.LOCKED,Status.OCCUPIED) && newState != state) return new Reply(415, t("{} locked by {}!",this,train)); if (address == 0) { state = newState; plan.place(this); diff --git a/src/main/java/de/srsoftware/web4rail/tiles/TurnoutL.java b/src/main/java/de/srsoftware/web4rail/tiles/TurnoutL.java index 19287dd..e82fa4b 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/TurnoutL.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/TurnoutL.java @@ -16,8 +16,8 @@ public abstract class TurnoutL extends Turnout { public Object click(boolean shift) throws IOException { Object o = super.click(shift); if (!shift) { - if (route != null) { - plan.stream(t("{} is locked by {}!",this,route)); + if (isSet(train)) { + plan.stream(t("{} is locked by {}!",this,train)); } else state(state == State.STRAIGHT ? State.LEFT : State.STRAIGHT); } return o; diff --git a/src/main/java/de/srsoftware/web4rail/tiles/TurnoutR.java b/src/main/java/de/srsoftware/web4rail/tiles/TurnoutR.java index 782c4b3..1c5dfde 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/TurnoutR.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/TurnoutR.java @@ -16,8 +16,8 @@ public abstract class TurnoutR extends Turnout { public Object click(boolean shift) throws IOException { Object o = super.click(shift); if (!shift) { - if (route != null) { - plan.stream(t("{} is locked by {}!",this,route)); + if (isSet(train)) { + plan.stream(t("{} is locked by {}!",this,train)); } else state(state == State.STRAIGHT ? State.RIGHT : State.STRAIGHT); } return o; From c6aa5e45da9a0518e569088b8692c2dab4fbf5d3 Mon Sep 17 00:00:00 2001 From: Stephan Richter Date: Thu, 11 Mar 2021 11:20:48 +0100 Subject: [PATCH 05/26] working on new autopilot code --- pom.xml | 2 +- .../java/de/srsoftware/web4rail/Plan.java | 11 ++- .../java/de/srsoftware/web4rail/Route.java | 21 ++-- .../web4rail/actions/BrakeStart.java | 4 +- .../de/srsoftware/web4rail/moving/Train.java | 23 +++++ .../web4rail/threads/BrakeProcessor.java | 99 +++++++++++++++++++ .../web4rail/threads/ControlUnit.java | 2 - .../de/srsoftware/web4rail/tiles/Tile.java | 1 + 8 files changed, 144 insertions(+), 19 deletions(-) create mode 100644 src/main/java/de/srsoftware/web4rail/threads/BrakeProcessor.java diff --git a/pom.xml b/pom.xml index 019ecca..dec68d8 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 de.srsoftware web4rail - 1.3.53 + 1.3.54 Web4Rail jar Java Model Railway Control diff --git a/src/main/java/de/srsoftware/web4rail/Plan.java b/src/main/java/de/srsoftware/web4rail/Plan.java index f6a3fdf..dc43bef 100644 --- a/src/main/java/de/srsoftware/web4rail/Plan.java +++ b/src/main/java/de/srsoftware/web4rail/Plan.java @@ -36,6 +36,7 @@ import de.srsoftware.web4rail.tags.Input; import de.srsoftware.web4rail.tags.Label; import de.srsoftware.web4rail.tags.Table; import de.srsoftware.web4rail.tags.Window; +import de.srsoftware.web4rail.threads.BrakeProcessor; import de.srsoftware.web4rail.threads.ControlUnit; import de.srsoftware.web4rail.tiles.Block; import de.srsoftware.web4rail.tiles.BlockContact; @@ -152,7 +153,7 @@ public class Plan extends BaseClass{ private static final String SPEED_UNIT = "speed_unit"; private static final String LENGTH_UNIT = "length_unit"; private static final String CONFIRM = "confirm"; -// private static final String FINAL_SPEED = "final_speed"; // TODO + private static final String FINAL_SPEED = "final_speed"; private static final String FREE_BEHIND_TRAIN = "free_behind_train"; private static final String RENAME = "rename"; private String name = DEFAULT_NAME; @@ -356,7 +357,7 @@ public class Plan extends BaseClass{ new Input(ACTION,ACTION_UPDATE).hideIn(form); new Input(LENGTH_UNIT, lengthUnit).addTo(new Label(t("Length unit")+COL)).addTo(form); new Input(SPEED_UNIT, speedUnit).addTo(new Label(t("Speed unit")+COL)).addTo(form); - //new Input(FINAL_SPEED, BrakeProcessor.defaultEndSpeed).addTo(new Label(t("Lower speed limit")+COL)).attr("title", t("Final speed after breaking, before halting")).addTo(form); // TODO + new Input(FINAL_SPEED, BrakeProcessor.defaultEndSpeed).addTo(new Label(t("Lower speed limit")+COL)).attr("title", t("Final speed after breaking, before halting")).addTo(form); // TODO new Checkbox(FREE_BEHIND_TRAIN, t("Free tiles behind train"), Route.freeBehindTrain).attr("title", t("If checked, tiles behind the train are freed according to the length of the train and the tiles. If it is unchecked, tiles will not get free before route is finished.")).addTo(form); new Button(t("Save"), form).addTo(form); form.addTo(fieldset); @@ -490,7 +491,7 @@ public class Plan extends BaseClass{ .forEach(jTiles::put); return new JSONObject() -// .put(FINAL_SPEED, BrakeProcessor.defaultEndSpeed) // TODO + .put(FINAL_SPEED, BrakeProcessor.defaultEndSpeed) .put(FREE_BEHIND_TRAIN, Route.freeBehindTrain) .put(LENGTH_UNIT, lengthUnit) .put(SPEED_UNIT, speedUnit) @@ -524,7 +525,7 @@ public class Plan extends BaseClass{ if (json.has(TILE)) json.getJSONArray(TILE).forEach(object -> Tile.load(object, plan)); if (json.has(LENGTH_UNIT)) lengthUnit = json.getString(LENGTH_UNIT); if (json.has(SPEED_UNIT)) speedUnit = json.getString(SPEED_UNIT); -// if (json.has(FINAL_SPEED)) BrakeProcessor.defaultEndSpeed = json.getInt(FINAL_SPEED); // TODOO + if (json.has(FINAL_SPEED)) BrakeProcessor.defaultEndSpeed = json.getInt(FINAL_SPEED); if (json.has(FREE_BEHIND_TRAIN)) Route.freeBehindTrain = json.getBoolean(FREE_BEHIND_TRAIN); try { @@ -983,7 +984,7 @@ public class Plan extends BaseClass{ if (params.containsKey(LENGTH_UNIT)) lengthUnit = params.get(LENGTH_UNIT); if (params.containsKey(SPEED_UNIT)) speedUnit = params.get(SPEED_UNIT); -// if (params.containsKey(FINAL_SPEED)) BrakeProcessor.defaultEndSpeed = Integer.parseInt(params.get(FINAL_SPEED)); // TODO + if (params.containsKey(FINAL_SPEED)) BrakeProcessor.defaultEndSpeed = Integer.parseInt(params.get(FINAL_SPEED)); Route.freeBehindTrain = "on".equalsIgnoreCase(params.get(FREE_BEHIND_TRAIN)); return t("Plan updated."); diff --git a/src/main/java/de/srsoftware/web4rail/Route.java b/src/main/java/de/srsoftware/web4rail/Route.java index 18a62f1..b6341a5 100644 --- a/src/main/java/de/srsoftware/web4rail/Route.java +++ b/src/main/java/de/srsoftware/web4rail/Route.java @@ -266,11 +266,6 @@ public class Route extends BaseClass { return this; } - public void brakeStart() { - if (isNull(train)) return; -// brakeProcessor = new BrakeProcessor(this,train); - } - protected Route clone() { Route clone = new Route(); clone.startBlock = startBlock; @@ -331,7 +326,7 @@ public class Route extends BaseClass { ActionList actions = triggeredActions.get(contact.trigger()); LOG.debug("Contact has id {} / trigger {} and is assigned with {}",contact.id(),contact.trigger(),isNull(actions)?t("nothing"):actions); if (isNull(actions)) return; - Context context = new Context(this).train(train); + Context context = new Context(this).train(train).contact(contact); actions.fire(context,"Route.Contact("+contact.addr()+")"); } @@ -382,11 +377,19 @@ public class Route extends BaseClass { public void finish() { LOG.debug("{}.finish()",this); - - // TODO: - + train.endRoute(); + train = null; + free(); } + private void free() { + for (Tile tile : path) { + if (train.onTrace(tile)) { + tile.setState(Status.OCCUPIED, train); + } else tile.free(); + } + } + private String generateName() { StringBuilder sb = new StringBuilder(); for (int i=0; i { private HashSet listeners = new HashSet(); + private BrakeProcessor brakeProcessor; + public static Object action(HashMap params, Plan plan) throws IOException { String action = params.get(ACTION); if (isNull(action)) return t("No action passed to Train.action!"); @@ -409,6 +412,14 @@ public class Train extends BaseClass implements Comparable { while (!trace.isEmpty()) trace.removeFirst().free(); } + public void endRoute() { + setSpeed(0); + if (isSet(brakeProcessor)) brakeProcessor.end(); + brakeProcessor = null; + route = null; + } + + private Tag faster(int steps) { setSpeed(speed+steps); return properties(); @@ -901,6 +912,18 @@ public class Train extends BaseClass implements Comparable { // TODO Auto-generated method stub return null; } + + public void startBrake() { + if (isNull(route)) { + LOG.warn("{}.startBrake() called, but train ist not on a route!",this); + return; + } + if (isSet(brakeProcessor)) { + LOG.debug("{} already is braking."); + return; + } + brakeProcessor = new BrakeProcessor(this).start(); + } public Object stopNow() { setSpeed(0); diff --git a/src/main/java/de/srsoftware/web4rail/threads/BrakeProcessor.java b/src/main/java/de/srsoftware/web4rail/threads/BrakeProcessor.java new file mode 100644 index 0000000..80d2e5b --- /dev/null +++ b/src/main/java/de/srsoftware/web4rail/threads/BrakeProcessor.java @@ -0,0 +1,99 @@ +package de.srsoftware.web4rail.threads; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import de.srsoftware.web4rail.BaseClass; +import de.srsoftware.web4rail.Route; +import de.srsoftware.web4rail.moving.Train; + +/** + * @author Stephan Richter, SRSoftware + * + */ +public class BrakeProcessor extends BaseClass implements Runnable { + + private enum State { + IDLE, BRAKING, ABORTED, ENDED; + } + + private static final Logger LOG = LoggerFactory.getLogger(BrakeProcessor.class); + public static int defaultEndSpeed; + private Train train; + private State state = State.IDLE; + private long measuredDistance; + private long lastTime; + private Integer brakeTime; + private int startSpeed; + + public BrakeProcessor(Train train) { + this.train = train; + } + + public void end() { + state = State.ENDED; + measuredDistance += train.speed * (BaseClass.timestamp() - lastTime); + LOG.debug("old brake time: {}, measured distance: {}",brakeTime,measuredDistance); + int step = brakeTime; + for (int i=0; i<10; i++) { + long calculatedDistance = calculate(brakeTime,startSpeed); + step/=2; + if (step<1) step = 1; + LOG.debug("new brake time: {}, calculated distance: {}",brakeTime,calculatedDistance); + if (measuredDistance < calculatedDistance) { + brakeTime -= step; + } + if (measuredDistance > calculatedDistance) { + brakeTime += step; + } + } + LOG.debug("new brake time: {}, calculated distance: {}",brakeTime,""); + } + + private static long calculate(int brakeTime, int speed) { + long dist = 0; + while (speed > defaultEndSpeed) { + dist += speed*brakeTime; + speed -= 10; + } + return dist; + } + + @Override + public void run() { + LOG.debug("run()"); + Route route = train.route(); + if (isNull(route)) return; + brakeTime = route.brakeTime(train.brakeId()); + if (isNull(brakeTime)) brakeTime = 250; + + state = State.BRAKING; + measuredDistance = 0; + lastTime = BaseClass.timestamp(); + startSpeed = train.speed; + int targetSpeed = defaultEndSpeed; + while (state == State.BRAKING) { + sleep(brakeTime); + long newTime = BaseClass.timestamp(); + if (isNull(train.route())) state = State.ABORTED; + if (state != State.BRAKING) break; + measuredDistance += train.speed * (newTime - lastTime); + int newSpeed = train.speed - 10; + if (newSpeed < targetSpeed) { + train.setSpeed(targetSpeed); + break; + } + train.setSpeed(newSpeed); + lastTime = newTime; + } + LOG.debug("{} reached final speed.", train); + } + + public BrakeProcessor start() { + Thread thread = new Thread(this); + thread.setName(getClass().getSimpleName()); + thread.start(); + return this; + } + +} diff --git a/src/main/java/de/srsoftware/web4rail/threads/ControlUnit.java b/src/main/java/de/srsoftware/web4rail/threads/ControlUnit.java index 97e5054..e2dc9fc 100644 --- a/src/main/java/de/srsoftware/web4rail/threads/ControlUnit.java +++ b/src/main/java/de/srsoftware/web4rail/threads/ControlUnit.java @@ -321,9 +321,7 @@ public class ControlUnit extends Thread implements Constants{ Thread thread = new Thread() { @Override public void run() { - set(false); plan.sensor(addr,active); - set(true); } }; thread.setName(Application.threadName("CU.FeedBack("+addr+")")); diff --git a/src/main/java/de/srsoftware/web4rail/tiles/Tile.java b/src/main/java/de/srsoftware/web4rail/tiles/Tile.java index 9df37f4..5887355 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/Tile.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/Tile.java @@ -139,6 +139,7 @@ public abstract class Tile extends BaseClass implements Comparable{ public void free() { train = null; status = Status.FREE; + plan.place(this); } public int height() { From a60e766dac386d5eeaf15f4916d6f0c29fc27daa Mon Sep 17 00:00:00 2001 From: Stephan Richter Date: Fri, 12 Mar 2021 12:46:18 +0100 Subject: [PATCH 06/26] working on new autopilot --- pom.xml | 2 +- .../java/de/srsoftware/web4rail/Route.java | 35 +++---------------- .../de/srsoftware/web4rail/moving/Train.java | 35 ++++++++++++++++++- .../web4rail/threads/BrakeProcessor.java | 26 +++++++------- 4 files changed, 51 insertions(+), 47 deletions(-) diff --git a/pom.xml b/pom.xml index dec68d8..242c403 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 de.srsoftware web4rail - 1.3.54 + 1.3.55 Web4Rail jar Java Model Railway Control diff --git a/src/main/java/de/srsoftware/web4rail/Route.java b/src/main/java/de/srsoftware/web4rail/Route.java index b6341a5..63a5c80 100644 --- a/src/main/java/de/srsoftware/web4rail/Route.java +++ b/src/main/java/de/srsoftware/web4rail/Route.java @@ -6,7 +6,6 @@ import java.io.FileWriter; import java.io.IOException; import java.util.HashMap; import java.util.HashSet; -import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -237,6 +236,7 @@ public class Route extends BaseClass { } public void brakeTime(String brakeId, Integer newTimeStep) { + LOG.debug("new brake time for route {}: {}",this,newTimeStep); brakeTimes.put(brakeId,newTimeStep); } @@ -378,8 +378,10 @@ public class Route extends BaseClass { public void finish() { LOG.debug("{}.finish()",this); train.endRoute(); + free(); + train.set(endBlock); + train.heading(endDirection); train = null; - free(); } private void free() { @@ -822,36 +824,7 @@ public class Route extends BaseClass { public String toString() { return getClass().getSimpleName()+"("+(isSet(train)?train+":":"")+name()+")"; } - - public void traceTrainFrom(Tile newHead) { - LOG.debug("{}.traceTrainFrom({})",this,newHead); - if (isNull(train)) return; - if (newHead instanceof BlockContact) newHead = (Tile) ((BlockContact)newHead).parent(); - Tile traceHead = train.traceHead(); - Integer remainingLength = null; - LinkedList newTrace = new LinkedList(); - for (int i=path.size(); i>0; i--) { // pfad rückwärts ablaufen - Tile tile = path.elementAt(i-1); - if (isNull(remainingLength)) { - if (tile == newHead) traceHead = newHead; // wenn wir zuerst newHead finden: newHead als neuen traceHead übernehmen - if (tile == traceHead) { - remainingLength = train.length(); // sobald wir auf den traceHead stoßen: suche beenden - } - } - if (isSet(remainingLength)) { - if (remainingLength>=0) { - newTrace.add(tile); - remainingLength -= tile.length(); - } else if (Route.freeBehindTrain) { - - // TODO - } else break; - } - } - train.setTrace(newTrace); - } - public Train train() { return train; } diff --git a/src/main/java/de/srsoftware/web4rail/moving/Train.java b/src/main/java/de/srsoftware/web4rail/moving/Train.java index 2f4ccc1..a3d90d5 100644 --- a/src/main/java/de/srsoftware/web4rail/moving/Train.java +++ b/src/main/java/de/srsoftware/web4rail/moving/Train.java @@ -39,6 +39,7 @@ import de.srsoftware.web4rail.tags.Window; import de.srsoftware.web4rail.threads.BrakeProcessor; import de.srsoftware.web4rail.threads.PathFinder; import de.srsoftware.web4rail.tiles.Block; +import de.srsoftware.web4rail.tiles.BlockContact; import de.srsoftware.web4rail.tiles.Contact; import de.srsoftware.web4rail.tiles.Tile; import de.srsoftware.web4rail.tiles.Tile.Status; @@ -320,7 +321,11 @@ public class Train extends BaseClass implements Comparable { } public void contact(Contact contact) { - if (isSet(route)) route.contact(contact); + if (isSet(route)) { + Route lastRoute = route; // route field might be set to null during route.contact(...)! + route.contact(contact); + traceFrom(contact,lastRoute); + } } @@ -957,6 +962,34 @@ public class Train extends BaseClass implements Comparable { return name(); } + public void traceFrom(Tile newHead,Route route) { + LOG.debug("{}.traceTrainFrom({})",this,newHead); + if (isNull(route)) return; + if (newHead instanceof BlockContact) newHead = (Tile) ((BlockContact)newHead).parent(); + + Tile traceHead = traceHead(); + Integer remainingLength = null; + LinkedList newTrace = new LinkedList(); + Vector path = route.path(); + for (int i=path.size(); i>0; i--) { // pfad rückwärts ablaufen + Tile tile = path.elementAt(i-1); + if (isNull(remainingLength)) { + if (tile == newHead) traceHead = newHead; // wenn wir zuerst newHead finden: newHead als neuen traceHead übernehmen + if (tile == traceHead) remainingLength = length(); // sobald wir auf den traceHead stoßen: Suche beenden + } + if (isSet(remainingLength)) { + if (remainingLength>=0) { + newTrace.add(tile); + remainingLength -= tile.length(); + } else if (Route.freeBehindTrain) { + + // TODO + } else break; + } + } + setTrace(newTrace); + } + public Tile traceHead() { return trace == null || trace.isEmpty() ? null : trace.getFirst(); } diff --git a/src/main/java/de/srsoftware/web4rail/threads/BrakeProcessor.java b/src/main/java/de/srsoftware/web4rail/threads/BrakeProcessor.java index 80d2e5b..b49c0e9 100644 --- a/src/main/java/de/srsoftware/web4rail/threads/BrakeProcessor.java +++ b/src/main/java/de/srsoftware/web4rail/threads/BrakeProcessor.java @@ -33,27 +33,25 @@ public class BrakeProcessor extends BaseClass implements Runnable { public void end() { state = State.ENDED; measuredDistance += train.speed * (BaseClass.timestamp() - lastTime); - LOG.debug("old brake time: {}, measured distance: {}",brakeTime,measuredDistance); + Route route = train.route(); + if (isNull(route)) return; + LOG.debug("old brake time: {}, measured distance: {}", brakeTime, measuredDistance); int step = brakeTime; - for (int i=0; i<10; i++) { - long calculatedDistance = calculate(brakeTime,startSpeed); - step/=2; - if (step<1) step = 1; - LOG.debug("new brake time: {}, calculated distance: {}",brakeTime,calculatedDistance); - if (measuredDistance < calculatedDistance) { - brakeTime -= step; - } - if (measuredDistance > calculatedDistance) { - brakeTime += step; - } + for (int i = 0; i < 15; i++) { + long calculatedDistance = calculate(brakeTime, startSpeed); + step /= 2; + if (step < 1) step = 1; + if (measuredDistance > calculatedDistance) brakeTime += step; + if (measuredDistance < calculatedDistance) brakeTime -= step; + LOG.debug("new brake time: {}, calculated distance: {}", brakeTime, calculatedDistance); } - LOG.debug("new brake time: {}, calculated distance: {}",brakeTime,""); + route.brakeTime(train.brakeId(), brakeTime); } private static long calculate(int brakeTime, int speed) { long dist = 0; while (speed > defaultEndSpeed) { - dist += speed*brakeTime; + dist += speed * brakeTime; speed -= 10; } return dist; From 2556ea7cd9fa0661e85be4dd9921398adec0a6f7 Mon Sep 17 00:00:00 2001 From: Stephan Richter Date: Fri, 12 Mar 2021 14:07:05 +0100 Subject: [PATCH 07/26] implemented correct behaviour of Train.stopNow --- pom.xml | 2 +- src/main/java/de/srsoftware/web4rail/Route.java | 6 +++--- src/main/java/de/srsoftware/web4rail/moving/Train.java | 6 +++++- src/main/java/de/srsoftware/web4rail/tiles/Contact.java | 2 +- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 242c403..685ad2a 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 de.srsoftware web4rail - 1.3.55 + 1.3.56 Web4Rail jar Java Model Railway Control diff --git a/src/main/java/de/srsoftware/web4rail/Route.java b/src/main/java/de/srsoftware/web4rail/Route.java index 63a5c80..33c8843 100644 --- a/src/main/java/de/srsoftware/web4rail/Route.java +++ b/src/main/java/de/srsoftware/web4rail/Route.java @@ -386,7 +386,7 @@ public class Route extends BaseClass { private void free() { for (Tile tile : path) { - if (train.onTrace(tile)) { + if (isSet(train) && train.onTrace(tile)) { tile.setState(Status.OCCUPIED, train); } else tile.free(); } @@ -746,8 +746,8 @@ public class Route extends BaseClass { public boolean reset() { LOG.debug("{}.reset()",this); - - // TODO + free(); + train = null; return true; } diff --git a/src/main/java/de/srsoftware/web4rail/moving/Train.java b/src/main/java/de/srsoftware/web4rail/moving/Train.java index a3d90d5..09ec77a 100644 --- a/src/main/java/de/srsoftware/web4rail/moving/Train.java +++ b/src/main/java/de/srsoftware/web4rail/moving/Train.java @@ -932,7 +932,11 @@ public class Train extends BaseClass implements Comparable { public Object stopNow() { setSpeed(0); - listeners.forEach(listener -> listener.on(Listener.Signal.STOP)); + listeners.forEach(listener -> listener.on(Listener.Signal.STOP)); // abort PathFinder + if (isSet(route)) { + route.reset(); + route = null; + } return properties(); } diff --git a/src/main/java/de/srsoftware/web4rail/tiles/Contact.java b/src/main/java/de/srsoftware/web4rail/tiles/Contact.java index 2e60e05..db91815 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/Contact.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/Contact.java @@ -85,7 +85,7 @@ public class Contact extends Tile{ state = true; if (isSet(timer)) timer.abort(); Context context = new Context(this); - if (isSet(train)) { + if (isSet(train())) { train.contact(this); context.train(train); } From 6bf7882f3b19154b5cc4a7210bdc80ab81919e27 Mon Sep 17 00:00:00 2001 From: Stephan Richter Date: Fri, 12 Mar 2021 16:04:08 +0100 Subject: [PATCH 08/26] improved error messages --- pom.xml | 2 +- resources/css/style.css | 9 ++- resources/js/plan.js | 2 +- .../translations/Application.de.translation | 1 + .../de/srsoftware/web4rail/BaseClass.java | 14 ++++- .../java/de/srsoftware/web4rail/Route.java | 41 +++++++------ .../srsoftware/web4rail/actions/Action.java | 4 +- .../web4rail/actions/ActionList.java | 4 +- .../actions/AddRemoveDestination.java | 4 +- .../web4rail/actions/AddRemoveTag.java | 4 +- .../web4rail/actions/AlterDirection.java | 4 +- .../web4rail/actions/ConditionalAction.java | 4 +- .../web4rail/actions/CoupleTrain.java | 4 +- .../web4rail/actions/DelayedAction.java | 4 +- .../actions/DetermineTrainInBlock.java | 4 +- .../web4rail/actions/DisableEnableBlock.java | 4 +- .../web4rail/actions/EngageDecoupler.java | 4 +- .../web4rail/actions/FinishRoute.java | 5 +- .../de/srsoftware/web4rail/actions/Loop.java | 4 +- .../web4rail/actions/SendCommand.java | 4 +- .../web4rail/actions/SetContextTrain.java | 4 +- .../web4rail/actions/SetDisplayText.java | 4 +- .../srsoftware/web4rail/actions/SetPower.java | 4 +- .../web4rail/actions/SetRelayOrSwitch.java | 4 +- .../web4rail/actions/SetSignal.java | 4 +- .../srsoftware/web4rail/actions/SetSpeed.java | 4 +- .../web4rail/actions/SetTurnout.java | 4 +- .../web4rail/actions/SplitTrain.java | 4 +- .../web4rail/actions/StartStopAuto.java | 4 +- .../web4rail/actions/SwitchFunction.java | 4 +- .../web4rail/actions/TextAction.java | 4 +- .../web4rail/actions/TriggerContact.java | 4 +- .../web4rail/actions/WaitForContact.java | 4 +- .../web4rail/conditions/BlockFree.java | 4 +- .../web4rail/conditions/CarInTrain.java | 4 +- .../web4rail/conditions/CarOrientation.java | 4 +- .../web4rail/conditions/Condition.java | 4 +- .../web4rail/conditions/ConditionList.java | 6 +- .../web4rail/conditions/RouteEndBlock.java | 4 +- .../web4rail/conditions/SwitchIsOn.java | 4 +- .../web4rail/conditions/TrainHasTag.java | 4 +- .../web4rail/conditions/TrainLength.java | 4 +- .../web4rail/conditions/TrainSelect.java | 4 +- .../web4rail/conditions/TrainSpeed.java | 4 +- .../web4rail/conditions/TrainWasInBlock.java | 4 +- .../de/srsoftware/web4rail/moving/Car.java | 4 +- .../web4rail/moving/Locomotive.java | 6 +- .../de/srsoftware/web4rail/moving/Train.java | 61 +++++++++---------- .../web4rail/threads/BrakeProcessor.java | 10 +-- .../web4rail/threads/PathFinder.java | 28 ++++----- .../de/srsoftware/web4rail/tiles/Block.java | 4 +- .../de/srsoftware/web4rail/tiles/Bridge.java | 4 +- .../de/srsoftware/web4rail/tiles/Contact.java | 4 +- .../srsoftware/web4rail/tiles/Decoupler.java | 4 +- .../de/srsoftware/web4rail/tiles/Relay.java | 4 +- .../de/srsoftware/web4rail/tiles/Signal.java | 4 +- .../web4rail/tiles/StretchableTile.java | 4 +- .../de/srsoftware/web4rail/tiles/Switch.java | 4 +- .../web4rail/tiles/TextDisplay.java | 4 +- .../de/srsoftware/web4rail/tiles/Tile.java | 4 +- .../de/srsoftware/web4rail/tiles/Turnout.java | 4 +- .../srsoftware/web4rail/tiles/TurnoutL.java | 4 +- .../srsoftware/web4rail/tiles/TurnoutR.java | 4 +- 63 files changed, 205 insertions(+), 184 deletions(-) diff --git a/pom.xml b/pom.xml index 685ad2a..ed11862 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 de.srsoftware web4rail - 1.3.56 + 1.3.57 Web4Rail jar Java Model Railway Control diff --git a/resources/css/style.css b/resources/css/style.css index 60088db..9902704 100644 --- a/resources/css/style.css +++ b/resources/css/style.css @@ -256,7 +256,14 @@ fieldset{ border-radius: 5px; } .error{ - background: red; + background: orange; + margin: 30px 10px 5px; + padding: 0; + border-radius: 10px; +} + +.error p{ + padding: 5px; } h4,ul{ diff --git a/resources/js/plan.js b/resources/js/plan.js index dfb4ef6..a0fd926 100644 --- a/resources/js/plan.js +++ b/resources/js/plan.js @@ -37,7 +37,7 @@ function arrangeTabs(){ var tabs = $('
',{'class':'tabs'}); var winId = $('.window').attr('id')+"-"; - tabs.insertAfter($('.swapbtn')); + tabs.insertAfter($('.error')); var target = null; var index = null; $('.window > fieldset > legend').each(function(){ diff --git a/resources/translations/Application.de.translation b/resources/translations/Application.de.translation index b140948..15e63fc 100644 --- a/resources/translations/Application.de.translation +++ b/resources/translations/Application.de.translation @@ -1,4 +1,5 @@ abort : abbrechen +Aborting route allocation... : Routen-Reservierung wird abgebrochen... Accessory : Zubehör Action : Aktion Actions : Aktionen diff --git a/src/main/java/de/srsoftware/web4rail/BaseClass.java b/src/main/java/de/srsoftware/web4rail/BaseClass.java index 0c75070..6672bcc 100644 --- a/src/main/java/de/srsoftware/web4rail/BaseClass.java +++ b/src/main/java/de/srsoftware/web4rail/BaseClass.java @@ -423,13 +423,21 @@ public abstract class BaseClass implements Constants{ return (T) this; } - public Window properties() { - return properties(new ArrayList<>(), new FormInput(), new ArrayList<>()); + public Window properties(String...error) { + return properties(new ArrayList<>(), new FormInput(), new ArrayList<>(),error); } - protected Window properties(List
preForm,FormInput formInputs,List
postForm) { + protected Window properties(List
preForm,FormInput formInputs,List
postForm, String...errorMessages) { Window win = new Window(getClass().getSimpleName()+"-properties", t("Properties of {}",this.title())); + + Tag errorDiv = new Tag("div").clazz("error").content(""); + if (errorMessages != null && errorMessages.length > 0) { + for (String errorMessage : errorMessages) { + if (isSet(errorMessage)) new Tag("p").content(errorMessage).addTo(errorDiv); + } + } + errorDiv.addTo(win); preForm.forEach(fieldset -> fieldset.addTo(win)); diff --git a/src/main/java/de/srsoftware/web4rail/Route.java b/src/main/java/de/srsoftware/web4rail/Route.java index 33c8843..5ca606c 100644 --- a/src/main/java/de/srsoftware/web4rail/Route.java +++ b/src/main/java/de/srsoftware/web4rail/Route.java @@ -88,7 +88,7 @@ public class Route extends BaseClass { public Direction endDirection; private Vector path; private Vector signals; - private Train train; + //private Train train; private HashMap triggeredActions = new HashMap(); private HashMap turnouts; private Block startBlock = null; @@ -214,7 +214,7 @@ public class Route extends BaseClass { private Fieldset basicProperties() { Fieldset fieldset = new Fieldset(t("Route properties")); - if (isSet(train)) train.link("span",t("Train")+": "+train).addTo(fieldset); +// if (isSet(train)) train.link("span",t("Train")+": "+train).addTo(fieldset); Tag list = new Tag("ul"); startBlock.link("li",t("Origin: {} to {}",startBlock.name,startDirection)).addTo(list); endBlock.link("li",t("Destination: {} from {}",endBlock.name,endDirection.inverse())).addTo(list); @@ -319,15 +319,16 @@ public class Route extends BaseClass { * @param contact * @param trainHead */ - public void contact(Contact contact) { + public void contact(Context context) { + Contact contact = context.contact(); if (triggeredContacts.contains(contact)) return; // don't trigger contact a second time triggeredContacts.add(contact); - LOG.debug("{} on {} activated {}.",train,this,contact); + LOG.debug("{} on {} activated {}.",context.train(),this,contact); ActionList actions = triggeredActions.get(contact.trigger()); LOG.debug("Contact has id {} / trigger {} and is assigned with {}",contact.id(),contact.trigger(),isNull(actions)?t("nothing"):actions); if (isNull(actions)) return; - Context context = new Context(this).train(train).contact(contact); - actions.fire(context,"Route.Contact("+contact.addr()+")"); + //Context context = new Context(this).train(train).contact(contact); + actions.fire(context.route(this),"Route.Contact("+contact.addr()+")"); } public Vector contacts() { @@ -375,7 +376,7 @@ public class Route extends BaseClass { return endBlock; } - public void finish() { + public void finish(Train train) { LOG.debug("{}.finish()",this); train.endRoute(); free(); @@ -386,6 +387,7 @@ public class Route extends BaseClass { private void free() { for (Tile tile : path) { + Train train = tile.train(); if (isSet(train) && train.onTrace(tile)) { tile.setState(Status.OCCUPIED, train); } else tile.free(); @@ -687,7 +689,7 @@ public class Route extends BaseClass { } @Override - protected Window properties(List
preForm, FormInput formInputs, List
postForm) { + protected Window properties(List
preForm, FormInput formInputs, List
postForm,String...errors) { preForm.add(conditions.list(t("Route will only be available, if all conditions are fulfilled."))); preForm.add(contactsAndActions()); @@ -703,7 +705,7 @@ public class Route extends BaseClass { postForm.add(basicProperties()); if (!turnouts.isEmpty()) postForm.add(turnouts()); postForm.add(brakeTimes()); - Window win = super.properties(preForm, formInputs, postForm); + Window win = super.properties(preForm, formInputs, postForm,errors); previewScript().addTo(win); return win; } @@ -715,7 +717,7 @@ public class Route extends BaseClass { @Override public BaseClass remove() { LOG.debug("Removing route ({}) {}",id(),this); - if (isSet(train)) train.removeChild(this); +// if (isSet(train)) train.removeChild(this); for (Tile tile : path) { tile.removeChild(this); } @@ -734,7 +736,7 @@ public class Route extends BaseClass { if (child == endBlock) endBlock = null; path.remove(child); signals.remove(child); - if (child == train) train = null; +// if (child == train) train = null; for (ActionList list : triggeredActions.values()) { list.removeChild(child); } @@ -746,8 +748,9 @@ public class Route extends BaseClass { public boolean reset() { LOG.debug("{}.reset()",this); + setSignals(Signal.RED); free(); - train = null; +// train = null; return true; } @@ -794,15 +797,15 @@ public class Route extends BaseClass { // if (state == State.STARTED) return true; LOG.debug("{}.start()",this); if (isNull(newTrain)) return false; // can't set route's train to null - if (isSet(train)) { +/* if (isSet(train)) { if (newTrain != train) return false; // can't alter route's train - } else train = newTrain.setRoute(this); // set new train + } else train = */newTrain.setRoute(this); // set new train ActionList startActions = triggeredActions.get(ROUTE_START); if (isSet(startActions)) { - Context context = new Context(train).route(this); - if (!startActions.fire(context,this+".start("+train.name()+")")) return false; // start actions failed + Context context = new Context(newTrain).route(this); + if (!startActions.fire(context,this+".start("+newTrain.name()+")")) return false; // start actions failed } // state = State.STARTED; triggeredContacts.clear(); @@ -822,12 +825,12 @@ public class Route extends BaseClass { @Override public String toString() { - return getClass().getSimpleName()+"("+(isSet(train)?train+":":"")+name()+")"; + return getClass().getSimpleName()+"("+name()+")"; } - public Train train() { +/* public Train train() { return train; - } + }*/ private Fieldset turnouts() { Fieldset win = new Fieldset(t("Turnouts")); diff --git a/src/main/java/de/srsoftware/web4rail/actions/Action.java b/src/main/java/de/srsoftware/web4rail/actions/Action.java index d24afbf..605e6eb 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/Action.java +++ b/src/main/java/de/srsoftware/web4rail/actions/Action.java @@ -138,10 +138,10 @@ public abstract class Action extends BaseClass { } @Override - protected Window properties(List
preForm, FormInput formInputs, List
postForm) { + protected Window properties(List
preForm, FormInput formInputs, List
postForm,String...errors) { formInputs.add(t("Edit json"),button(t("export"), Map.of(ACTION, ACTION_SAVE))); - return super.properties(preForm, formInputs, postForm); + return super.properties(preForm, formInputs, postForm,errors); } public static Tag selector() { diff --git a/src/main/java/de/srsoftware/web4rail/actions/ActionList.java b/src/main/java/de/srsoftware/web4rail/actions/ActionList.java index 27ecfeb..6fe6ffa 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/ActionList.java +++ b/src/main/java/de/srsoftware/web4rail/actions/ActionList.java @@ -252,11 +252,11 @@ public class ActionList extends Action implements Iterable{ } @Override - protected Window properties(List
preForm, FormInput formInputs, List
postForm) { + protected Window properties(List
preForm, FormInput formInputs, List
postForm,String...errors) { Fieldset fieldset = new Fieldset(t("Actions")); list().addTo(fieldset); postForm.add(fieldset); - return super.properties(preForm, formInputs, postForm); + return super.properties(preForm, formInputs, postForm,errors); } @Override diff --git a/src/main/java/de/srsoftware/web4rail/actions/AddRemoveDestination.java b/src/main/java/de/srsoftware/web4rail/actions/AddRemoveDestination.java index 3552aed..96a8698 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/AddRemoveDestination.java +++ b/src/main/java/de/srsoftware/web4rail/actions/AddRemoveDestination.java @@ -86,14 +86,14 @@ public class AddRemoveDestination extends Action { } @Override - protected Window properties(List
preForm, FormInput formInputs, List
postForm) { + protected Window properties(List
preForm, FormInput formInputs, List
postForm,String...errors) { Tag span = new Tag("span"); button(t("Select from plan"),Map.of(ACTION,ACTION_UPDATE,ASSIGN,Train.DESTINATION)).addTo(span); button(t("Clear destinations"),Map.of(ACTION,ACTION_UPDATE,Train.DESTINATION,"0")).addTo(span); formInputs.add(t("Destination")+": "+(isNull(destination) ? t("Clear destinations") : destination),span); formInputs.add(t("Turn at destination"),new Checkbox(TURN, t("Turn"), turnAtDestination)); formInputs.add(t("Shunting"),new Checkbox(SHUNTING, t("Shunting"), shunting)); - return super.properties(preForm, formInputs, postForm); + return super.properties(preForm, formInputs, postForm,errors); } @Override diff --git a/src/main/java/de/srsoftware/web4rail/actions/AddRemoveTag.java b/src/main/java/de/srsoftware/web4rail/actions/AddRemoveTag.java index 52be625..307610c 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/AddRemoveTag.java +++ b/src/main/java/de/srsoftware/web4rail/actions/AddRemoveTag.java @@ -51,13 +51,13 @@ public class AddRemoveTag extends Action{ } @Override - protected Window properties(List
preForm, FormInput formInputs, List
postForm) { + protected Window properties(List
preForm, FormInput formInputs, List
postForm,String...errors) { formInputs.add(t("Tag"),new Input(TAG, tag)); Tag div = new Tag("div"); new Radio(TYPE, ACTION_ADD, t("add"), !remove).addTo(div); new Radio(TYPE, ACTION_DROP, t("delete"), remove).addTo(div); formInputs.add(t("Action"),div); - return super.properties(preForm, formInputs, postForm); + return super.properties(preForm, formInputs, postForm,errors); } @Override diff --git a/src/main/java/de/srsoftware/web4rail/actions/AlterDirection.java b/src/main/java/de/srsoftware/web4rail/actions/AlterDirection.java index 7c6d98d..ea342d2 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/AlterDirection.java +++ b/src/main/java/de/srsoftware/web4rail/actions/AlterDirection.java @@ -77,14 +77,14 @@ public class AlterDirection extends Action{ } @Override - protected Window properties(List
preForm, FormInput formInputs, List
postForm) { + protected Window properties(List
preForm, FormInput formInputs, List
postForm,String...errors) { Tag radios = new Tag("div"); for (NEWDIR d : NEWDIR.values()) { new Radio(NEW_DIRECTION, d, t("{}",d), newDir == d).addTo(radios); } formInputs.add(t("new direction"),radios); - return super.properties(preForm, formInputs, postForm); + return super.properties(preForm, formInputs, postForm,errors); } @SuppressWarnings("incomplete-switch") diff --git a/src/main/java/de/srsoftware/web4rail/actions/ConditionalAction.java b/src/main/java/de/srsoftware/web4rail/actions/ConditionalAction.java index 1f3ad8f..ae86c67 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/ConditionalAction.java +++ b/src/main/java/de/srsoftware/web4rail/actions/ConditionalAction.java @@ -70,9 +70,9 @@ public class ConditionalAction extends ActionList { } @Override - protected Window properties(List
preForm, FormInput formInputs, List
postForm) { + protected Window properties(List
preForm, FormInput formInputs, List
postForm,String...errors) { preForm.add(conditions.list()); - return super.properties(preForm, formInputs, postForm); + return super.properties(preForm, formInputs, postForm,errors); } diff --git a/src/main/java/de/srsoftware/web4rail/actions/CoupleTrain.java b/src/main/java/de/srsoftware/web4rail/actions/CoupleTrain.java index a4557fd..c2fb782 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/CoupleTrain.java +++ b/src/main/java/de/srsoftware/web4rail/actions/CoupleTrain.java @@ -56,10 +56,10 @@ public class CoupleTrain extends Action { } @Override - protected Window properties(List
preForm, FormInput formInputs, List
postForm) { + protected Window properties(List
preForm, FormInput formInputs, List
postForm,String...errors) { formInputs.add(t("Couple"),new Checkbox(LAST, t("last parked train"), last)); formInputs.add(t("Swap order"),new Checkbox(SWAP, t("Swap order of trains"), swap)); - return super.properties(preForm, formInputs, postForm); + return super.properties(preForm, formInputs, postForm,errors); } @Override diff --git a/src/main/java/de/srsoftware/web4rail/actions/DelayedAction.java b/src/main/java/de/srsoftware/web4rail/actions/DelayedAction.java index c03f321..5cca381 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/DelayedAction.java +++ b/src/main/java/de/srsoftware/web4rail/actions/DelayedAction.java @@ -61,10 +61,10 @@ public class DelayedAction extends ActionList { } @Override - protected Window properties(List
preForm, FormInput formInputs, List
postForm) { + protected Window properties(List
preForm, FormInput formInputs, List
postForm,String...errors) { formInputs.add(t("Minimum delay"),new Input(MIN_DELAY,min_delay).numeric().addTo(new Tag("span")).content(NBSP+"ms")); formInputs.add(t("Maximum delay"),new Input(MAX_DELAY,max_delay).numeric().addTo(new Tag("span")).content(NBSP+"ms")); - return super.properties(preForm, formInputs, postForm); + return super.properties(preForm, formInputs, postForm,errors); } public DelayedAction setMaxDelay(int max_delay) { diff --git a/src/main/java/de/srsoftware/web4rail/actions/DetermineTrainInBlock.java b/src/main/java/de/srsoftware/web4rail/actions/DetermineTrainInBlock.java index c73b5aa..1c70ef7 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/DetermineTrainInBlock.java +++ b/src/main/java/de/srsoftware/web4rail/actions/DetermineTrainInBlock.java @@ -55,9 +55,9 @@ public class DetermineTrainInBlock extends Action { } @Override - protected Window properties(List
preForm, FormInput formInputs, List
postForm) { + protected Window properties(List
preForm, FormInput formInputs, List
postForm,String...errors) { formInputs.add(t("Block")+": "+(isNull(block) ? t("unset") : block),button(t("Select from plan"),Map.of(ACTION,ACTION_UPDATE,ASSIGN,BLOCK))); - return super.properties(preForm, formInputs, postForm); + return super.properties(preForm, formInputs, postForm,errors); } @Override diff --git a/src/main/java/de/srsoftware/web4rail/actions/DisableEnableBlock.java b/src/main/java/de/srsoftware/web4rail/actions/DisableEnableBlock.java index 6aee81f..a84f217 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/DisableEnableBlock.java +++ b/src/main/java/de/srsoftware/web4rail/actions/DisableEnableBlock.java @@ -64,13 +64,13 @@ public class DisableEnableBlock extends Action { } @Override - protected Window properties(List
preForm, FormInput formInputs, List
postForm) { + protected Window properties(List
preForm, FormInput formInputs, List
postForm,String...errors) { formInputs.add(t("Block")+": "+(isNull(block) ? t("block from context") : block),button(t("Select from plan"),Map.of(ACTION,ACTION_UPDATE,ASSIGN,Block.class.getSimpleName()))); Tag radios = new Tag("p"); new Radio(STATE, "enable", t("enable"), !disable).addTo(radios); new Radio(STATE, "disable", t("disable"), disable).addTo(radios); formInputs.add(t("Action"),radios); - return super.properties(preForm, formInputs, postForm); + return super.properties(preForm, formInputs, postForm,errors); } @Override diff --git a/src/main/java/de/srsoftware/web4rail/actions/EngageDecoupler.java b/src/main/java/de/srsoftware/web4rail/actions/EngageDecoupler.java index f5c21ee..7152a03 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/EngageDecoupler.java +++ b/src/main/java/de/srsoftware/web4rail/actions/EngageDecoupler.java @@ -57,10 +57,10 @@ public class EngageDecoupler extends Action { } @Override - protected Window properties(List
preForm, FormInput formInputs, List
postForm) { + protected Window properties(List
preForm, FormInput formInputs, List
postForm,String...errors) { formInputs.add(t("Decoupler")+": "+(isNull(decoupler) ? t("unset") : decoupler),button(t("Select from plan"),Map.of(ACTION,ACTION_UPDATE,ASSIGN,DECOUPLER))); - return super.properties(preForm, formInputs, postForm); + return super.properties(preForm, formInputs, postForm,errors); } @Override diff --git a/src/main/java/de/srsoftware/web4rail/actions/FinishRoute.java b/src/main/java/de/srsoftware/web4rail/actions/FinishRoute.java index 0a8a94f..a263f94 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/FinishRoute.java +++ b/src/main/java/de/srsoftware/web4rail/actions/FinishRoute.java @@ -2,6 +2,7 @@ package de.srsoftware.web4rail.actions; import de.srsoftware.web4rail.BaseClass; import de.srsoftware.web4rail.Route; +import de.srsoftware.web4rail.moving.Train; public class FinishRoute extends Action { @@ -12,7 +13,9 @@ public class FinishRoute extends Action { @Override public boolean fire(Context context,Object cause) { Route route = context.route(); - if (isSet(route)) route.finish(); + Train train = context.train(); + if (isNull(train)) return false; + if (isSet(route)) route.finish(train); return true; } } diff --git a/src/main/java/de/srsoftware/web4rail/actions/Loop.java b/src/main/java/de/srsoftware/web4rail/actions/Loop.java index 818fe76..a0ac414 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/Loop.java +++ b/src/main/java/de/srsoftware/web4rail/actions/Loop.java @@ -73,9 +73,9 @@ public class Loop extends ActionList { } @Override - protected Window properties(List
preForm, FormInput formInputs, List
postForm) { + protected Window properties(List
preForm, FormInput formInputs, List
postForm,String...errors) { formInputs.add(t("Select object"),typeSelector()); - return super.properties(preForm, formInputs, postForm); + return super.properties(preForm, formInputs, postForm,errors); } public String toString() { diff --git a/src/main/java/de/srsoftware/web4rail/actions/SendCommand.java b/src/main/java/de/srsoftware/web4rail/actions/SendCommand.java index 2081ad1..6f186e8 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/SendCommand.java +++ b/src/main/java/de/srsoftware/web4rail/actions/SendCommand.java @@ -71,13 +71,13 @@ public class SendCommand extends Action{ } @Override - protected Window properties(List
preForm, FormInput formInputs, List
postForm) { + protected Window properties(List
preForm, FormInput formInputs, List
postForm,String...errors) { formInputs.add(t("Command to send"),new Input(COMMAND, command)); Tag div = new Tag("div"); new Radio(TARGET, Target.SYSTEM, t("Operating System"), target == Target.SYSTEM).addTo(div); new Radio(TARGET, Target.SRCP, t("SRCP daemon"), target == Target.SRCP).addTo(div); formInputs.add(t("Send command to"),div); - return super.properties(preForm, formInputs, postForm); + return super.properties(preForm, formInputs, postForm,errors); } @Override diff --git a/src/main/java/de/srsoftware/web4rail/actions/SetContextTrain.java b/src/main/java/de/srsoftware/web4rail/actions/SetContextTrain.java index 4ea9109..6cb6a70 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/SetContextTrain.java +++ b/src/main/java/de/srsoftware/web4rail/actions/SetContextTrain.java @@ -52,9 +52,9 @@ public class SetContextTrain extends Action { } @Override - protected Window properties(List
preForm, FormInput formInputs, List
postForm) { + protected Window properties(List
preForm, FormInput formInputs, List
postForm,String...errors) { formInputs.add(t("Select train"),Train.selector(train, null)); - return super.properties(preForm, formInputs, postForm); + return super.properties(preForm, formInputs, postForm,errors); } @Override diff --git a/src/main/java/de/srsoftware/web4rail/actions/SetDisplayText.java b/src/main/java/de/srsoftware/web4rail/actions/SetDisplayText.java index 53999f7..1818331 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/SetDisplayText.java +++ b/src/main/java/de/srsoftware/web4rail/actions/SetDisplayText.java @@ -62,9 +62,9 @@ public class SetDisplayText extends TextAction{ } @Override - protected Window properties(List
preForm, FormInput formInputs, List
postForm) { + protected Window properties(List
preForm, FormInput formInputs, List
postForm,String...errors) { formInputs.add(t("Display")+": "+(isNull(display) ? t("unset") : display),button(t("Select from plan"),Map.of(ACTION,ACTION_UPDATE,ASSIGN,DISPLAY))); - return super.properties(preForm, formInputs, postForm); + return super.properties(preForm, formInputs, postForm,errors); } @Override diff --git a/src/main/java/de/srsoftware/web4rail/actions/SetPower.java b/src/main/java/de/srsoftware/web4rail/actions/SetPower.java index 21889f6..3de5b74 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/SetPower.java +++ b/src/main/java/de/srsoftware/web4rail/actions/SetPower.java @@ -58,14 +58,14 @@ public class SetPower extends Action{ } @Override - protected Window properties(List
preForm, FormInput formInputs, List
postForm) { + protected Window properties(List
preForm, FormInput formInputs, List
postForm,String...errors) { Tag div = new Tag("div"); new Radio(STATE, POWERCHANGE.ON, t("On"), pc == POWERCHANGE.ON).addTo(div); new Radio(STATE, POWERCHANGE.OFF, t("Off"), pc == POWERCHANGE.OFF).addTo(div); new Radio(STATE, POWERCHANGE.TOGGLE, t("Toggle"), pc == POWERCHANGE.TOGGLE).addTo(div); formInputs.add(t("Set state to"),div); - return super.properties(preForm, formInputs, postForm); + return super.properties(preForm, formInputs, postForm,errors); } @Override diff --git a/src/main/java/de/srsoftware/web4rail/actions/SetRelayOrSwitch.java b/src/main/java/de/srsoftware/web4rail/actions/SetRelayOrSwitch.java index 19eeadb..a437833 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/SetRelayOrSwitch.java +++ b/src/main/java/de/srsoftware/web4rail/actions/SetRelayOrSwitch.java @@ -79,7 +79,7 @@ public class SetRelayOrSwitch extends Action { } @Override - protected Window properties(List
preForm, FormInput formInputs, List
postForm) { + protected Window properties(List
preForm, FormInput formInputs, List
postForm,String...errors) { Tag span = new Tag("span"); if (isSet(relayOrSwitch)) span.content(relayOrSwitch+NBSP); button(t("Select from plan"),Map.of(ACTION,ACTION_UPDATE,ASSIGN,Relay.class.getSimpleName())).addTo(span); @@ -96,7 +96,7 @@ public class SetRelayOrSwitch extends Action { } formInputs.add(t("Select state"),state); - return super.properties(preForm, formInputs, postForm); + return super.properties(preForm, formInputs, postForm,errors); } @Override diff --git a/src/main/java/de/srsoftware/web4rail/actions/SetSignal.java b/src/main/java/de/srsoftware/web4rail/actions/SetSignal.java index 02304f5..883f9b3 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/SetSignal.java +++ b/src/main/java/de/srsoftware/web4rail/actions/SetSignal.java @@ -59,7 +59,7 @@ public class SetSignal extends Action { } @Override - protected Window properties(List
preForm, FormInput formInputs, List
postForm) { + protected Window properties(List
preForm, FormInput formInputs, List
postForm,String...errors) { formInputs.add(t("Signal")+": "+(isNull(signal) ? t("unset") : signal),button(t("Select from plan"),Map.of(ACTION,ACTION_UPDATE,ASSIGN,SIGNAL))); Select state = new Select(Signal.STATE); for (String st:Signal.knownStates) { @@ -68,7 +68,7 @@ public class SetSignal extends Action { } formInputs.add(t("Select state"),state); - return super.properties(preForm, formInputs, postForm); + return super.properties(preForm, formInputs, postForm,errors); } @Override diff --git a/src/main/java/de/srsoftware/web4rail/actions/SetSpeed.java b/src/main/java/de/srsoftware/web4rail/actions/SetSpeed.java index d0621a8..0d335c5 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/SetSpeed.java +++ b/src/main/java/de/srsoftware/web4rail/actions/SetSpeed.java @@ -47,9 +47,9 @@ public class SetSpeed extends Action{ } @Override - protected Window properties(List
preForm, FormInput formInputs, List
postForm) { + protected Window properties(List
preForm, FormInput formInputs, List
postForm,String...errors) { formInputs.add(t("Set speed to"),new Input(MAX_SPEED, speed).numeric().addTo(new Tag("span")).content(NBSP+speedUnit)); - return super.properties(preForm, formInputs, postForm); + return super.properties(preForm, formInputs, postForm,errors); } public int getSpeed() { diff --git a/src/main/java/de/srsoftware/web4rail/actions/SetTurnout.java b/src/main/java/de/srsoftware/web4rail/actions/SetTurnout.java index 78f8fce..f8cd546 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/SetTurnout.java +++ b/src/main/java/de/srsoftware/web4rail/actions/SetTurnout.java @@ -66,7 +66,7 @@ public class SetTurnout extends Action { } @Override - protected Window properties(List
preForm, FormInput formInputs, List
postForm) { + protected Window properties(List
preForm, FormInput formInputs, List
postForm,String...errors) { formInputs.add(t("Turnout")+": "+(isNull(turnout) ? t("unset") : turnout),button(t("Select from plan"),Map.of(ACTION,ACTION_UPDATE,ASSIGN,TURNOUT))); if (isSet(turnout)) { @@ -79,7 +79,7 @@ public class SetTurnout extends Action { formInputs.add(t("Select state"),select); } - return super.properties(preForm, formInputs, postForm); + return super.properties(preForm, formInputs, postForm,errors); } @Override diff --git a/src/main/java/de/srsoftware/web4rail/actions/SplitTrain.java b/src/main/java/de/srsoftware/web4rail/actions/SplitTrain.java index 9ab466f..c3e383d 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/SplitTrain.java +++ b/src/main/java/de/srsoftware/web4rail/actions/SplitTrain.java @@ -47,9 +47,9 @@ public class SplitTrain extends Action { } @Override - protected Window properties(List
preForm, FormInput formInputs, List
postForm) { + protected Window properties(List
preForm, FormInput formInputs, List
postForm,String...errors) { formInputs.add(t("Split behind"),new Input(POSITION, position).numeric().addTo(new Tag("span")).content(t(" cars"))); - return super.properties(preForm, formInputs, postForm); + return super.properties(preForm, formInputs, postForm,errors); } @Override diff --git a/src/main/java/de/srsoftware/web4rail/actions/StartStopAuto.java b/src/main/java/de/srsoftware/web4rail/actions/StartStopAuto.java index 53be6a3..6d1b0d9 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/StartStopAuto.java +++ b/src/main/java/de/srsoftware/web4rail/actions/StartStopAuto.java @@ -41,13 +41,13 @@ public class StartStopAuto extends Action { } @Override - protected Window properties(List
preForm, FormInput formInputs, List
postForm) { + protected Window properties(List
preForm, FormInput formInputs, List
postForm,String...errors) { Tag radios = new Tag("div"); new Radio(INVERTED, "on", t("Start autopilot"), inverted).addTo(radios); new Radio(INVERTED, "off", t("Stop autopilot"), !inverted).addTo(radios); formInputs.add(t("Action"), radios); - return super.properties(preForm, formInputs, postForm); + return super.properties(preForm, formInputs, postForm,errors); } @Override diff --git a/src/main/java/de/srsoftware/web4rail/actions/SwitchFunction.java b/src/main/java/de/srsoftware/web4rail/actions/SwitchFunction.java index 62768c4..e00d773 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/SwitchFunction.java +++ b/src/main/java/de/srsoftware/web4rail/actions/SwitchFunction.java @@ -61,7 +61,7 @@ public class SwitchFunction extends Action { } @Override - protected Window properties(List
preForm, FormInput formInputs, List
postForm) { + protected Window properties(List
preForm, FormInput formInputs, List
postForm,String...errors) { Select selector = new Select(FUNCTION); for (int i=1; i<5;i++) { @@ -76,7 +76,7 @@ public class SwitchFunction extends Action { new Radio(EFFECT, OFF, t("Off"), effect == OFF).addTo(radioGroup); formInputs.add(t("Effect"),radioGroup); - return super.properties(preForm, formInputs, postForm); + return super.properties(preForm, formInputs, postForm,errors); } @Override diff --git a/src/main/java/de/srsoftware/web4rail/actions/TextAction.java b/src/main/java/de/srsoftware/web4rail/actions/TextAction.java index 5cce90d..1b43b1e 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/TextAction.java +++ b/src/main/java/de/srsoftware/web4rail/actions/TextAction.java @@ -46,9 +46,9 @@ public abstract class TextAction extends Action { } @Override - protected Window properties(List
preForm, FormInput formInputs, List
postForm) { + protected Window properties(List
preForm, FormInput formInputs, List
postForm,String...errors) { formInputs.add(t("Text"),new Input(TEXT, text)); - return super.properties(preForm, formInputs, postForm); + return super.properties(preForm, formInputs, postForm,errors); } @Override diff --git a/src/main/java/de/srsoftware/web4rail/actions/TriggerContact.java b/src/main/java/de/srsoftware/web4rail/actions/TriggerContact.java index 4c60474..d825322 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/TriggerContact.java +++ b/src/main/java/de/srsoftware/web4rail/actions/TriggerContact.java @@ -41,9 +41,9 @@ public class TriggerContact extends Action { } @Override - protected Window properties(List
preForm, FormInput formInputs, List
postForm) { + protected Window properties(List
preForm, FormInput formInputs, List
postForm,String...errors) { formInputs.add(t("Select contact")+": "+(isNull(contact) ? t("unset") : contact),button(t("Select from plan"),Map.of(ACTION,ACTION_UPDATE,ASSIGN,CONTACT))); - return super.properties(preForm, formInputs, postForm); + return super.properties(preForm, formInputs, postForm,errors); } @Override diff --git a/src/main/java/de/srsoftware/web4rail/actions/WaitForContact.java b/src/main/java/de/srsoftware/web4rail/actions/WaitForContact.java index 754486a..a33083c 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/WaitForContact.java +++ b/src/main/java/de/srsoftware/web4rail/actions/WaitForContact.java @@ -99,7 +99,7 @@ public class WaitForContact extends ActionList { } @Override - protected Window properties(List
preForm, FormInput formInputs, List
postForm) { + protected Window properties(List
preForm, FormInput formInputs, List
postForm,String...errors) { formInputs.add(t("Contact")+": "+(isNull(contact) ? t("unset") : contact),button(t("Select from plan"),Map.of(ACTION,ACTION_UPDATE,ASSIGN,CONTACT))); formInputs.add(t("Timeout"),new Input(TIMEOUT,timeout).numeric().addTo(new Tag("span")).content(NBSP+"ms")); @@ -108,7 +108,7 @@ public class WaitForContact extends ActionList { timeoutActions.list().addTo(fieldset); postForm.add(fieldset); - return super.properties(preForm, formInputs, postForm); + return super.properties(preForm, formInputs, postForm,errors); } @Override diff --git a/src/main/java/de/srsoftware/web4rail/conditions/BlockFree.java b/src/main/java/de/srsoftware/web4rail/conditions/BlockFree.java index d46794b..b4b8659 100644 --- a/src/main/java/de/srsoftware/web4rail/conditions/BlockFree.java +++ b/src/main/java/de/srsoftware/web4rail/conditions/BlockFree.java @@ -51,9 +51,9 @@ public class BlockFree extends Condition { } @Override - protected Window properties(List
preForm, FormInput formInputs, List
postForm) { + protected Window properties(List
preForm, FormInput formInputs, List
postForm,String...errors) { formInputs.add(t("Block")+": "+(isNull(block) ? t("unset") : block),button(t("Select from plan"),Map.of(ACTION,ACTION_UPDATE,ASSIGN,BLOCK))); - return super.properties(preForm, formInputs, postForm); + return super.properties(preForm, formInputs, postForm,errors); } @Override diff --git a/src/main/java/de/srsoftware/web4rail/conditions/CarInTrain.java b/src/main/java/de/srsoftware/web4rail/conditions/CarInTrain.java index 532cbd5..a037d14 100644 --- a/src/main/java/de/srsoftware/web4rail/conditions/CarInTrain.java +++ b/src/main/java/de/srsoftware/web4rail/conditions/CarInTrain.java @@ -38,10 +38,10 @@ public class CarInTrain extends Condition { } @Override - protected Window properties(List
preForm, FormInput formInputs, List
postForm) { + protected Window properties(List
preForm, FormInput formInputs, List
postForm,String...errors) { formInputs.add(t("Select car"),Car.selector(car, null)); - return super.properties(preForm, formInputs, postForm); + return super.properties(preForm, formInputs, postForm,errors); } @Override diff --git a/src/main/java/de/srsoftware/web4rail/conditions/CarOrientation.java b/src/main/java/de/srsoftware/web4rail/conditions/CarOrientation.java index 9463942..8ef19fb 100644 --- a/src/main/java/de/srsoftware/web4rail/conditions/CarOrientation.java +++ b/src/main/java/de/srsoftware/web4rail/conditions/CarOrientation.java @@ -59,7 +59,7 @@ public class CarOrientation extends Condition { } @Override - protected Window properties(List
preForm, FormInput formInputs, List
postForm) { + protected Window properties(List
preForm, FormInput formInputs, List
postForm,String...errors) { formInputs.add(t("Select car"),Car.selector(isSet(car) ? car : t("Car of train"), null)); formInputs.add(t("If car of train: inspect car number"),new Input(POSITION, position).numeric().addTo(new Tag("span")).content(NBSP+"("+t("Use negative number to count from end.")+")")); @@ -67,7 +67,7 @@ public class CarOrientation extends Condition { new Radio(ORIENTATION, "f", t("forward"), orientation).addTo(radioGroup); new Radio(ORIENTATION, "r", t("revers"), !orientation).addTo(radioGroup); - return super.properties(preForm, formInputs, postForm); + return super.properties(preForm, formInputs, postForm,errors); } @Override diff --git a/src/main/java/de/srsoftware/web4rail/conditions/Condition.java b/src/main/java/de/srsoftware/web4rail/conditions/Condition.java index 6dab371..d10d46e 100644 --- a/src/main/java/de/srsoftware/web4rail/conditions/Condition.java +++ b/src/main/java/de/srsoftware/web4rail/conditions/Condition.java @@ -142,9 +142,9 @@ public abstract class Condition extends BaseClass { } @Override - protected Window properties(List
preForm, FormInput formInputs, List
postForm) { + protected Window properties(List
preForm, FormInput formInputs, List
postForm,String...errors) { inversionOption(formInputs); - return super.properties(preForm, formInputs, postForm); + return super.properties(preForm, formInputs, postForm,errors); } public static Select selector() { diff --git a/src/main/java/de/srsoftware/web4rail/conditions/ConditionList.java b/src/main/java/de/srsoftware/web4rail/conditions/ConditionList.java index 8f4467b..435ef4c 100644 --- a/src/main/java/de/srsoftware/web4rail/conditions/ConditionList.java +++ b/src/main/java/de/srsoftware/web4rail/conditions/ConditionList.java @@ -114,10 +114,10 @@ public class ConditionList extends Condition implements Iterable{ } @Override - public Window properties() { + public Window properties(String...errors) { BaseClass parent = parent(); - if (isSet(parent)) return parent.properties(); - return super.properties(); + if (isSet(parent)) return parent.properties(errors); + return super.properties(errors); } @Override diff --git a/src/main/java/de/srsoftware/web4rail/conditions/RouteEndBlock.java b/src/main/java/de/srsoftware/web4rail/conditions/RouteEndBlock.java index 6eec56a..a055459 100644 --- a/src/main/java/de/srsoftware/web4rail/conditions/RouteEndBlock.java +++ b/src/main/java/de/srsoftware/web4rail/conditions/RouteEndBlock.java @@ -57,9 +57,9 @@ public class RouteEndBlock extends Condition{ } @Override - protected Window properties(List
preForm, FormInput formInputs, List
postForm) { + protected Window properties(List
preForm, FormInput formInputs, List
postForm,String...errors) { formInputs.add(t("Block")+": "+(isNull(block) ? t("unset") : block),button(t("Select from plan"),Map.of(ACTION,ACTION_UPDATE,ASSIGN,BLOCK))); - return super.properties(preForm, formInputs, postForm); + return super.properties(preForm, formInputs, postForm,errors); } @Override diff --git a/src/main/java/de/srsoftware/web4rail/conditions/SwitchIsOn.java b/src/main/java/de/srsoftware/web4rail/conditions/SwitchIsOn.java index dacb2a7..69f2c92 100644 --- a/src/main/java/de/srsoftware/web4rail/conditions/SwitchIsOn.java +++ b/src/main/java/de/srsoftware/web4rail/conditions/SwitchIsOn.java @@ -61,10 +61,10 @@ public class SwitchIsOn extends Condition { } @Override - protected Window properties(List
preForm, FormInput formInputs, List
postForm) { + protected Window properties(List
preForm, FormInput formInputs, List
postForm,String...errors) { formInputs.add(t("Select switch")+": "+(isNull(swtch) ? t("unset") : swtch),button(t("Select from plan"),Map.of(ACTION,ACTION_UPDATE,ASSIGN,SWITCH))); - return super.properties(preForm, formInputs, postForm); + return super.properties(preForm, formInputs, postForm,errors); } @Override diff --git a/src/main/java/de/srsoftware/web4rail/conditions/TrainHasTag.java b/src/main/java/de/srsoftware/web4rail/conditions/TrainHasTag.java index 43206f6..c9f6dcc 100644 --- a/src/main/java/de/srsoftware/web4rail/conditions/TrainHasTag.java +++ b/src/main/java/de/srsoftware/web4rail/conditions/TrainHasTag.java @@ -46,9 +46,9 @@ public class TrainHasTag extends Condition { } @Override - protected Window properties(List
preForm, FormInput formInputs, List
postForm) { + protected Window properties(List
preForm, FormInput formInputs, List
postForm,String...errors) { formInputs.add(t("Tag"),new Input(TAG, tag == null ? "" : tag)); - return super.properties(preForm, formInputs, postForm); + return super.properties(preForm, formInputs, postForm,errors); } @Override diff --git a/src/main/java/de/srsoftware/web4rail/conditions/TrainLength.java b/src/main/java/de/srsoftware/web4rail/conditions/TrainLength.java index 1b3f084..284cb84 100644 --- a/src/main/java/de/srsoftware/web4rail/conditions/TrainLength.java +++ b/src/main/java/de/srsoftware/web4rail/conditions/TrainLength.java @@ -34,9 +34,9 @@ public class TrainLength extends Condition { } @Override - protected Window properties(List
preForm, FormInput formInputs, List
postForm) { + protected Window properties(List
preForm, FormInput formInputs, List
postForm,String...errors) { formInputs.add(t("Maximum train length"),new Input(LENGTH, treshold).numeric().addTo(new Tag("span")).content(lengthUnit)); - return super.properties(preForm, formInputs, postForm); + return super.properties(preForm, formInputs, postForm,errors); } @Override diff --git a/src/main/java/de/srsoftware/web4rail/conditions/TrainSelect.java b/src/main/java/de/srsoftware/web4rail/conditions/TrainSelect.java index 5ca364a..100d329 100644 --- a/src/main/java/de/srsoftware/web4rail/conditions/TrainSelect.java +++ b/src/main/java/de/srsoftware/web4rail/conditions/TrainSelect.java @@ -34,9 +34,9 @@ public class TrainSelect extends Condition { } @Override - protected Window properties(List
preForm, FormInput formInputs, List
postForm) { + protected Window properties(List
preForm, FormInput formInputs, List
postForm,String...errors) { formInputs.add(t("Select train")+":",Train.selector(train, null)); - return super.properties(preForm, formInputs, postForm); + return super.properties(preForm, formInputs, postForm,errors); } @Override diff --git a/src/main/java/de/srsoftware/web4rail/conditions/TrainSpeed.java b/src/main/java/de/srsoftware/web4rail/conditions/TrainSpeed.java index 885141f..d0586ed 100644 --- a/src/main/java/de/srsoftware/web4rail/conditions/TrainSpeed.java +++ b/src/main/java/de/srsoftware/web4rail/conditions/TrainSpeed.java @@ -33,9 +33,9 @@ public class TrainSpeed extends Condition { } @Override - protected Window properties(List
preForm, FormInput formInputs, List
postForm) { + protected Window properties(List
preForm, FormInput formInputs, List
postForm,String...errors) { formInputs.add(t("Train speed"),new Input(SPEED, treshold).numeric().addTo(new Tag("span")).content(speedUnit)); - return super.properties(preForm, formInputs, postForm); + return super.properties(preForm, formInputs, postForm,errors); } @Override diff --git a/src/main/java/de/srsoftware/web4rail/conditions/TrainWasInBlock.java b/src/main/java/de/srsoftware/web4rail/conditions/TrainWasInBlock.java index ed67d39..61d9ce3 100644 --- a/src/main/java/de/srsoftware/web4rail/conditions/TrainWasInBlock.java +++ b/src/main/java/de/srsoftware/web4rail/conditions/TrainWasInBlock.java @@ -48,10 +48,10 @@ public class TrainWasInBlock extends Condition { } @Override - protected Window properties(List
preForm, FormInput formInputs, List
postForm) { + protected Window properties(List
preForm, FormInput formInputs, List
postForm,String...errors) { formInputs.add(t("Block")+": "+(isNull(block) ? t("block from context") : block),button(t("Select from plan"),Map.of(ACTION,ACTION_UPDATE,ASSIGN,BLOCK))); formInputs.add(t("Seek in last"), new Input(COUNT, count).numeric().addTo(new Tag("span")).content(NBSP+t("blocks of train"))); - return super.properties(preForm, formInputs, postForm); + return super.properties(preForm, formInputs, postForm,errors); } @Override diff --git a/src/main/java/de/srsoftware/web4rail/moving/Car.java b/src/main/java/de/srsoftware/web4rail/moving/Car.java index c46499d..731f0f4 100644 --- a/src/main/java/de/srsoftware/web4rail/moving/Car.java +++ b/src/main/java/de/srsoftware/web4rail/moving/Car.java @@ -243,7 +243,7 @@ public class Car extends BaseClass implements Comparable{ } @Override - protected Window properties(List
preForm, FormInput formInputs, List
postForm) { + protected Window properties(List
preForm, FormInput formInputs, List
postForm,String...errors) { formInputs.add(t("Name"),new Input(NAME,name)); formInputs.add(t("Stock ID"),new Input(STOCK_ID,stockId)); formInputs.add(t("Length"),new Input(LENGTH,length).attr("type", "number").addTo(new Tag("span")).content(NBSP+lengthUnit)); @@ -255,7 +255,7 @@ public class Car extends BaseClass implements Comparable{ if (train != null) formInputs.add(t("Train"), train.link()); formInputs.add(t("Current orientation"),new Tag("span").content(orientation ? t("forward") : t("reverse"))); - return super.properties(preForm,formInputs,postForm); + return super.properties(preForm,formInputs,postForm,errors); } @Override diff --git a/src/main/java/de/srsoftware/web4rail/moving/Locomotive.java b/src/main/java/de/srsoftware/web4rail/moving/Locomotive.java index b474cc5..0d392d5 100644 --- a/src/main/java/de/srsoftware/web4rail/moving/Locomotive.java +++ b/src/main/java/de/srsoftware/web4rail/moving/Locomotive.java @@ -142,7 +142,7 @@ public class Locomotive extends Car implements Constants,Device{ par.addTo(fieldset); Tag direction = new Tag("p"); - if ((isSet(train) && (train.speed > 0 || isSet(train.route()))) || (isSet(loco) && loco.speed > 0)) { + if ((isSet(train) && train.isStoppable()) || (isSet(loco) && loco.speed > 0)) { params.put(ACTION, ACTION_STOP); new Button(t("Stop"),params).clazz(ACTION_STOP).addTo(direction); } @@ -338,7 +338,7 @@ public class Locomotive extends Car implements Constants,Device{ } @Override - protected Window properties(List
preForm, FormInput formInputs, List
postForm) { + protected Window properties(List
preForm, FormInput formInputs, List
postForm,String...errors) { preForm.add(cockpit(this)); Tag div = new Tag("div"); for (Protocol proto : Protocol.values()) { @@ -347,7 +347,7 @@ public class Locomotive extends Car implements Constants,Device{ formInputs.add(t("Protocol"),div); formInputs.add(t("Address"),new Input(ADDRESS, address).numeric()); postForm.add(programming()); - return super.properties(preForm, formInputs, postForm); + return super.properties(preForm, formInputs, postForm,errors); } private void queue() { diff --git a/src/main/java/de/srsoftware/web4rail/moving/Train.java b/src/main/java/de/srsoftware/web4rail/moving/Train.java index 09ec77a..0de8930 100644 --- a/src/main/java/de/srsoftware/web4rail/moving/Train.java +++ b/src/main/java/de/srsoftware/web4rail/moving/Train.java @@ -48,15 +48,6 @@ import de.srsoftware.web4rail.tiles.Tile.Status; * @author Stephan Richter, SRSoftware 2020-2021 * */ public class Train extends BaseClass implements Comparable { - - public interface Listener { - enum Signal { - STOP - } - - public void on(Signal signal); - } - private static final Logger LOG = LoggerFactory.getLogger(Train.class); private static final String CAR_ID = "carId"; @@ -101,10 +92,10 @@ public class Train extends BaseClass implements Comparable { private static final String SHUNTING = "shunting"; private boolean shunting = false; - private HashSet listeners = new HashSet(); - private BrakeProcessor brakeProcessor; + private PathFinder pathFinder; + public static Object action(HashMap params, Plan plan) throws IOException { String action = params.get(ACTION); if (isNull(action)) return t("No action passed to Train.action!"); @@ -150,8 +141,8 @@ public class Train extends BaseClass implements Comparable { case ACTION_SLOWER10: return train.slower(10); case ACTION_START: - train.start(); - return train.properties(); + String error = train.start(); + return train.properties(error); case ACTION_STOP: return train.stopNow(); case ACTION_TIMES: @@ -186,10 +177,6 @@ public class Train extends BaseClass implements Comparable { return properties(); } - public void addListener(Listener listener) { - listeners.add(listener); - } - public boolean automatic() { return false; } @@ -323,7 +310,7 @@ public class Train extends BaseClass implements Comparable { public void contact(Contact contact) { if (isSet(route)) { Route lastRoute = route; // route field might be set to null during route.contact(...)! - route.contact(contact); + route.contact(new Context(contact).train(this)); traceFrom(contact,lastRoute); } } @@ -625,7 +612,7 @@ public class Train extends BaseClass implements Comparable { @Override - protected Window properties(List
preForm, FormInput formInputs, List
postForm) { + protected Window properties(List
preForm, FormInput formInputs, List
postForm,String...errors) { Tag propList = new Tag("ul").clazz("proplist"); if (isSet(currentBlock)) currentBlock.button(currentBlock.toString()).addTo(new Tag("li").content(t("Current location")+COL)).addTo(propList); @@ -675,7 +662,7 @@ public class Train extends BaseClass implements Comparable { postForm.add(blockHistory()); - return super.properties(preForm, formInputs, postForm); + return super.properties(preForm, formInputs, postForm,errors); } public Object quitAutopilot() { @@ -878,34 +865,37 @@ public class Train extends BaseClass implements Comparable { } - public void start() { - new PathFinder(this,currentBlock,direction) { + public String start() { + if (isSet(pathFinder)) return t("Pathfinder already active for {}!",this); + pathFinder = new PathFinder(this,currentBlock,direction) { @Override public void aborted() { - LOG.debug("Aborted"); + plan.stream(t("Aborting route allocation...")); + LOG.debug("{} aborted",this); } @Override public void found(Route newRoute) { - // TODO Auto-generated method stub - LOG.debug("Found route {} for {}",newRoute,Train.this); + LOG.debug("Found route {} for {}",newRoute,Train.this); } @Override public void locked(Route newRoute) { - // TODO Auto-generated method stub LOG.debug("Locked route {} for {}",newRoute,Train.this); } @Override public void prepared(Route newRoute) { LOG.debug("Prepared route {} for {}",newRoute,Train.this); - newRoute.start(Train.this); + route = newRoute; + pathFinder = null; + route.start(Train.this); } }.start(); + return null; } public static void startAll() { @@ -930,9 +920,12 @@ public class Train extends BaseClass implements Comparable { brakeProcessor = new BrakeProcessor(this).start(); } - public Object stopNow() { + public Window stopNow() { setSpeed(0); - listeners.forEach(listener -> listener.on(Listener.Signal.STOP)); // abort PathFinder + if (isSet(pathFinder)) { + pathFinder.abort(); + pathFinder = null; + } if (isSet(route)) { route.reset(); route = null; @@ -986,8 +979,7 @@ public class Train extends BaseClass implements Comparable { newTrace.add(tile); remainingLength -= tile.length(); } else if (Route.freeBehindTrain) { - - // TODO + tile.free(); } else break; } } @@ -1031,4 +1023,11 @@ public class Train extends BaseClass implements Comparable { public boolean usesAutopilot() { return false; // TODO } + + public boolean isStoppable() { + if (speed > 0) return true; + if (isSet(pathFinder)) return true; + if (isSet(route)) return true; + return false; + } } diff --git a/src/main/java/de/srsoftware/web4rail/threads/BrakeProcessor.java b/src/main/java/de/srsoftware/web4rail/threads/BrakeProcessor.java index b49c0e9..7c3f2d4 100644 --- a/src/main/java/de/srsoftware/web4rail/threads/BrakeProcessor.java +++ b/src/main/java/de/srsoftware/web4rail/threads/BrakeProcessor.java @@ -39,10 +39,12 @@ public class BrakeProcessor extends BaseClass implements Runnable { int step = brakeTime; for (int i = 0; i < 15; i++) { long calculatedDistance = calculate(brakeTime, startSpeed); - step /= 2; - if (step < 1) step = 1; - if (measuredDistance > calculatedDistance) brakeTime += step; - if (measuredDistance < calculatedDistance) brakeTime -= step; + if (measuredDistance > calculatedDistance) brakeTime += brakeTime/2; + if (measuredDistance < calculatedDistance) { + step /= 2; + if (step < 1) step = 1; + brakeTime -= step; + } LOG.debug("new brake time: {}, calculated distance: {}", brakeTime, calculatedDistance); } route.brakeTime(train.brakeId(), brakeTime); diff --git a/src/main/java/de/srsoftware/web4rail/threads/PathFinder.java b/src/main/java/de/srsoftware/web4rail/threads/PathFinder.java index aef3ded..7ecf634 100644 --- a/src/main/java/de/srsoftware/web4rail/threads/PathFinder.java +++ b/src/main/java/de/srsoftware/web4rail/threads/PathFinder.java @@ -18,7 +18,7 @@ import de.srsoftware.web4rail.tiles.Block; /** * @author Stephan Richter, SRSoftware 2020-2021 */ -public abstract class PathFinder extends BaseClass implements Runnable, Train.Listener{ +public abstract class PathFinder extends BaseClass implements Runnable{ public static final Logger LOG = LoggerFactory.getLogger(PathFinder.class); // private Context context; private boolean aborted = false; @@ -150,14 +150,21 @@ public abstract class PathFinder extends BaseClass implements Runnable, Train.Li public void run() { while (true) { if (aborted) return; - Route route = chooseRoute(); + Route route = chooseRoute(); if (isSet(route)) { - found(route); if (aborted) return; + found(route); if (route.allocateFor(train)) { + if (aborted) { + route.reset(); + return; + } locked(route); - if (aborted) return; if (route.prepareFor(train)) { + if (aborted) { + route.reset(); + return; + } prepared(route); return; } @@ -172,19 +179,10 @@ public abstract class PathFinder extends BaseClass implements Runnable, Train.Li public abstract void found(Route r); public abstract void prepared(Route r); - @Override - public void on(Signal signal) { - switch (signal) { - case STOP: - abort(); - break; - } - } - - public void start() { - train.addListener(this); + public PathFinder start() { Thread thread = new Thread(this); thread.setName("Pathfinder("+train+")"); thread.start(); + return this; } } diff --git a/src/main/java/de/srsoftware/web4rail/tiles/Block.java b/src/main/java/de/srsoftware/web4rail/tiles/Block.java index 166c03e..a84a822 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/Block.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/Block.java @@ -325,14 +325,14 @@ public abstract class Block extends StretchableTile{ @Override - protected Window properties(List
preForm, FormInput formInputs, List
postForm) { + protected Window properties(List
preForm, FormInput formInputs, List
postForm,String...errors) { formInputs.add(t("Name"),new Input(NAME, name)); formInputs.add("",new Checkbox(ALLOW_TURN,t("Turn allowed"),turnAllowed)); formInputs.add(t("Train"),Train.selector(train, null)); postForm.add(contactForm()); postForm.add(waitTimeForm()); if (!parkedTrains.isEmpty()) postForm.add(parkedTrainList()); - return super.properties(preForm, formInputs, postForm); + return super.properties(preForm, formInputs, postForm,errors); } public Tile raise(String tag) { diff --git a/src/main/java/de/srsoftware/web4rail/tiles/Bridge.java b/src/main/java/de/srsoftware/web4rail/tiles/Bridge.java index 260f92f..5f7e56c 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/Bridge.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/Bridge.java @@ -75,12 +75,12 @@ public abstract class Bridge extends Tile { } @Override - protected Window properties(List
preForm, FormInput formInputs, List
postForm) { + protected Window properties(List
preForm, FormInput formInputs, List
postForm,String...errors) { Fieldset fieldset = new Fieldset(t("Counterpart")); new Tag("p").content(isSet(counterpart) ? t("Connected to {}.",counterpart) : t("Not connected to other bridge part!")).addTo(fieldset); button(t("Select counterpart"),Map.of(ACTION,ACTION_CONNECT)).addTo(fieldset); preForm.add(fieldset); - return super.properties(preForm, formInputs, postForm); + return super.properties(preForm, formInputs, postForm,errors); } public Window propMenu() { diff --git a/src/main/java/de/srsoftware/web4rail/tiles/Contact.java b/src/main/java/de/srsoftware/web4rail/tiles/Contact.java index db91815..9beccdb 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/Contact.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/Contact.java @@ -190,7 +190,7 @@ public class Contact extends Tile{ } @Override - protected Window properties(List
preForm, FormInput formInputs, List
postForm) { + protected Window properties(List
preForm, FormInput formInputs, List
postForm,String...errors) { Tag span = new Tag("span"); new Input(ADDRESS, addr).numeric().addTo(span).content(NBSP); button(t("learn"),Map.of(ACTION,ACTION_ANALYZE)).addTo(span); @@ -199,7 +199,7 @@ public class Contact extends Tile{ Fieldset fieldset = new Fieldset(t("Actions")).id("props-actions"); actions.list().addTo(fieldset); postForm.add(fieldset); - return super.properties(preForm, formInputs, postForm); + return super.properties(preForm, formInputs, postForm,errors); } @Override diff --git a/src/main/java/de/srsoftware/web4rail/tiles/Decoupler.java b/src/main/java/de/srsoftware/web4rail/tiles/Decoupler.java index 6435689..d3a7157 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/Decoupler.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/Decoupler.java @@ -111,7 +111,7 @@ public abstract class Decoupler extends Tile implements Device{ } @Override - protected Window properties(List
preForm, FormInput formInputs, List
postForm) { + protected Window properties(List
preForm, FormInput formInputs, List
postForm,String...errors) { Tag div = new Tag("div"); for (Protocol proto : Protocol.values()) { new Radio(PROTOCOL, proto.toString(), t(proto.toString()), proto == protocol).addTo(div); @@ -120,7 +120,7 @@ public abstract class Decoupler extends Tile implements Device{ formInputs.add(t("Address"),new Input(ADDRESS, address).numeric()); formInputs.add(t("Port"),new Input(PORT, port).numeric()); - return super.properties(preForm, formInputs, postForm); + return super.properties(preForm, formInputs, postForm,errors); } private char proto() { diff --git a/src/main/java/de/srsoftware/web4rail/tiles/Relay.java b/src/main/java/de/srsoftware/web4rail/tiles/Relay.java index b2a8bb4..d6a15af 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/Relay.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/Relay.java @@ -124,7 +124,7 @@ public class Relay extends Tile implements Device{ } @Override - protected Window properties(List
preForm, FormInput formInputs, List
postForm) { + protected Window properties(List
preForm, FormInput formInputs, List
postForm,String...errors) { formInputs.add(t("Name"),new Input(NAME,name)); Tag div = new Tag("div"); for (Protocol proto : Protocol.values()) { @@ -136,7 +136,7 @@ public class Relay extends Tile implements Device{ formInputs.add(t("Label for state {}","B"),new Input(LABEL_B, stateLabelB)); formInputs.add(t("Port for state {}",stateLabelA),new Input(PORT_A, portA).numeric()); formInputs.add(t("Port for state {}",stateLabelB),new Input(PORT_B, portB).numeric()); - return super.properties(preForm, formInputs, postForm); + return super.properties(preForm, formInputs, postForm,errors); } private char proto() { diff --git a/src/main/java/de/srsoftware/web4rail/tiles/Signal.java b/src/main/java/de/srsoftware/web4rail/tiles/Signal.java index 3f5c537..803ba5d 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/Signal.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/Signal.java @@ -127,7 +127,7 @@ public abstract class Signal extends Tile { } @Override - protected Window properties(List
preForm, FormInput formInputs, List
postForm) { + protected Window properties(List
preForm, FormInput formInputs, List
postForm,String...errors) { Fieldset aspectEditor = new Fieldset(t("Aspects")).id("props-aspects"); Form form = new Form("aspect-form"); new Input(REALM,REALM_PLAN).hideIn(form); @@ -158,7 +158,7 @@ public abstract class Signal extends Tile { form.addTo(aspectEditor); postForm.add(aspectEditor); - return super.properties(preForm, formInputs, postForm); + return super.properties(preForm, formInputs, postForm,errors); } public boolean state(String aspect) { diff --git a/src/main/java/de/srsoftware/web4rail/tiles/StretchableTile.java b/src/main/java/de/srsoftware/web4rail/tiles/StretchableTile.java index eceb967..25285a3 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/StretchableTile.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/StretchableTile.java @@ -56,9 +56,9 @@ public abstract class StretchableTile extends TileWithShadow { } @Override - protected Window properties(List
preForm, FormInput formInputs, List
postForm) { + protected Window properties(List
preForm, FormInput formInputs, List
postForm,String...errors) { formInputs.add(stretchType(),new Input(STRETCH_LENGTH, stretch).numeric().addTo(new Tag("span")).content(NBSP+t("Tile(s)"))); - return super.properties(preForm, formInputs, postForm); + return super.properties(preForm, formInputs, postForm,errors); } @Override diff --git a/src/main/java/de/srsoftware/web4rail/tiles/Switch.java b/src/main/java/de/srsoftware/web4rail/tiles/Switch.java index 974bedf..5a14f43 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/Switch.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/Switch.java @@ -126,7 +126,7 @@ public class Switch extends Tile{ } @Override - protected Window properties(List
preForm, FormInput formInputs, List
postForm) { + protected Window properties(List
preForm, FormInput formInputs, List
postForm,String...errors) { Fieldset fieldset = new Fieldset(t("Actions (On)")); fieldset.id("actionsOn"); actionsOn.list().addTo(fieldset); @@ -135,7 +135,7 @@ public class Switch extends Tile{ fieldset.id("actionsOff"); actionsOff.list().addTo(fieldset); postForm.add(fieldset); - return super.properties(preForm, formInputs, postForm); + return super.properties(preForm, formInputs, postForm,errors); } @Override diff --git a/src/main/java/de/srsoftware/web4rail/tiles/TextDisplay.java b/src/main/java/de/srsoftware/web4rail/tiles/TextDisplay.java index 02610f3..49e847a 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/TextDisplay.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/TextDisplay.java @@ -33,9 +33,9 @@ public class TextDisplay extends StretchableTile { } @Override - protected Window properties(List
preForm, FormInput formInputs, List
postForm) { + protected Window properties(List
preForm, FormInput formInputs, List
postForm,String...errors) { formInputs.add(t("Text"),new Input(TEXT, text)); - return super.properties(preForm, formInputs, postForm); + return super.properties(preForm, formInputs, postForm,errors); } public static Select selector(TextDisplay preselected,Collection exclude) { diff --git a/src/main/java/de/srsoftware/web4rail/tiles/Tile.java b/src/main/java/de/srsoftware/web4rail/tiles/Tile.java index 5887355..e92dcf7 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/Tile.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/Tile.java @@ -260,7 +260,7 @@ public abstract class Tile extends BaseClass implements Comparable{ } @Override - protected Window properties(List
preForm, FormInput formInputs, List
postForm) { + protected Window properties(List
preForm, FormInput formInputs, List
postForm,String...errors) { Fieldset fieldset = null; if (isSet(train)) { @@ -332,7 +332,7 @@ public abstract class Tile extends BaseClass implements Comparable{ } } - return super.properties(preForm, formInputs, postForm); + return super.properties(preForm, formInputs, postForm,errors); } private static String replace(String line, Entry replacement) { diff --git a/src/main/java/de/srsoftware/web4rail/tiles/Turnout.java b/src/main/java/de/srsoftware/web4rail/tiles/Turnout.java index 0b841e6..8ca7fbb 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/Turnout.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/Turnout.java @@ -116,14 +116,14 @@ public abstract class Turnout extends Tile implements Device{ } @Override - protected Window properties(List
preForm, FormInput formInputs, List
postForm) { + protected Window properties(List
preForm, FormInput formInputs, List
postForm,String...errors) { Tag div = new Tag("div"); for (Protocol proto : Protocol.values()) { new Radio(PROTOCOL, proto.toString(), t(proto.toString()), proto == protocol).addTo(div); } formInputs.add(t("Protocol"),div); formInputs.add(t("Address"),new Input(ADDRESS, address).numeric()); - return super.properties(preForm, formInputs, postForm); + return super.properties(preForm, formInputs, postForm,errors); } private char proto() { diff --git a/src/main/java/de/srsoftware/web4rail/tiles/TurnoutL.java b/src/main/java/de/srsoftware/web4rail/tiles/TurnoutL.java index e82fa4b..26bdbfa 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/TurnoutL.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/TurnoutL.java @@ -36,10 +36,10 @@ public abstract class TurnoutL extends Turnout { } @Override - protected Window properties(List
preForm, FormInput formInputs, List
postForm) { + protected Window properties(List
preForm, FormInput formInputs, List
postForm,String...errors) { formInputs.add(t("Straight port")+COL,new Input(STRAIGHT, portA).numeric()); formInputs.add(t("Left port")+COL,new Input(LEFT, portB).numeric()); - return super.properties(preForm, formInputs, postForm); + return super.properties(preForm, formInputs, postForm,errors); } @Override diff --git a/src/main/java/de/srsoftware/web4rail/tiles/TurnoutR.java b/src/main/java/de/srsoftware/web4rail/tiles/TurnoutR.java index 1c5dfde..ec5a003 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/TurnoutR.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/TurnoutR.java @@ -37,10 +37,10 @@ public abstract class TurnoutR extends Turnout { } @Override - protected Window properties(List
preForm, FormInput formInputs, List
postForm) { + protected Window properties(List
preForm, FormInput formInputs, List
postForm,String...errors) { formInputs.add(t("Straight port")+COL,new Input(STRAIGHT, portA).numeric()); formInputs.add(t("Right port")+COL,new Input(RIGHT, portB).numeric()); - return super.properties(preForm, formInputs, postForm); + return super.properties(preForm, formInputs, postForm,errors); } @Override From 9effb8d3370f40ebcb5b6861b8f03f796fe3c2d8 Mon Sep 17 00:00:00 2001 From: Stephan Richter Date: Fri, 12 Mar 2021 16:55:52 +0100 Subject: [PATCH 09/26] re-implemented basic autopilot --- pom.xml | 2 +- .../java/de/srsoftware/web4rail/Route.java | 6 +- .../web4rail/actions/StartStopAuto.java | 2 +- .../de/srsoftware/web4rail/moving/Train.java | 80 +++++++++++-------- .../web4rail/threads/BrakeProcessor.java | 11 +-- .../web4rail/threads/PathFinder.java | 11 +-- 6 files changed, 57 insertions(+), 55 deletions(-) diff --git a/pom.xml b/pom.xml index ed11862..56f0dad 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 de.srsoftware web4rail - 1.3.57 + 1.3.58 Web4Rail jar Java Model Railway Control diff --git a/src/main/java/de/srsoftware/web4rail/Route.java b/src/main/java/de/srsoftware/web4rail/Route.java index 5ca606c..72dee66 100644 --- a/src/main/java/de/srsoftware/web4rail/Route.java +++ b/src/main/java/de/srsoftware/web4rail/Route.java @@ -378,11 +378,9 @@ public class Route extends BaseClass { public void finish(Train train) { LOG.debug("{}.finish()",this); - train.endRoute(); - free(); - train.set(endBlock); - train.heading(endDirection); + train.endRoute(endBlock,endDirection); train = null; + free(); } private void free() { diff --git a/src/main/java/de/srsoftware/web4rail/actions/StartStopAuto.java b/src/main/java/de/srsoftware/web4rail/actions/StartStopAuto.java index 6d1b0d9..b1a8381 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/StartStopAuto.java +++ b/src/main/java/de/srsoftware/web4rail/actions/StartStopAuto.java @@ -24,7 +24,7 @@ public class StartStopAuto extends Action { public boolean fire(Context context,Object cause) { if (isNull(context.train())) return false; if (inverted) { - context.train().automatic(); + context.train().start(true); } else context.train().quitAutopilot(); return true; } diff --git a/src/main/java/de/srsoftware/web4rail/moving/Train.java b/src/main/java/de/srsoftware/web4rail/moving/Train.java index 0de8930..6c8046e 100644 --- a/src/main/java/de/srsoftware/web4rail/moving/Train.java +++ b/src/main/java/de/srsoftware/web4rail/moving/Train.java @@ -96,6 +96,8 @@ public class Train extends BaseClass implements Comparable { private PathFinder pathFinder; + private boolean autopilot = false; + public static Object action(HashMap params, Plan plan) throws IOException { String action = params.get(ACTION); if (isNull(action)) return t("No action passed to Train.action!"); @@ -115,7 +117,7 @@ public class Train extends BaseClass implements Comparable { case ACTION_ADD: return train.addCar(params); case ACTION_AUTO: - return train.automatic(); + return train.start(true); case ACTION_CONNECT: return train.connect(params); case ACTION_DROP: @@ -135,14 +137,13 @@ public class Train extends BaseClass implements Comparable { case ACTION_PROPS: return train.properties(); case ACTION_QUIT: - return train.quitAutopilot(); + return train.properties(train.quitAutopilot()); case ACTION_REVERSE: return train.reverse().properties(); case ACTION_SLOWER10: return train.slower(10); case ACTION_START: - String error = train.start(); - return train.properties(error); + return train.properties(train.start(false)); case ACTION_STOP: return train.stopNow(); case ACTION_TIMES: @@ -177,10 +178,6 @@ public class Train extends BaseClass implements Comparable { return properties(); } - public boolean automatic() { - return false; - } - private Fieldset blockHistory() { Fieldset fieldset = new Fieldset(t("Last blocks")).id("props-history"); Tag list = new Tag("ol"); @@ -355,7 +352,7 @@ public class Train extends BaseClass implements Comparable { } public Block destination(){ - return null; // TODO + return destination; } public String destinationTag() { @@ -404,14 +401,20 @@ public class Train extends BaseClass implements Comparable { while (!trace.isEmpty()) trace.removeFirst().free(); } - public void endRoute() { + public void endRoute(Block newBlock, Direction newDirection) { setSpeed(0); if (isSet(brakeProcessor)) brakeProcessor.end(); - brakeProcessor = null; + set(newBlock); + if (newBlock == destination) { + destination = null; + quitAutopilot(); + } + heading(newDirection); route = null; + brakeProcessor = null; + if (autopilot) start(false); } - private Tag faster(int steps) { setSpeed(speed+steps); return properties(); @@ -665,9 +668,12 @@ public class Train extends BaseClass implements Comparable { return super.properties(preForm, formInputs, postForm,errors); } - public Object quitAutopilot() { - // TODO Auto-generated method stub - return "not implemented"; + public String quitAutopilot() { + if (autopilot) { + autopilot = false; + return null; + } + return t("Autopilot already was disabled!"); } @Override @@ -764,21 +770,31 @@ public class Train extends BaseClass implements Comparable { } } + private void set(BrakeProcessor bp) { + LOG.debug("{}.set({})",this,bp); + brakeProcessor = bp; + } + + private void set(PathFinder pf) { + LOG.debug("{}.set({})",this,pf); + pathFinder = pf; + } + private Object setDestination(HashMap params) { String dest = params.get(DESTINATION); - if (isNull(dest)) return t("No destination supplied!"); + if (isNull(dest)) return properties(t("No destination supplied!")); if (dest.isEmpty()) { destination = null; return properties(); } Tile tile = plan.get(new Id(dest), true); - if (isNull(tile)) return t("Tile {} not known!",dest); + if (isNull(tile)) return properties(t("Tile {} not known!",dest)); if (tile instanceof Block) { destination = (Block) tile; - start(); + start(false); return t("{} now heading for {}",this,destination); } - return t("{} is not a block!",tile); + return properties(t("{} is not a block!",tile)); } public Object setFunction(int num, boolean active) { @@ -865,9 +881,10 @@ public class Train extends BaseClass implements Comparable { } - public String start() { + public String start(boolean autopilot) { + this.autopilot |= autopilot; if (isSet(pathFinder)) return t("Pathfinder already active for {}!",this); - pathFinder = new PathFinder(this,currentBlock,direction) { + PathFinder pathFinder = new PathFinder(this,currentBlock,direction) { @Override public void aborted() { @@ -889,23 +906,17 @@ public class Train extends BaseClass implements Comparable { public void prepared(Route newRoute) { LOG.debug("Prepared route {} for {}",newRoute,Train.this); route = newRoute; - pathFinder = null; + set((PathFinder)null); route.start(Train.this); } - - - }.start(); + }; + set(pathFinder); return null; } public static void startAll() { LOG.debug("Train.startAll()"); - for (Train train : BaseClass.listElements(Train.class)) LOG.info(train.startAutopilot()); - } - - private String startAutopilot() { - // TODO Auto-generated method stub - return null; + for (Train train : BaseClass.listElements(Train.class)) LOG.info(train.start(true)); } public void startBrake() { @@ -917,19 +928,20 @@ public class Train extends BaseClass implements Comparable { LOG.debug("{} already is braking."); return; } - brakeProcessor = new BrakeProcessor(this).start(); + set(new BrakeProcessor(this)); } public Window stopNow() { setSpeed(0); if (isSet(pathFinder)) { pathFinder.abort(); - pathFinder = null; + set((PathFinder)null); } if (isSet(route)) { route.reset(); route = null; } + quitAutopilot(); return properties(); } @@ -1021,7 +1033,7 @@ public class Train extends BaseClass implements Comparable { } public boolean usesAutopilot() { - return false; // TODO + return autopilot ; } public boolean isStoppable() { diff --git a/src/main/java/de/srsoftware/web4rail/threads/BrakeProcessor.java b/src/main/java/de/srsoftware/web4rail/threads/BrakeProcessor.java index 7c3f2d4..51e65a9 100644 --- a/src/main/java/de/srsoftware/web4rail/threads/BrakeProcessor.java +++ b/src/main/java/de/srsoftware/web4rail/threads/BrakeProcessor.java @@ -28,6 +28,9 @@ public class BrakeProcessor extends BaseClass implements Runnable { public BrakeProcessor(Train train) { this.train = train; + Thread thread = new Thread(this); + thread.setName(getClass().getSimpleName()); + thread.start(); } public void end() { @@ -88,12 +91,4 @@ public class BrakeProcessor extends BaseClass implements Runnable { } LOG.debug("{} reached final speed.", train); } - - public BrakeProcessor start() { - Thread thread = new Thread(this); - thread.setName(getClass().getSimpleName()); - thread.start(); - return this; - } - } diff --git a/src/main/java/de/srsoftware/web4rail/threads/PathFinder.java b/src/main/java/de/srsoftware/web4rail/threads/PathFinder.java index 7ecf634..31f73ef 100644 --- a/src/main/java/de/srsoftware/web4rail/threads/PathFinder.java +++ b/src/main/java/de/srsoftware/web4rail/threads/PathFinder.java @@ -30,6 +30,10 @@ public abstract class PathFinder extends BaseClass implements Runnable{ this.train = train; this.startBlock = start; this.direction = direction; + + Thread thread = new Thread(this); + thread.setName("Pathfinder("+train+")"); + thread.start(); } public void abort() { @@ -178,11 +182,4 @@ public abstract class PathFinder extends BaseClass implements Runnable{ public abstract void locked(Route r); public abstract void found(Route r); public abstract void prepared(Route r); - - public PathFinder start() { - Thread thread = new Thread(this); - thread.setName("Pathfinder("+train+")"); - thread.start(); - return this; - } } From 629b30c0780fc72ba61b9fa1f7bb3493d05a99d7 Mon Sep 17 00:00:00 2001 From: Stephan Richter Date: Fri, 12 Mar 2021 17:10:15 +0100 Subject: [PATCH 10/26] fixed bug in BrakeProcessor --- pom.xml | 2 +- .../java/de/srsoftware/web4rail/moving/Train.java | 11 +++++++---- .../srsoftware/web4rail/threads/BrakeProcessor.java | 7 ++++--- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/pom.xml b/pom.xml index 56f0dad..af2857a 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 de.srsoftware web4rail - 1.3.58 + 1.3.59 Web4Rail jar Java Model Railway Control diff --git a/src/main/java/de/srsoftware/web4rail/moving/Train.java b/src/main/java/de/srsoftware/web4rail/moving/Train.java index 6c8046e..0f9dba8 100644 --- a/src/main/java/de/srsoftware/web4rail/moving/Train.java +++ b/src/main/java/de/srsoftware/web4rail/moving/Train.java @@ -402,8 +402,9 @@ public class Train extends BaseClass implements Comparable { } public void endRoute(Block newBlock, Direction newDirection) { - setSpeed(0); - if (isSet(brakeProcessor)) brakeProcessor.end(); + if (isSet(brakeProcessor)) { + brakeProcessor.end(); + } else setSpeed(0); set(newBlock); if (newBlock == destination) { destination = null; @@ -708,8 +709,9 @@ public class Train extends BaseClass implements Comparable { } public void reserveNext() { - // TODO + LOG.debug("{}.reserveNext()",this); } + /** * This turns the train as if it went through a loop. Example: * before: CabCar→ MiddleCar→ Loco→ @@ -882,7 +884,8 @@ public class Train extends BaseClass implements Comparable { public String start(boolean autopilot) { - this.autopilot |= autopilot; + this.autopilot |= autopilot; + if (isSet(route)) return t("{} already on {}!",this,route); if (isSet(pathFinder)) return t("Pathfinder already active for {}!",this); PathFinder pathFinder = new PathFinder(this,currentBlock,direction) { diff --git a/src/main/java/de/srsoftware/web4rail/threads/BrakeProcessor.java b/src/main/java/de/srsoftware/web4rail/threads/BrakeProcessor.java index 51e65a9..9698535 100644 --- a/src/main/java/de/srsoftware/web4rail/threads/BrakeProcessor.java +++ b/src/main/java/de/srsoftware/web4rail/threads/BrakeProcessor.java @@ -36,16 +36,17 @@ public class BrakeProcessor extends BaseClass implements Runnable { public void end() { state = State.ENDED; measuredDistance += train.speed * (BaseClass.timestamp() - lastTime); + train.setSpeed(0); Route route = train.route(); if (isNull(route)) return; LOG.debug("old brake time: {}, measured distance: {}", brakeTime, measuredDistance); int step = brakeTime; for (int i = 0; i < 15; i++) { long calculatedDistance = calculate(brakeTime, startSpeed); - if (measuredDistance > calculatedDistance) brakeTime += brakeTime/2; + step /= 2; + if (step < 1) step = 1; + if (measuredDistance > calculatedDistance) brakeTime += step; if (measuredDistance < calculatedDistance) { - step /= 2; - if (step < 1) step = 1; brakeTime -= step; } LOG.debug("new brake time: {}, calculated distance: {}", brakeTime, calculatedDistance); From 8a9aada83eeea0c5cc868fc647929177db3a455e Mon Sep 17 00:00:00 2001 From: Stephan Richter Date: Fri, 12 Mar 2021 17:19:42 +0100 Subject: [PATCH 11/26] tunin BrakeProcessor --- pom.xml | 2 +- resources/logback.xml | 3 ++- src/main/java/de/srsoftware/web4rail/moving/Train.java | 2 +- .../de/srsoftware/web4rail/threads/BrakeProcessor.java | 9 ++++++--- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index af2857a..d598643 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 de.srsoftware web4rail - 1.3.59 + 1.3.60 Web4Rail jar Java Model Railway Control diff --git a/resources/logback.xml b/resources/logback.xml index bf19776..ae16a2f 100644 --- a/resources/logback.xml +++ b/resources/logback.xml @@ -29,7 +29,7 @@ - + @@ -37,5 +37,6 @@ + diff --git a/src/main/java/de/srsoftware/web4rail/moving/Train.java b/src/main/java/de/srsoftware/web4rail/moving/Train.java index 0f9dba8..99d839c 100644 --- a/src/main/java/de/srsoftware/web4rail/moving/Train.java +++ b/src/main/java/de/srsoftware/web4rail/moving/Train.java @@ -903,12 +903,12 @@ public class Train extends BaseClass implements Comparable { @Override public void locked(Route newRoute) { LOG.debug("Locked route {} for {}",newRoute,Train.this); + route = newRoute; } @Override public void prepared(Route newRoute) { LOG.debug("Prepared route {} for {}",newRoute,Train.this); - route = newRoute; set((PathFinder)null); route.start(Train.this); } diff --git a/src/main/java/de/srsoftware/web4rail/threads/BrakeProcessor.java b/src/main/java/de/srsoftware/web4rail/threads/BrakeProcessor.java index 9698535..43d721e 100644 --- a/src/main/java/de/srsoftware/web4rail/threads/BrakeProcessor.java +++ b/src/main/java/de/srsoftware/web4rail/threads/BrakeProcessor.java @@ -43,10 +43,13 @@ public class BrakeProcessor extends BaseClass implements Runnable { int step = brakeTime; for (int i = 0; i < 15; i++) { long calculatedDistance = calculate(brakeTime, startSpeed); - step /= 2; - if (step < 1) step = 1; - if (measuredDistance > calculatedDistance) brakeTime += step; + if (measuredDistance > calculatedDistance) { + brakeTime += brakeTime; + step = brakeTime/3; + } if (measuredDistance < calculatedDistance) { + step /= 2; + if (step < 1) step = 1; brakeTime -= step; } LOG.debug("new brake time: {}, calculated distance: {}", brakeTime, calculatedDistance); From 5b2bf1ddc037970f0b7ce80c7283c9aaac64128c Mon Sep 17 00:00:00 2001 From: Stephan Richter Date: Sat, 13 Mar 2021 13:12:37 +0100 Subject: [PATCH 12/26] improved javascript --- pom.xml | 2 +- resources/js/plan.js | 2 +- .../java/de/srsoftware/web4rail/Plan.java | 31 +++++++++++++------ 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/pom.xml b/pom.xml index d598643..a43e146 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 de.srsoftware web4rail - 1.3.60 + 1.3.61 Web4Rail jar Java Model Railway Control diff --git a/resources/js/plan.js b/resources/js/plan.js index a0fd926..b4a2737 100644 --- a/resources/js/plan.js +++ b/resources/js/plan.js @@ -48,7 +48,6 @@ function arrangeTabs(){ index = i; target = this; } - //if (fs.id == lastTab) target = this; $(this).appendTo(tabs).click(fs.id,clickLegend); if (id > 0) { $(fs).hide(); @@ -71,6 +70,7 @@ function clickLegend(ev){ $(ev.target).addClass('front'); $('.window > fieldset').hide(); $('#'+lastTab).show(); + $('#'+lastTab+" input:not([type=hidden])").first().focus().select(); if (!('no-update' in ev)) remember(lastTab); } diff --git a/src/main/java/de/srsoftware/web4rail/Plan.java b/src/main/java/de/srsoftware/web4rail/Plan.java index dc43bef..8eef4e9 100644 --- a/src/main/java/de/srsoftware/web4rail/Plan.java +++ b/src/main/java/de/srsoftware/web4rail/Plan.java @@ -676,19 +676,27 @@ public class Plan extends BaseClass{ } public Window properties(HashMap params) { + if (params.containsKey(ID)) { Tile tile = get(Id.from(params), true); if (isSet(tile)) return tile.properties(); - } + } - Window win = new Window("plan-properties", t("Properties of {}",t("Plan"))); + return properties(); + } + + @Override + protected Window properties(List
preForm, FormInput formInputs, List
postForm, String... errorMessages) { + formInputs.add(null, new Input(REALM,REALM_PLAN)); + formInputs.add(null, new Input(ACTION,ACTION_UPDATE)); + formInputs.add(t("Length unit"),new Input(LENGTH_UNIT, lengthUnit)); + formInputs.add(t("Speed unit"),new Input(SPEED_UNIT, speedUnit)); + formInputs.add(t("Lower speed limit"),new Input(FINAL_SPEED, BrakeProcessor.defaultEndSpeed).attr("title", t("Final speed after breaking, before halting"))); + formInputs.add(t("Free tiles behind train"),new Checkbox(FREE_BEHIND_TRAIN, t("If checked, tiles behind the train are freed according to the length of the train and the tiles. If it is unchecked, tiles will not get free before route is finished."), Route.freeBehindTrain)); - editableProperties().addTo(win); - relayProperties().addTo(win); - routeProperties().addTo(win); - - - return win; + postForm.add(relayProperties()); + postForm.add(routeProperties()); + return super.properties(preForm, formInputs, postForm, errorMessages); } /** @@ -790,7 +798,7 @@ public class Plan extends BaseClass{ } - private Tag routeProperties() { + private Fieldset routeProperties() { Fieldset fieldset = new Fieldset(t("Routes")); Table table = new Table(); table.addHead(t("Name"),t("Start"),t("End"),t("Actions")); @@ -969,6 +977,11 @@ public class Plan extends BaseClass{ new Div(ACTION_PROPS).clazz(REALM_CAR).content(t("Manage cars")).addTo(tiles); return tiles.addTo(tileMenu); } + + @Override + public String toString() { + return name; + } /** * updates a tile From 9939d8d63030ee7b4edd5c16f62bce515c2bf519 Mon Sep 17 00:00:00 2001 From: Stephan Richter Date: Sat, 13 Mar 2021 13:19:49 +0100 Subject: [PATCH 13/26] resetting route planner --- pom.xml | 2 +- .../java/de/srsoftware/web4rail/Plan.java | 11 +- .../java/de/srsoftware/web4rail/Route.java | 12 +- .../de/srsoftware/web4rail/moving/Train.java | 86 +------- .../web4rail/threads/BrakeProcessor.java | 98 ---------- .../web4rail/threads/PathFinder.java | 185 ------------------ .../de/srsoftware/web4rail/tiles/Tile.java | 15 +- 7 files changed, 20 insertions(+), 389 deletions(-) delete mode 100644 src/main/java/de/srsoftware/web4rail/threads/BrakeProcessor.java delete mode 100644 src/main/java/de/srsoftware/web4rail/threads/PathFinder.java diff --git a/pom.xml b/pom.xml index a43e146..5af24f3 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 de.srsoftware web4rail - 1.3.61 + 1.3.62 Web4Rail jar Java Model Railway Control diff --git a/src/main/java/de/srsoftware/web4rail/Plan.java b/src/main/java/de/srsoftware/web4rail/Plan.java index 8eef4e9..657725c 100644 --- a/src/main/java/de/srsoftware/web4rail/Plan.java +++ b/src/main/java/de/srsoftware/web4rail/Plan.java @@ -36,7 +36,6 @@ import de.srsoftware.web4rail.tags.Input; import de.srsoftware.web4rail.tags.Label; import de.srsoftware.web4rail.tags.Table; import de.srsoftware.web4rail.tags.Window; -import de.srsoftware.web4rail.threads.BrakeProcessor; import de.srsoftware.web4rail.threads.ControlUnit; import de.srsoftware.web4rail.tiles.Block; import de.srsoftware.web4rail.tiles.BlockContact; @@ -357,7 +356,7 @@ public class Plan extends BaseClass{ new Input(ACTION,ACTION_UPDATE).hideIn(form); new Input(LENGTH_UNIT, lengthUnit).addTo(new Label(t("Length unit")+COL)).addTo(form); new Input(SPEED_UNIT, speedUnit).addTo(new Label(t("Speed unit")+COL)).addTo(form); - new Input(FINAL_SPEED, BrakeProcessor.defaultEndSpeed).addTo(new Label(t("Lower speed limit")+COL)).attr("title", t("Final speed after breaking, before halting")).addTo(form); // TODO + new Input(FINAL_SPEED, Train.defaultEndSpeed).addTo(new Label(t("Lower speed limit")+COL)).attr("title", t("Final speed after breaking, before halting")).addTo(form); // TODO new Checkbox(FREE_BEHIND_TRAIN, t("Free tiles behind train"), Route.freeBehindTrain).attr("title", t("If checked, tiles behind the train are freed according to the length of the train and the tiles. If it is unchecked, tiles will not get free before route is finished.")).addTo(form); new Button(t("Save"), form).addTo(form); form.addTo(fieldset); @@ -491,7 +490,7 @@ public class Plan extends BaseClass{ .forEach(jTiles::put); return new JSONObject() - .put(FINAL_SPEED, BrakeProcessor.defaultEndSpeed) + .put(FINAL_SPEED, Train.defaultEndSpeed) .put(FREE_BEHIND_TRAIN, Route.freeBehindTrain) .put(LENGTH_UNIT, lengthUnit) .put(SPEED_UNIT, speedUnit) @@ -525,7 +524,7 @@ public class Plan extends BaseClass{ if (json.has(TILE)) json.getJSONArray(TILE).forEach(object -> Tile.load(object, plan)); if (json.has(LENGTH_UNIT)) lengthUnit = json.getString(LENGTH_UNIT); if (json.has(SPEED_UNIT)) speedUnit = json.getString(SPEED_UNIT); - if (json.has(FINAL_SPEED)) BrakeProcessor.defaultEndSpeed = json.getInt(FINAL_SPEED); + if (json.has(FINAL_SPEED)) Train.defaultEndSpeed = json.getInt(FINAL_SPEED); if (json.has(FREE_BEHIND_TRAIN)) Route.freeBehindTrain = json.getBoolean(FREE_BEHIND_TRAIN); try { @@ -691,7 +690,7 @@ public class Plan extends BaseClass{ formInputs.add(null, new Input(ACTION,ACTION_UPDATE)); formInputs.add(t("Length unit"),new Input(LENGTH_UNIT, lengthUnit)); formInputs.add(t("Speed unit"),new Input(SPEED_UNIT, speedUnit)); - formInputs.add(t("Lower speed limit"),new Input(FINAL_SPEED, BrakeProcessor.defaultEndSpeed).attr("title", t("Final speed after breaking, before halting"))); + formInputs.add(t("Lower speed limit"),new Input(FINAL_SPEED, Train.defaultEndSpeed).attr("title", t("Final speed after breaking, before halting"))); formInputs.add(t("Free tiles behind train"),new Checkbox(FREE_BEHIND_TRAIN, t("If checked, tiles behind the train are freed according to the length of the train and the tiles. If it is unchecked, tiles will not get free before route is finished."), Route.freeBehindTrain)); postForm.add(relayProperties()); @@ -997,7 +996,7 @@ public class Plan extends BaseClass{ if (params.containsKey(LENGTH_UNIT)) lengthUnit = params.get(LENGTH_UNIT); if (params.containsKey(SPEED_UNIT)) speedUnit = params.get(SPEED_UNIT); - if (params.containsKey(FINAL_SPEED)) BrakeProcessor.defaultEndSpeed = Integer.parseInt(params.get(FINAL_SPEED)); + if (params.containsKey(FINAL_SPEED)) Train.defaultEndSpeed = Integer.parseInt(params.get(FINAL_SPEED)); Route.freeBehindTrain = "on".equalsIgnoreCase(params.get(FREE_BEHIND_TRAIN)); return t("Plan updated."); diff --git a/src/main/java/de/srsoftware/web4rail/Route.java b/src/main/java/de/srsoftware/web4rail/Route.java index 72dee66..bd5ffd6 100644 --- a/src/main/java/de/srsoftware/web4rail/Route.java +++ b/src/main/java/de/srsoftware/web4rail/Route.java @@ -37,7 +37,6 @@ import de.srsoftware.web4rail.tags.Fieldset; import de.srsoftware.web4rail.tags.Input; import de.srsoftware.web4rail.tags.Table; import de.srsoftware.web4rail.tags.Window; -import de.srsoftware.web4rail.threads.PathFinder; import de.srsoftware.web4rail.tiles.Block; import de.srsoftware.web4rail.tiles.BlockContact; import de.srsoftware.web4rail.tiles.Contact; @@ -88,7 +87,6 @@ public class Route extends BaseClass { public Direction endDirection; private Vector path; private Vector signals; - //private Train train; private HashMap triggeredActions = new HashMap(); private HashMap turnouts; private Block startBlock = null; @@ -417,14 +415,8 @@ public class Route extends BaseClass { } public boolean isFreeFor(Train newTrain) { - PathFinder.LOG.debug("{}.isFreeFor({})",this,newTrain); - for (int i=1; i { private static final String TRACE = "trace"; private static final String NAME = "name"; + + public static int defaultEndSpeed; private String name = null; @@ -91,11 +91,6 @@ public class Train extends BaseClass implements Comparable { public int speed = 0; private static final String SHUNTING = "shunting"; private boolean shunting = false; - - private BrakeProcessor brakeProcessor; - - private PathFinder pathFinder; - private boolean autopilot = false; public static Object action(HashMap params, Plan plan) throws IOException { @@ -402,18 +397,7 @@ public class Train extends BaseClass implements Comparable { } public void endRoute(Block newBlock, Direction newDirection) { - if (isSet(brakeProcessor)) { - brakeProcessor.end(); - } else setSpeed(0); - set(newBlock); - if (newBlock == destination) { - destination = null; - quitAutopilot(); - } - heading(newDirection); - route = null; - brakeProcessor = null; - if (autopilot) start(false); + } private Tag faster(int steps) { @@ -772,16 +756,6 @@ public class Train extends BaseClass implements Comparable { } } - private void set(BrakeProcessor bp) { - LOG.debug("{}.set({})",this,bp); - brakeProcessor = bp; - } - - private void set(PathFinder pf) { - LOG.debug("{}.set({})",this,pf); - pathFinder = pf; - } - private Object setDestination(HashMap params) { String dest = params.get(DESTINATION); if (isNull(dest)) return properties(t("No destination supplied!")); @@ -884,37 +858,7 @@ public class Train extends BaseClass implements Comparable { public String start(boolean autopilot) { - this.autopilot |= autopilot; - if (isSet(route)) return t("{} already on {}!",this,route); - if (isSet(pathFinder)) return t("Pathfinder already active for {}!",this); - PathFinder pathFinder = new PathFinder(this,currentBlock,direction) { - - @Override - public void aborted() { - plan.stream(t("Aborting route allocation...")); - LOG.debug("{} aborted",this); - } - - @Override - public void found(Route newRoute) { - LOG.debug("Found route {} for {}",newRoute,Train.this); - } - - @Override - public void locked(Route newRoute) { - LOG.debug("Locked route {} for {}",newRoute,Train.this); - route = newRoute; - } - - @Override - public void prepared(Route newRoute) { - LOG.debug("Prepared route {} for {}",newRoute,Train.this); - set((PathFinder)null); - route.start(Train.this); - } - }; - set(pathFinder); - return null; + return t("{}.start() not implemented",this); } public static void startAll() { @@ -922,28 +866,10 @@ public class Train extends BaseClass implements Comparable { for (Train train : BaseClass.listElements(Train.class)) LOG.info(train.start(true)); } - public void startBrake() { - if (isNull(route)) { - LOG.warn("{}.startBrake() called, but train ist not on a route!",this); - return; - } - if (isSet(brakeProcessor)) { - LOG.debug("{} already is braking."); - return; - } - set(new BrakeProcessor(this)); - } + public void startBrake() {} public Window stopNow() { setSpeed(0); - if (isSet(pathFinder)) { - pathFinder.abort(); - set((PathFinder)null); - } - if (isSet(route)) { - route.reset(); - route = null; - } quitAutopilot(); return properties(); } @@ -1041,8 +967,6 @@ public class Train extends BaseClass implements Comparable { public boolean isStoppable() { if (speed > 0) return true; - if (isSet(pathFinder)) return true; - if (isSet(route)) return true; return false; } } diff --git a/src/main/java/de/srsoftware/web4rail/threads/BrakeProcessor.java b/src/main/java/de/srsoftware/web4rail/threads/BrakeProcessor.java deleted file mode 100644 index 43d721e..0000000 --- a/src/main/java/de/srsoftware/web4rail/threads/BrakeProcessor.java +++ /dev/null @@ -1,98 +0,0 @@ -package de.srsoftware.web4rail.threads; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import de.srsoftware.web4rail.BaseClass; -import de.srsoftware.web4rail.Route; -import de.srsoftware.web4rail.moving.Train; - -/** - * @author Stephan Richter, SRSoftware - * - */ -public class BrakeProcessor extends BaseClass implements Runnable { - - private enum State { - IDLE, BRAKING, ABORTED, ENDED; - } - - private static final Logger LOG = LoggerFactory.getLogger(BrakeProcessor.class); - public static int defaultEndSpeed; - private Train train; - private State state = State.IDLE; - private long measuredDistance; - private long lastTime; - private Integer brakeTime; - private int startSpeed; - - public BrakeProcessor(Train train) { - this.train = train; - Thread thread = new Thread(this); - thread.setName(getClass().getSimpleName()); - thread.start(); - } - - public void end() { - state = State.ENDED; - measuredDistance += train.speed * (BaseClass.timestamp() - lastTime); - train.setSpeed(0); - Route route = train.route(); - if (isNull(route)) return; - LOG.debug("old brake time: {}, measured distance: {}", brakeTime, measuredDistance); - int step = brakeTime; - for (int i = 0; i < 15; i++) { - long calculatedDistance = calculate(brakeTime, startSpeed); - if (measuredDistance > calculatedDistance) { - brakeTime += brakeTime; - step = brakeTime/3; - } - if (measuredDistance < calculatedDistance) { - step /= 2; - if (step < 1) step = 1; - brakeTime -= step; - } - LOG.debug("new brake time: {}, calculated distance: {}", brakeTime, calculatedDistance); - } - route.brakeTime(train.brakeId(), brakeTime); - } - - private static long calculate(int brakeTime, int speed) { - long dist = 0; - while (speed > defaultEndSpeed) { - dist += speed * brakeTime; - speed -= 10; - } - return dist; - } - - @Override - public void run() { - LOG.debug("run()"); - Route route = train.route(); - if (isNull(route)) return; - brakeTime = route.brakeTime(train.brakeId()); - if (isNull(brakeTime)) brakeTime = 250; - - state = State.BRAKING; - measuredDistance = 0; - lastTime = BaseClass.timestamp(); - startSpeed = train.speed; - int targetSpeed = defaultEndSpeed; - while (state == State.BRAKING) { - sleep(brakeTime); - long newTime = BaseClass.timestamp(); - if (isNull(train.route())) state = State.ABORTED; - if (state != State.BRAKING) break; - measuredDistance += train.speed * (newTime - lastTime); - int newSpeed = train.speed - 10; - if (newSpeed < targetSpeed) { - train.setSpeed(targetSpeed); - break; - } - train.setSpeed(newSpeed); - lastTime = newTime; - } - LOG.debug("{} reached final speed.", train); - } -} diff --git a/src/main/java/de/srsoftware/web4rail/threads/PathFinder.java b/src/main/java/de/srsoftware/web4rail/threads/PathFinder.java deleted file mode 100644 index 31f73ef..0000000 --- a/src/main/java/de/srsoftware/web4rail/threads/PathFinder.java +++ /dev/null @@ -1,185 +0,0 @@ -package de.srsoftware.web4rail.threads; - -import java.util.HashSet; -import java.util.List; -import java.util.Map.Entry; -import java.util.TreeMap; -import java.util.Vector; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import de.srsoftware.web4rail.BaseClass; -import de.srsoftware.web4rail.Plan.Direction; -import de.srsoftware.web4rail.Route; -import de.srsoftware.web4rail.moving.Train; -import de.srsoftware.web4rail.tiles.Block; - -/** - * @author Stephan Richter, SRSoftware 2020-2021 - */ -public abstract class PathFinder extends BaseClass implements Runnable{ - public static final Logger LOG = LoggerFactory.getLogger(PathFinder.class); -// private Context context; - private boolean aborted = false; - private Direction direction; - private Block startBlock; - private Train train; - - public PathFinder(Train train, Block start, Direction direction) { - this.train = train; - this.startBlock = start; - this.direction = direction; - - Thread thread = new Thread(this); - thread.setName("Pathfinder("+train+")"); - thread.start(); - } - - public void abort() { - aborted = true; - aborted(); - LOG.debug("aborted {}",this); - } - - private static TreeMap> availableRoutes(Train train, Block start, Direction startDir, HashSet visitedRoutes){ - String inset = ""; - for (int i=0; i>(); - - if (isSet(startDir)) { - LOG.debug("{}Looking for {}-bound routes from {}",inset,startDir,start); - } else { - LOG.debug("{}Looking for all routes from {}",inset,start); - }//*/ - Block destination = train.destination(); - if (isSet(destination) && visitedRoutes.isEmpty()) LOG.debug("{}- Destination: {}",inset,destination); - - //Route currentRoute = context.route(); - TreeMap> availableRoutes = new TreeMap>(); - - for (Route routeCandidate : start.routes()) { - if (routeCandidate.path().firstElement() != start) continue; // Routen, die nicht vom aktuellen Block starten sind bubu - if (visitedRoutes.contains(routeCandidate)) { - LOG.debug("{}→ Candidate {} would create loop, skipping",inset,routeCandidate.shortName()); - continue; - } - Context c = new Context(train).block(start).direction(startDir); - if (!routeCandidate.allowed(c)) { - if (routeCandidate.endBlock() != destination) { // allowance may be overridden by destination - LOG.debug("{} not allowed for {}",routeCandidate,c); - continue; // Zug darf auf Grund einer nicht erfüllten Bedingung nicht auf die Route - } - LOG.debug("{} not allowed for {} – overridden by selected destination",routeCandidate,c); - } - - int priority = 0; - if (isSet(startDir) && routeCandidate.startDirection != startDir) { // Route startet entgegen der aktuellen Fahrtrichtung des Zuges - if (!train.pushPull) continue; // Zug kann nicht wenden - if (!start.turnAllowed) continue; // Wenden im Block nicht gestattet - priority -= 5; - } - //if (routeCandidate == currentRoute) priority-=10; // möglichst andere Route als zuvor wählen // TODO: den Routen einen "last-used" Zeitstempel hinzufügen, und diesen mit in die Priorisierung einbeziehen - - if (isSet(destination)) { - if (routeCandidate.endBlock() == destination) { // route goes directly to destination - LOG.debug("{}→ Candidate {} directly leads to {}",inset,routeCandidate.shortName(),destination); - priority = 1_000_000; - } else { - LOG.debug("{}- Candidate: {}",inset,routeCandidate.shortName()); - visitedRoutes.add(routeCandidate); - TreeMap> forwardRoutes = availableRoutes(train, routeCandidate.endBlock(), routeCandidate.endDirection, visitedRoutes); - visitedRoutes.remove(routeCandidate); - if (forwardRoutes.isEmpty()) continue; // the candidate does not lead to a block, from which routes to the destination exist - Entry> entry = forwardRoutes.lastEntry(); - LOG.debug("{}→ The following routes have connections to {}:",inset,destination); - for (Route rt: entry.getValue()) LOG.debug("{} - {}",inset,rt.shortName()); - priority += entry.getKey()-10; - } - } - - List routeSet = availableRoutes.get(priority); - if (isNull(routeSet)) { - routeSet = new Vector(); - availableRoutes.put(priority, routeSet); - } - routeSet.add(routeCandidate); - if (routeCandidate.endBlock() == destination) break; // direct connection to destination discovered, quit search - } - if (!availableRoutes.isEmpty()) LOG.debug("{}→ Routes from {}: {}",inset,start,availableRoutes.isEmpty()?"none":""); - for (Entry> entry : availableRoutes.entrySet()) { - LOG.debug("{} - Priority {}:",inset,entry.getKey()); - for (Route r : entry.getValue()) { - LOG.debug("{} - {}",inset,r.shortName()); - } - } - return availableRoutes; - } - - public Route chooseRoute() { - LOG.debug("PathFinder.chooseRoute()"); - HashSet visitedRoutes = new HashSet(); - TreeMap> availableRoutes = availableRoutes(train, startBlock, direction,visitedRoutes); - while (!availableRoutes.isEmpty()) { - LOG.debug("availableRoutes: {}",availableRoutes); - Entry> entry = availableRoutes.lastEntry(); - List preferredRoutes = entry.getValue(); - LOG.debug("preferredRoutes: {}",preferredRoutes); - Route selectedRoute = preferredRoutes.get(random.nextInt(preferredRoutes.size())); - if (selectedRoute.isFreeFor(train)) { - LOG.debug("Chose \"{}\" with priority {}.",selectedRoute,entry.getKey()); - return selectedRoute; - } - - LOG.debug("Selected route \"{}\" is not free for {}",selectedRoute,train); - preferredRoutes.remove(selectedRoute); - if (preferredRoutes.isEmpty()) availableRoutes.remove(availableRoutes.lastKey()); - } - return null; - } - - @Override - public void run() { - while (true) { - if (aborted) return; - Route route = chooseRoute(); - if (isSet(route)) { - if (aborted) return; - found(route); - if (route.allocateFor(train)) { - if (aborted) { - route.reset(); - return; - } - locked(route); - if (route.prepareFor(train)) { - if (aborted) { - route.reset(); - return; - } - prepared(route); - return; - } - } - } - sleep(1000); - } - } - - public abstract void aborted(); - public abstract void locked(Route r); - public abstract void found(Route r); - public abstract void prepared(Route r); -} diff --git a/src/main/java/de/srsoftware/web4rail/tiles/Tile.java b/src/main/java/de/srsoftware/web4rail/tiles/Tile.java index e92dcf7..f306a7f 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/Tile.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/Tile.java @@ -24,15 +24,14 @@ import de.srsoftware.web4rail.BaseClass; import de.srsoftware.web4rail.Connector; import de.srsoftware.web4rail.Plan; import de.srsoftware.web4rail.Plan.Direction; -import de.srsoftware.web4rail.actions.AlterDirection; import de.srsoftware.web4rail.Route; +import de.srsoftware.web4rail.actions.AlterDirection; import de.srsoftware.web4rail.moving.Train; import de.srsoftware.web4rail.tags.Checkbox; import de.srsoftware.web4rail.tags.Fieldset; import de.srsoftware.web4rail.tags.Input; import de.srsoftware.web4rail.tags.Radio; import de.srsoftware.web4rail.tags.Window; -import de.srsoftware.web4rail.threads.PathFinder; /** * Base class for all tiles @@ -82,28 +81,28 @@ public abstract class Tile extends BaseClass implements Comparable{ } public boolean canNeEnteredBy(Train newTrain) { - PathFinder.LOG.debug("{}.canNeEnteredBy({})",this,newTrain); + LOG.debug("{}.canNeEnteredBy({})",this,newTrain); if (disabled) { - PathFinder.LOG.debug("{} is disabled!",this); + LOG.debug("{} is disabled!",this); return false; } if (isNull(train)) { - PathFinder.LOG.debug("→ free"); + LOG.debug("→ free"); return true; } if (newTrain == train) { // during train.reserveNext, we may encounter, parts, that are already reserved by the respective train, but having another route. do not compare routes in that case! - PathFinder.LOG.debug("already reserved by {} → true",train); + LOG.debug("already reserved by {} → true",train); return true; } if (isSet(newTrain) && newTrain.isShunting()) { - PathFinder.LOG.debug("occupied by {}. Allowed for shunting {}",train,newTrain); + LOG.debug("occupied by {}. Allowed for shunting {}",train,newTrain); return true; } - PathFinder.LOG.debug("occupied by {} → false",train); + LOG.debug("occupied by {} → false",train); return false; } From 2647b4c43dbafd983c936d5672a464242a172d78 Mon Sep 17 00:00:00 2001 From: Stephan Richter Date: Sat, 13 Mar 2021 20:15:51 +0100 Subject: [PATCH 14/26] Started clean re-implementation of - reserving route - preparing route - locking route New implementation now properly handles stop event from user. Next things to implement: - tracing train upon contact activation, taking care of: - causing contact - train's last position - route - plan.freeBehindTrain - finish event, keeping brake processor in mind --- pom.xml | 2 +- resources/css/style.css | 4 +- resources/logback.xml | 8 +- .../de/srsoftware/web4rail/BaseClass.java | 4 + .../de/srsoftware/web4rail/Constants.java | 1 + .../java/de/srsoftware/web4rail/Route.java | 109 +++-- .../web4rail/actions/ActionList.java | 9 +- .../web4rail/actions/CoupleTrain.java | 2 +- .../web4rail/conditions/BlockFree.java | 2 +- .../web4rail/moving/Locomotive.java | 2 +- .../de/srsoftware/web4rail/moving/Train.java | 120 ++--- .../web4rail/threads/RouteManager.java | 204 ++++++++ .../de/srsoftware/web4rail/tiles/Block.java | 250 +++++++--- .../web4rail/tiles/BlockContact.java | 3 +- .../de/srsoftware/web4rail/tiles/Bridge.java | 14 +- .../de/srsoftware/web4rail/tiles/Contact.java | 8 +- .../de/srsoftware/web4rail/tiles/Tile.java | 451 ++++++++++-------- .../de/srsoftware/web4rail/tiles/Turnout.java | 2 +- .../srsoftware/web4rail/tiles/TurnoutL.java | 2 + .../srsoftware/web4rail/tiles/TurnoutR.java | 2 + 20 files changed, 795 insertions(+), 404 deletions(-) create mode 100644 src/main/java/de/srsoftware/web4rail/threads/RouteManager.java diff --git a/pom.xml b/pom.xml index 5af24f3..46fdadd 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 de.srsoftware web4rail - 1.3.62 + 1.3.65 Web4Rail jar Java Model Railway Control diff --git a/resources/css/style.css b/resources/css/style.css index 9902704..217659d 100644 --- a/resources/css/style.css +++ b/resources/css/style.css @@ -77,8 +77,8 @@ svg.Relay rect{ fill: white; } -svg.allocated polygon, -svg.allocated rect:not(.sig_a):not(.sig_b){ +svg.reserved polygon, +svg.reserved rect:not(.sig_a):not(.sig_b){ fill: yellow; } diff --git a/resources/logback.xml b/resources/logback.xml index ae16a2f..e941c08 100644 --- a/resources/logback.xml +++ b/resources/logback.xml @@ -4,7 +4,7 @@ - %d{HH:mm:ss.SSS} [%thread] %-5level %logger{5} - %msg%n + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{5}: %msg%n @@ -35,8 +35,8 @@ - - - + + + diff --git a/src/main/java/de/srsoftware/web4rail/BaseClass.java b/src/main/java/de/srsoftware/web4rail/BaseClass.java index 6672bcc..1676969 100644 --- a/src/main/java/de/srsoftware/web4rail/BaseClass.java +++ b/src/main/java/de/srsoftware/web4rail/BaseClass.java @@ -141,6 +141,10 @@ public abstract class BaseClass implements Constants{ return this; } + public void invalidate() { + setMain(null); + } + public boolean invalidated() { return isNull(main); } diff --git a/src/main/java/de/srsoftware/web4rail/Constants.java b/src/main/java/de/srsoftware/web4rail/Constants.java index 23d8501..4df1d7f 100644 --- a/src/main/java/de/srsoftware/web4rail/Constants.java +++ b/src/main/java/de/srsoftware/web4rail/Constants.java @@ -58,6 +58,7 @@ public interface Constants { public static final String DEFAULT_SPEED_UNIT = "km/h"; public static final String DEFAULT_LENGTH_UNIT = "mm"; public static final String DISABLED = "disabled"; + public static final String DIRECTION = "direction"; public static final String GITHUB_URL = "https://github.com/srsoftware-de/Web4Rail"; public static final String ID = "id"; public static final String NAME = "name"; diff --git a/src/main/java/de/srsoftware/web4rail/Route.java b/src/main/java/de/srsoftware/web4rail/Route.java index bd5ffd6..4afaa79 100644 --- a/src/main/java/de/srsoftware/web4rail/Route.java +++ b/src/main/java/de/srsoftware/web4rail/Route.java @@ -9,7 +9,6 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; -import java.util.Stack; import java.util.Vector; import org.json.JSONArray; @@ -43,7 +42,6 @@ import de.srsoftware.web4rail.tiles.Contact; import de.srsoftware.web4rail.tiles.Shadow; import de.srsoftware.web4rail.tiles.Signal; import de.srsoftware.web4rail.tiles.Tile; -import de.srsoftware.web4rail.tiles.Tile.Status; import de.srsoftware.web4rail.tiles.Turnout; /** * A route is a vector of tiles that leads from one block to another. @@ -82,6 +80,7 @@ public class Route extends BaseClass { private HashMap brakeTimes = new HashMap(); private ConditionList conditions; private Vector contacts; + private Context context; private boolean disabled = false; private Block endBlock = null; public Direction endDirection; @@ -195,10 +194,6 @@ public class Route extends BaseClass { turnouts.put(t, s); } - public boolean allocateFor(Train newTrain) { - return pathState(newTrain,Tile.Status.ALLOCATED); - } - /** * checks, whether the route may be used in a given context * @param context @@ -316,17 +311,18 @@ public class Route extends BaseClass { * Kontakt der Route aktivieren * @param contact * @param trainHead + * @return */ - public void contact(Context context) { - Contact contact = context.contact(); - if (triggeredContacts.contains(contact)) return; // don't trigger contact a second time + public Context contact(Contact contact) { + context.contact(contact); + if (triggeredContacts.contains(contact)) return context; // don't trigger contact a second time triggeredContacts.add(contact); LOG.debug("{} on {} activated {}.",context.train(),this,contact); ActionList actions = triggeredActions.get(contact.trigger()); LOG.debug("Contact has id {} / trigger {} and is assigned with {}",contact.id(),contact.trigger(),isNull(actions)?t("nothing"):actions); - if (isNull(actions)) return; - //Context context = new Context(this).train(train).contact(contact); - actions.fire(context.route(this),"Route.Contact("+contact.addr()+")"); + if (isNull(actions)) return context; + actions.fire(context,"Route.Contact("+contact.addr()+")"); + return context; } public Vector contacts() { @@ -377,17 +373,20 @@ public class Route extends BaseClass { public void finish(Train train) { LOG.debug("{}.finish()",this); train.endRoute(endBlock,endDirection); - train = null; free(); + train = null; } private void free() { - for (Tile tile : path) { - Train train = tile.train(); - if (isSet(train) && train.onTrace(tile)) { - tile.setState(Status.OCCUPIED, train); - } else tile.free(); + context.invalidate(); + Train train = context.train(); + for (int i=path.size(); i>0; i--) { + Tile tile = path.get(i-1); + if (isSet(train) && !train.onTrace(tile)) { + tile.free(train); + } } + context = null; } private String generateName() { @@ -414,9 +413,13 @@ public class Route extends BaseClass { return disabled; } - public boolean isFreeFor(Train newTrain) { - LOG.debug("{}.isFreeFor({})",this,newTrain); - return false; + public boolean isFreeFor(Context train) { + LOG.debug("{}.isFreeFor({})",this,train); + if (isNull(train.train())) return false; + for (Tile tile : path) { + if (!tile.isFreeFor(train)) return false; + } + return true; } /** @@ -647,29 +650,24 @@ public class Route extends BaseClass { return result; } - private boolean pathState(Train newTrain, Status newState) { - Stack visited = new Stack<>(); - for (Tile t : path) { - if (t.setState(newState,newTrain)) { - visited.push(t); - } else { - while (!visited.isEmpty()) visited.pop().free(); + public boolean prepareAndLock() { + LOG.debug("{}.prepareAndLock()",this); + Train train = context.train(); + ActionList setupActions = triggeredActions.get(ROUTE_SETUP); + if (isSet(setupActions) && !setupActions.fire(context.route(this),this+".prepare()")) { + LOG.debug("Was not able to prepare route for {}.",train); + return false; + } + + for (Tile tile : path) { + if (context.invalidated() || !tile.lockFor(context)) { + LOG.debug("Was not able to allocate route for {}.",context); return false; } } return true; } - public boolean prepareFor(Train newTrain) { -// if (state == State.PREPARED || state == State.STARTED) return true; - LOG.debug("{}.prepare()",this); - ActionList setupActions = triggeredActions.get(ROUTE_SETUP); - if (isSet(setupActions) && !setupActions.fire(new Context(newTrain).route(this),this+".prepare()")) return false; -// state = State.PREPARED; - pathState(newTrain,Tile.Status.LOCKED); - return true; - } - private Tag previewScript() { Tag script = new Tag("script").attr("type", "text/javascript"); for (Tile tile : path) { @@ -736,11 +734,25 @@ public class Route extends BaseClass { super.removeChild(child); } + public boolean reserveFor(Context newContext) { + LOG.debug("{}.reserverFor({})",this,newContext); + if (isSet(context)) return false; // route already has context! + context = newContext; + for (Tile tile : path) { + if (newContext.invalidated() || !tile.reserveFor(newContext)) { + LOG.debug("Was not able to allocate route for {}.",newContext); + return false; + } + } + return true; + } + public boolean reset() { LOG.debug("{}.reset()",this); setSignals(Signal.RED); + Train train = context.train(); free(); -// train = null; + train.drop(this); return true; } @@ -783,21 +795,22 @@ public class Route extends BaseClass { return this; } - public boolean start(Train newTrain) { -// if (state == State.STARTED) return true; + public boolean start() { LOG.debug("{}.start()",this); - if (isNull(newTrain)) return false; // can't set route's train to null -/* if (isSet(train)) { - if (newTrain != train) return false; // can't alter route's train - } else train = */newTrain.setRoute(this); // set new train + if (isNull(context) || context.invalidated()) return false; + + Train train = context.train(); + if (isNull(train)) return false; // can't set route's train to null + train.setRoute(this); // set new train ActionList startActions = triggeredActions.get(ROUTE_START); if (isSet(startActions)) { - Context context = new Context(newTrain).route(this); - if (!startActions.fire(context,this+".start("+newTrain.name()+")")) return false; // start actions failed + context.route(this); + String cause = this+".start("+train.name()+")"; + if (!startActions.fire(context,cause)) return false; // start actions failed } -// state = State.STARTED; + triggeredContacts.clear(); return true; } diff --git a/src/main/java/de/srsoftware/web4rail/actions/ActionList.java b/src/main/java/de/srsoftware/web4rail/actions/ActionList.java index 6fe6ffa..f73cefa 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/ActionList.java +++ b/src/main/java/de/srsoftware/web4rail/actions/ActionList.java @@ -81,12 +81,11 @@ public class ActionList extends Action implements Iterable{ } public boolean fire(Context context,Object cause) { - if (context.invalidated()) { - LOG.debug("Context has been invalidated, aborting {}",this); - return false; - } - for (Action action : actions) { + if (context.invalidated()) { + LOG.debug("Context has been invalidated, aborting {}",this); + return false; + } LOG.debug("firing \"{}\"",action); if (!action.fire(context,cause)) { LOG.warn("{} failed",action); diff --git a/src/main/java/de/srsoftware/web4rail/actions/CoupleTrain.java b/src/main/java/de/srsoftware/web4rail/actions/CoupleTrain.java index c2fb782..a1e4b1a 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/CoupleTrain.java +++ b/src/main/java/de/srsoftware/web4rail/actions/CoupleTrain.java @@ -29,7 +29,7 @@ public class CoupleTrain extends Action { if (isNull(train)) return false; Block block = train.currentBlock(); if (isNull(block)) return false; - Train parkingTrain = block.parkedTrain(last); + Train parkingTrain = block.lastTrain(); if (isNull(parkingTrain)) return false; train.coupleWith(parkingTrain,swap); return true; diff --git a/src/main/java/de/srsoftware/web4rail/conditions/BlockFree.java b/src/main/java/de/srsoftware/web4rail/conditions/BlockFree.java index b4b8659..8b6152b 100644 --- a/src/main/java/de/srsoftware/web4rail/conditions/BlockFree.java +++ b/src/main/java/de/srsoftware/web4rail/conditions/BlockFree.java @@ -25,7 +25,7 @@ public class BlockFree extends Condition { @Override public boolean fulfilledBy(Context context) { - return block.canNeEnteredBy(null) != inverted; + return block.isFreeFor(null) != inverted; } @Override diff --git a/src/main/java/de/srsoftware/web4rail/moving/Locomotive.java b/src/main/java/de/srsoftware/web4rail/moving/Locomotive.java index 0d392d5..f96e9fd 100644 --- a/src/main/java/de/srsoftware/web4rail/moving/Locomotive.java +++ b/src/main/java/de/srsoftware/web4rail/moving/Locomotive.java @@ -153,7 +153,7 @@ public class Locomotive extends Car implements Constants,Device{ if (isSet(train)) { Block currentBlock = train.currentBlock(); if (isSet(currentBlock)) { - if (isNull(train.route())) { + if (!train.isStoppable()) { params.put(ACTION, ACTION_START); new Button(t("depart"),params).addTo(direction); } diff --git a/src/main/java/de/srsoftware/web4rail/moving/Train.java b/src/main/java/de/srsoftware/web4rail/moving/Train.java index 9a37205..03c4393 100644 --- a/src/main/java/de/srsoftware/web4rail/moving/Train.java +++ b/src/main/java/de/srsoftware/web4rail/moving/Train.java @@ -36,11 +36,11 @@ import de.srsoftware.web4rail.tags.Label; import de.srsoftware.web4rail.tags.Select; import de.srsoftware.web4rail.tags.Table; import de.srsoftware.web4rail.tags.Window; +import de.srsoftware.web4rail.threads.RouteManager; import de.srsoftware.web4rail.tiles.Block; import de.srsoftware.web4rail.tiles.BlockContact; import de.srsoftware.web4rail.tiles.Contact; import de.srsoftware.web4rail.tiles.Tile; -import de.srsoftware.web4rail.tiles.Tile.Status; /** * @author Stephan Richter, SRSoftware 2020-2021 * @@ -61,7 +61,6 @@ public class Train extends BaseClass implements Comparable { private static final String ROUTE = "route"; private Route route; - private static final String DIRECTION = "direction"; private Direction direction; private static final String PUSH_PULL = "pushPull"; @@ -91,7 +90,7 @@ public class Train extends BaseClass implements Comparable { public int speed = 0; private static final String SHUNTING = "shunting"; private boolean shunting = false; - private boolean autopilot = false; + private RouteManager routeManager = null; public static Object action(HashMap params, Plan plan) throws IOException { String action = params.get(ACTION); @@ -270,7 +269,7 @@ public class Train extends BaseClass implements Comparable { if (isSet(currentBlock)) { Tag ul = new Tag("ul"); if (isSet(currentBlock.train()) && currentBlock.train() != this) currentBlock.train().link().addTo(new Tag("li")).addTo(ul); - for (Train tr : currentBlock.parkedTrains()) { + for (Train tr : currentBlock.trains()) { if (tr == this) continue; Tag li = new Tag("li").addTo(ul); tr.link().addTo(li); @@ -299,12 +298,11 @@ public class Train extends BaseClass implements Comparable { return properties(); } - public void contact(Contact contact) { - if (isSet(route)) { - Route lastRoute = route; // route field might be set to null during route.contact(...)! - route.contact(new Context(contact).train(this)); - traceFrom(contact,lastRoute); - } + public Context contact(Contact contact) { + if (isNull(route)) return new Context(contact).train(this); + Context context = route.contact(contact); + traceFrom(context); + return context; } @@ -357,12 +355,11 @@ public class Train extends BaseClass implements Comparable { return null; } - - public String directedName() { + public String directedName(Direction dir) { String result = name(); String mark = ""; //isSet(autopilot) ? "ⓐ" : ""; - if (isNull(direction)) return result; - switch (direction) { + if (isNull(dir)) return result; + switch (dir) { case NORTH: case WEST: return '←'+mark+result; @@ -370,7 +367,7 @@ public class Train extends BaseClass implements Comparable { case EAST: return result+mark+'→'; } - return result; + return mark+result; } public Direction direction() { @@ -392,8 +389,16 @@ public class Train extends BaseClass implements Comparable { return properties(); } + public boolean drop(Route oldRoute) { + if (isNull(route)) return true; + if (route != oldRoute) return false; + route = null; + return true; + } + + public void dropTrace() { - while (!trace.isEmpty()) trace.removeFirst().free(); + while (!trace.isEmpty()) trace.removeFirst().free(this); } public void endRoute(Block newBlock, Direction newDirection) { @@ -438,7 +443,13 @@ public class Train extends BaseClass implements Comparable { return shunting; } - + public boolean isStoppable() { + if (speed > 0) return true; + if (isSet(routeManager) && routeManager.isActive()) return true; + if (isSet(route)) return true; + return false; + } + public JSONObject json() { JSONObject json = super.json(); json.put(PUSH_PULL, pushPull); @@ -500,11 +511,11 @@ public class Train extends BaseClass implements Comparable { if (json.has(TAGS)) json.getJSONArray(TAGS ).forEach(elem -> { tags.add(elem.toString()); }); if (json.has(TRACE)) json.getJSONArray(TRACE).forEach(elem -> { Tile tile = plan.get(new Id(elem.toString()), false); - if (tile.setState(Status.OCCUPIED,this)) trace.add(tile); + if (tile.setTrain(this)) trace.add(tile); }); if (json.has(BLOCK)) {// do not move this up! during set, other fields will be referenced! currentBlock = (Block) plan.get(new Id(json.getString(BLOCK)), false); - currentBlock.setState(Status.OCCUPIED,this); + currentBlock.add(this, direction); } if (json.has(LOCOS)) { // for downward compatibility for (Object id : json.getJSONArray(LOCOS)) add(BaseClass.get(new Id(""+id))); @@ -654,10 +665,7 @@ public class Train extends BaseClass implements Comparable { } public String quitAutopilot() { - if (autopilot) { - autopilot = false; - return null; - } + if (isSet(routeManager)) routeManager.quit(); return t("Autopilot already was disabled!"); } @@ -682,6 +690,7 @@ public class Train extends BaseClass implements Comparable { //if (child == nextRoute) nextRoute = null; // TODO if (child == currentBlock) currentBlock = null; if (child == destination) destination = null; + if (child == routeManager) routeManager = null; cars.remove(child); trace.remove(child); super.removeChild(child); @@ -747,10 +756,10 @@ public class Train extends BaseClass implements Comparable { public void set(Block newBlock) { LOG.debug("{}.set({})",this,newBlock); - if (isSet(currentBlock)) currentBlock.free(); + if (isSet(currentBlock)) currentBlock.free(this); currentBlock = newBlock; if (isSet(currentBlock)) { - currentBlock.setState(Status.OCCUPIED,this); + currentBlock.setTrain(this); lastBlocks.add(newBlock); if (lastBlocks.size()>32) lastBlocks.remove(0); } @@ -811,18 +820,6 @@ public class Train extends BaseClass implements Comparable { cars.stream().filter(c -> c instanceof Locomotive).forEach(car -> ((Locomotive)car).setSpeed(speed)); plan.stream(t("Set {} to {} {}",this,speed,speedUnit)); } - - public void setTrace(LinkedList newTrace) { - LOG.debug("{}.setTrace({})",this,newTrace); - LOG.debug("old trace: {}",trace); - - trace.removeAll(newTrace); - for (Tile tile : trace) tile.free(); - trace = newTrace; - for (Tile tile : trace) tile.setState(Status.OCCUPIED,this); - - LOG.debug("new trace of {}: {}",this,trace); - } private Tag slower(int steps) { setSpeed(speed-steps); @@ -850,15 +847,18 @@ public class Train extends BaseClass implements Comparable { if (remaining.cars.isEmpty()) return false; remaining.direction = this.direction; this.name = null; - currentBlock.add(remaining); + currentBlock.add(remaining,direction); remaining.currentBlock = currentBlock; plan.place(currentBlock); return true; } - public String start(boolean autopilot) { - return t("{}.start() not implemented",this); + public String start(boolean auto) { + if (isNull(routeManager)) routeManager = new RouteManager(this); + routeManager.setAuto(auto); + plan.stream(t("Started {}",this)); + return null; } public static void startAll() { @@ -871,6 +871,9 @@ public class Train extends BaseClass implements Comparable { public Window stopNow() { setSpeed(0); quitAutopilot(); + if (isSet(route)) { + route.reset(); + } return properties(); } @@ -900,31 +903,9 @@ public class Train extends BaseClass implements Comparable { return name(); } - public void traceFrom(Tile newHead,Route route) { - LOG.debug("{}.traceTrainFrom({})",this,newHead); - if (isNull(route)) return; - if (newHead instanceof BlockContact) newHead = (Tile) ((BlockContact)newHead).parent(); - - Tile traceHead = traceHead(); - Integer remainingLength = null; - LinkedList newTrace = new LinkedList(); - Vector path = route.path(); - for (int i=path.size(); i>0; i--) { // pfad rückwärts ablaufen - Tile tile = path.elementAt(i-1); - if (isNull(remainingLength)) { - if (tile == newHead) traceHead = newHead; // wenn wir zuerst newHead finden: newHead als neuen traceHead übernehmen - if (tile == traceHead) remainingLength = length(); // sobald wir auf den traceHead stoßen: Suche beenden - } - if (isSet(remainingLength)) { - if (remainingLength>=0) { - newTrace.add(tile); - remainingLength -= tile.length(); - } else if (Route.freeBehindTrain) { - tile.free(); - } else break; - } - } - setTrace(newTrace); + public void traceFrom(Context context) { + // TOSO: neu implementieren! + // Beachten: Route aus Context, plan.freeBehindTrain } public Tile traceHead() { @@ -960,13 +941,8 @@ public class Train extends BaseClass implements Comparable { } return properties(); } - - public boolean usesAutopilot() { - return autopilot ; - } - public boolean isStoppable() { - if (speed > 0) return true; - return false; + public boolean usesAutopilot() { + return isSet(routeManager) && routeManager.autoEnabled(); } } diff --git a/src/main/java/de/srsoftware/web4rail/threads/RouteManager.java b/src/main/java/de/srsoftware/web4rail/threads/RouteManager.java new file mode 100644 index 0000000..e12a1a7 --- /dev/null +++ b/src/main/java/de/srsoftware/web4rail/threads/RouteManager.java @@ -0,0 +1,204 @@ +package de.srsoftware.web4rail.threads; + +import java.util.HashSet; +import java.util.List; +import java.util.Map.Entry; +import java.util.TreeMap; +import java.util.Vector; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import de.srsoftware.web4rail.BaseClass; +import de.srsoftware.web4rail.Plan.Direction; +import de.srsoftware.web4rail.Route; +import de.srsoftware.web4rail.moving.Train; +import de.srsoftware.web4rail.tiles.Block; + +public class RouteManager extends BaseClass implements Runnable { + + enum State { + ENDED,IDLE,STARTED; + } + private static final Logger LOG = LoggerFactory.getLogger(RouteManager.class); + private static final int DEFAULT_PAUSE_TIME = 250; // ms + private State state = State.IDLE; + private boolean autopilot; + private Context context; + private int time = 0; + + public RouteManager(Train train) { + context = new Context(train); + state = State.STARTED; + Thread thread = new Thread(this); + thread.setName(train.name()); + thread.start(); + } + + private static TreeMap> availableRoutes(Context context,HashSet visitedRoutes){ + String inset = ""; + for (int i=0; i> availableRoutes = new TreeMap>(); + + boolean error = false; + if (isNull(block) && (error = true)) LOG.warn("{} → {}.availableRoutes called without context.block!",inset,Train.class.getSimpleName()); + if (isNull(train) && (error = true)) LOG.warn("{}→ {}.availableRoutes called without context.train!", inset,Train.class.getSimpleName()); + if (error) return availableRoutes; + + Block destination = train.destination(); + if (isSet(startDirection)) { + LOG.debug("{}- Looking for {}-bound routes from {}",inset,startDirection,block); + } else { + LOG.debug("{}- Looking for all routes from {}",inset,block); + } + + if (isSet(destination) && visitedRoutes.isEmpty()) LOG.debug("{}- Destination: {}",inset,destination); + + for (Route routeCandidate : block.routes(startDirection)) { + if (context.invalidated()) return availableRoutes; + if (visitedRoutes.contains(routeCandidate)) { + LOG.debug("{}→ Candidate {} would create loop, skipping",inset,routeCandidate.shortName()); + continue; + } + if (!routeCandidate.allowed(context)) { + if (routeCandidate.endBlock() != destination) { // allowance may be overridden by destination + LOG.debug("{} not allowed for {}",routeCandidate,context); + continue; // Zug darf auf Grund einer nicht erfüllten Bedingung nicht auf die Route + } + LOG.debug("{} not allowed for {} – overridden by selected destination",routeCandidate,context); + } + + int priority = 0; + if (isSet(startDirection) && routeCandidate.startDirection != startDirection) { // Route startet entgegen der aktuellen Fahrtrichtung des Zuges + if (!train.pushPull) continue; // Zug kann nicht wenden + if (!block.turnAllowed) continue; // Wenden im Block nicht gestattet + priority -= 5; + } + if (routeCandidate == currentRoute) priority-=10; // möglichst andere Route als zuvor wählen // TODO: den Routen einen "last-used" Zeitstempel hinzufügen, und diesen mit in die Priorisierung einbeziehen + + if (isSet(destination)) { + if (routeCandidate.endBlock() == destination) { // route goes directly to destination + LOG.debug("{}→ Candidate {} directly leads to {}",inset,routeCandidate.shortName(),destination); + priority = 1_000_000; + } else { + LOG.debug("{}- Candidate: {}",inset,routeCandidate.shortName()); + Context forwardContext = new Context(train).block(routeCandidate.endBlock()).route(null).direction(routeCandidate.endDirection); + visitedRoutes.add(routeCandidate); + TreeMap> forwardRoutes = availableRoutes(forwardContext,visitedRoutes); + visitedRoutes.remove(routeCandidate); + if (forwardRoutes.isEmpty()) continue; // the candidate does not lead to a block, from which routes to the destination exist + Entry> entry = forwardRoutes.lastEntry(); + LOG.debug("{}→ The following routes have connections to {}:",inset,destination); + for (Route rt: entry.getValue()) LOG.debug("{} - {}",inset,rt.shortName()); + priority += entry.getKey()-10; + } + } + + List routeSet = availableRoutes.get(priority); + if (isNull(routeSet)) { + routeSet = new Vector(); + availableRoutes.put(priority, routeSet); + } + routeSet.add(routeCandidate); + if (routeCandidate.endBlock() == destination) break; // direct connection to destination discovered, quit search + } + if (!availableRoutes.isEmpty()) LOG.debug("{}→ Routes from {}: {}",inset,block,availableRoutes.isEmpty()?"none":""); + for (Entry> entry : availableRoutes.entrySet()) { + LOG.debug("{} - Priority {}:",inset,entry.getKey()); + for (Route r : entry.getValue()) LOG.debug("{} - {}",inset,r.shortName()); + } + return availableRoutes; + } + + public boolean autoEnabled() { + return autopilot && isActive(); + } + + public static Route chooseRoute(Context context) { + LOG.debug("{}.chooseRoute({})",RouteManager.class.getSimpleName(),context); + TreeMap> availableRoutes = availableRoutes(context,new HashSet()); + while (!availableRoutes.isEmpty()) { + if (context.invalidated()) break; + LOG.debug("availableRoutes: {}",availableRoutes); + Entry> entry = availableRoutes.lastEntry(); + List preferredRoutes = entry.getValue(); + LOG.debug("preferredRoutes: {}",preferredRoutes); + Route selectedRoute = preferredRoutes.get(random.nextInt(preferredRoutes.size())); + if (selectedRoute.isFreeFor(context)) { + LOG.debug("Chose \"{}\" with priority {}.",selectedRoute,entry.getKey()); + return selectedRoute; + } + + LOG.debug("Selected route \"{}\" is not free for {}",selectedRoute,context); + preferredRoutes.remove(selectedRoute); + if (preferredRoutes.isEmpty()) availableRoutes.remove(availableRoutes.lastKey()); + } + return null; + } + + public boolean isActive() { + switch (state) { + case ENDED: + return false; + case IDLE: + return false; + default: + return true; + } + } + + private void pause(){ + if (time == 0) { + time = DEFAULT_PAUSE_TIME; + } else sleep(time); + } + + public void quit() { + LOG.debug("{}.quit",this); + autopilot = false; + context.invalidate(); + } + + @Override + public void run() { + Train train = context.train(); + try { + do { + pause(); + if (context.invalidated()) return; + context.block(train.currentBlock()).direction(train.direction()); + Route route = chooseRoute(context); + if (isNull(route)) continue; + context.route(route); + if (!route.reserveFor(context)) { + route.reset(); + continue; + } + if (!route.prepareAndLock()) { + route.reset(); + continue; + } + // Route reserved, prepared and locked: + if (!route.start()) { + route.reset(); + continue; + } + } while (autopilot); + } finally { + // do not invalidate context here: may be used in delayed actions called from (successful) start + state = State.ENDED; + train.removeChild(this); + } + } + + public void setAuto(boolean auto) { + LOG.debug("{}abled autopilot of {}",auto?"En":"Dis"); + autopilot = auto; + } +} diff --git a/src/main/java/de/srsoftware/web4rail/tiles/Block.java b/src/main/java/de/srsoftware/web4rail/tiles/Block.java index a84a822..d53e0c8 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/Block.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/Block.java @@ -3,11 +3,13 @@ package de.srsoftware.web4rail.tiles; import java.io.IOException; import java.util.Collection; import java.util.HashMap; -import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Vector; +import java.util.stream.Collectors; import org.json.JSONArray; import org.json.JSONException; @@ -20,6 +22,7 @@ import de.srsoftware.web4rail.BaseClass; import de.srsoftware.web4rail.Connector; import de.srsoftware.web4rail.Plan.Direction; import de.srsoftware.web4rail.Range; +import de.srsoftware.web4rail.Route; import de.srsoftware.web4rail.moving.Train; import de.srsoftware.web4rail.tags.Button; import de.srsoftware.web4rail.tags.Checkbox; @@ -35,6 +38,53 @@ import de.srsoftware.web4rail.tags.Window; */ public abstract class Block extends StretchableTile{ protected static Logger LOG = LoggerFactory.getLogger(Block.class); + + private class TrainList implements Iterable{ + + private LinkedList trains = new LinkedList(); + private HashMap dirs = new HashMap(); + + public void add(Train train, Direction direction) { + trains.remove(train); + trains.addFirst(train); + if (isSet(direction)) dirs.put(train, direction); + } + + public Direction directionOf(Train train) { + return dirs.get(train); + } + + public Train first() { + return trains.isEmpty() ? null : trains.getFirst(); + } + + public boolean isEmpty() { + return trains.isEmpty(); + } + + @Override + public Iterator iterator() { + return trains.iterator(); + } + + public Train last() { + return trains.isEmpty() ? null : trains.getLast(); + } + + public List list() { + return new Vector<>(trains); + } + + public boolean remove(BaseClass b) { + dirs.remove(b); + return trains.remove(b); + } + + @Override + public String toString() { + return trains.toString(); + } + } private static final String ALLOW_TURN = "allowTurn"; private static final String NAME = "name"; private static final String NO_TAG = "[default]"; @@ -44,11 +94,12 @@ public abstract class Block extends StretchableTile{ private static final String RAISE = "raise"; public static final String ACTION_ADD_CONTACT = "add_contact"; private static final String PARKED_TRAINS = "parked_trains"; + private static final String TRAINS = "parked_trains"; public String name = "Block"; public boolean turnAllowed = false; private Vector internalContacts = new Vector(); - private Vector parkedTrains = new Vector(); + private TrainList trains = new TrainList(); public Block() { super(); @@ -134,9 +185,9 @@ public abstract class Block extends StretchableTile{ private Vector waitTimes = new Vector(); - public void add(Train parkedTrain) { - parkedTrain.register(); - parkedTrains.add(parkedTrain); + public void add(Train train,Direction direction) { + train.register(); + trains.add(train,direction); } @@ -147,22 +198,17 @@ public abstract class Block extends StretchableTile{ } @Override - public boolean canNeEnteredBy(Train newTrain) { - if (!super.canNeEnteredBy(newTrain)) return false; - if (parkedTrains.isEmpty()) return true; - return isNull(newTrain) ? false : newTrain.isShunting(); // block contains train(s), thus it is only free for shunting train - } - - @Override - protected HashSet classes() { - HashSet classes = super.classes(); - if (!parkedTrains.isEmpty()) classes.add(Status.OCCUPIED.toString()); - return classes; + public boolean isFreeFor(Context context) { + Train train = context.train(); + if (is(Status.DISABLED)) return false; + if (trains.isEmpty()) return true; + if (trains.first() == train) return true; + return train.isShunting(); // block contains train(s), thus it is only free for shunting train } @Override public Object click(boolean shift) throws IOException { - if (isSet(train) && !shift) return train.properties(); + if (!trains.isEmpty() && !shift) return trains.first().properties(); return super.click(false); } @@ -211,6 +257,27 @@ public abstract class Block extends StretchableTile{ return this; } + private void dropFirstTrain() { + Train train = trains.first(); + if (isSet(train)) { + train.dropTrace(); + train.set(null); + trains.remove(train); + } + } + + @Override + public boolean free(Train train) { + LOG.debug("{}.free()"); + Train firstTrain = trains.first(); + if (isNull(firstTrain)) return true; + if (firstTrain != train) return false; + trains.remove(train); + status = trains.isEmpty() ? Status.FREE : Status.OCCUPIED; + plan.place(this); + return true; + } + private WaitTime getWaitTime(String tag) { if (tag == null) return null; for (WaitTime wt : waitTimes) { @@ -252,9 +319,9 @@ public abstract class Block extends StretchableTile{ } } if (isSet(jContacts)) json.put(CONTACT, jContacts); - if (!parkedTrains.isEmpty()) { + if (!trains.isEmpty()) { JSONArray ptids = new JSONArray(); - for (Train parked : parkedTrains) { + for (Train parked : trains) { if (isSet(parked)) ptids.put(parked.id().toString()); } json.put(PARKED_TRAINS, ptids); @@ -262,6 +329,11 @@ public abstract class Block extends StretchableTile{ return json; } + public Train lastTrain() { + return trains.last(); + } + + /** * If arguments are given, the first is taken as content, the second as tag type. * If no content is supplied, name is set as content. @@ -294,44 +366,61 @@ public abstract class Block extends StretchableTile{ } catch (JSONException e) {} } } - if (json.has(PARKED_TRAINS)) { + if (json.has(TRAINS)) { + JSONArray jTrains = json.getJSONArray(TRAINS); + for (Object o : jTrains) { + if (o instanceof JSONObject) { + JSONObject to = (JSONObject) o; + Train train = BaseClass.get(new Id(to.getString(ID))); + Direction direction = to.has(DIRECTION) ? Direction.valueOf(to.getString(DIRECTION)) : null; + if (isSet(train)) trains.add(train, direction); + } + } + } + if (json.has(PARKED_TRAINS)) { // legacy JSONArray ptids = json.getJSONArray(PARKED_TRAINS); for (Object id : ptids) { Train train = BaseClass.get(new Id(id.toString())); - if (isSet(train)) parkedTrains.add(train); + if (isSet(train)) trains.add(train,null); } } return super.load(json); } - private Fieldset parkedTrainList() { - Fieldset fieldset = new Fieldset(t("parked trains")); - Tag list = new Tag("ul"); - for (Train t : parkedTrains) { - if (isSet(t)) t.link("li", t).addTo(list); + @Override + public boolean lockFor(Context context) { + Train newTrain = context.train(); + Route route = context.route(); + LOG.debug("{}.lock({})",this,newTrain); + if (isNull(newTrain)) return false; + Train train = trains.first(); + if (isSet(train) && train != newTrain) return false; + switch (status) { + case DISABLED: + return false; + case FREE: + case RESERVED: + status = Status.LOCKED; + Direction dir = trains.directionOf(train); + if (isSet(route) && this == route.endBlock()) dir = route.endDirection; + add(newTrain,dir); + plan.place(this); + break; + case OCCUPIED: + case LOCKED: + break; // do not downgrade } - list.addTo(fieldset); - return fieldset; + return true; } - public List parkedTrains(){ - return parkedTrains; - } - - public Train parkedTrain(boolean last) { - if (parkedTrains.isEmpty()) return null; - return last ? parkedTrains.lastElement() : parkedTrains.firstElement(); - } - - @Override protected Window properties(List
preForm, FormInput formInputs, List
postForm,String...errors) { formInputs.add(t("Name"),new Input(NAME, name)); formInputs.add("",new Checkbox(ALLOW_TURN,t("Turn allowed"),turnAllowed)); - formInputs.add(t("Train"),Train.selector(train, null)); + formInputs.add(t("Train"),Train.selector(trains.first(), null)); postForm.add(contactForm()); postForm.add(waitTimeForm()); - if (!parkedTrains.isEmpty()) postForm.add(parkedTrainList()); + if (!trains.isEmpty()) postForm.add(trainList()); return super.properties(preForm, formInputs, postForm,errors); } @@ -346,6 +435,32 @@ public abstract class Block extends StretchableTile{ } return this; } + + @Override + public boolean reserveFor(Context context) { + Train newTrain = context.train(); + Route route = context.route(); + LOG.debug("{}.reserverFor({})",this,newTrain); + if (isNull(newTrain)) return false; + Train train = trains.first(); + if (isSet(train) && train != newTrain) return false; + switch (status) { + case DISABLED: + return false; + case FREE: + status = Status.RESERVED; + Direction dir = trains.directionOf(train); + if (isSet(route) && this == route.endBlock()) dir = route.endDirection; + add(newTrain,dir); + plan.place(this); + break; + case OCCUPIED: + case LOCKED: + case RESERVED: + break; // do not downgrade + } + return true; + } public BlockContact register(BlockContact contact) { internalContacts.add(contact); @@ -356,13 +471,17 @@ public abstract class Block extends StretchableTile{ public void removeChild(BaseClass child) { super.removeChild(child); internalContacts.remove(child); - if (parkedTrains.remove(child)) plan.place(this); + if (trains.remove(child)) plan.place(this); } public void removeContact(BlockContact blockContact) { internalContacts.remove(blockContact); } + public List routes(Direction direction) { + return routes().stream().filter(route -> route.startBlock() == Block.this).collect(Collectors.toList()); + } + public abstract List startPoints(); @Override @@ -370,10 +489,7 @@ public abstract class Block extends StretchableTile{ if (isNull(replacements)) replacements = new HashMap(); replacements.put("%text%",name); Vector trainNames = new Vector(); - if (isSet(train)) trainNames.add(train.directedName()); - for (Train t:parkedTrains) { - if (isSet(t)) trainNames.add(t.directedName()); - } + for (Train train: trains) trainNames.add(train.directedName(trains.directionOf(train))); if (!trainNames.isEmpty())replacements.put("%text%",String.join(" | ", trainNames)); Tag tag = super.tag(replacements); tag.clazz(tag.get("class")+" Block"); @@ -390,23 +506,45 @@ public abstract class Block extends StretchableTile{ return name + " @ ("+x+","+y+")"; } + @Override + public Train train() { + return train(false); + } + + public Train train(boolean last) { + return last ? trains.last() : trains.first(); + } + + + private Fieldset trainList() { + Fieldset fieldset = new Fieldset(t("Trains")); + Tag list = new Tag("ul"); + for (Train t : trains) { + if (isSet(t)) t.link("li", t).addTo(list); + } + list.addTo(fieldset); + return fieldset; + } + + public List trains(){ + return trains.list(); + } + @Override public Tile update(HashMap params) { if (params.containsKey(NAME)) name=params.get(NAME); if (params.containsKey(Train.class.getSimpleName())) { Id trainId = Id.from(params,Train.class.getSimpleName()); - if (trainId.equals(0)) { - if (isSet(train)) { - train.dropTrace(); - train.set(null); - } - train = null; + if (trainId.equals(0)) { // remove first train + dropFirstTrain(); } else { - Train newTrain = Train.get(trainId); - if (isSet(newTrain) && newTrain != train) { - newTrain.dropTrace(); - if (connections(newTrain.direction()).isEmpty()) newTrain.heading(null); - newTrain.set(this); + Train train = Train.get(trainId); + if (isSet(train) && train != trains.first()) { + dropFirstTrain(); + train.dropTrace(); + if (connections(train.direction()).isEmpty()) train.heading(null); + train.set(this); + trains.add(train,train.direction()); } } } diff --git a/src/main/java/de/srsoftware/web4rail/tiles/BlockContact.java b/src/main/java/de/srsoftware/web4rail/tiles/BlockContact.java index 8963560..0e63fe3 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/BlockContact.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/BlockContact.java @@ -34,8 +34,7 @@ public class BlockContact extends Contact { @Override public Train train() { - train = ((Block)parent()).train(); - return train; + return ((Block)parent()).train(); } @Override diff --git a/src/main/java/de/srsoftware/web4rail/tiles/Bridge.java b/src/main/java/de/srsoftware/web4rail/tiles/Bridge.java index 5f7e56c..845d2c1 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/Bridge.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/Bridge.java @@ -42,9 +42,9 @@ public abstract class Bridge extends Tile { protected abstract Connector connector(); @Override - public void free() { - if (isSet(counterpart) && counterpart.train != null) counterpart.free(); - super.free(); + public boolean free(Train train) { + if (!super.free(train)) return false; + return isSet(counterpart) ? counterpart.free(train) : true; } @Override @@ -68,10 +68,10 @@ public abstract class Bridge extends Tile { } @Override - public boolean setState(Status newState, Train newTrain) { - if (train == newTrain && is(newState)) return true; - if (!super.setState(newState,newTrain)) return false; - return isNull(counterpart) ? true : counterpart.setState(newState,newTrain); + public boolean setTrain(Train newTrain) { + if (train() == newTrain) return true; + if (!super.setTrain(newTrain)) return false; + return isNull(counterpart) ? true : counterpart.setTrain(newTrain); } @Override diff --git a/src/main/java/de/srsoftware/web4rail/tiles/Contact.java b/src/main/java/de/srsoftware/web4rail/tiles/Contact.java index 9beccdb..a12c376 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/Contact.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/Contact.java @@ -18,6 +18,7 @@ import de.srsoftware.web4rail.Application; import de.srsoftware.web4rail.BaseClass; import de.srsoftware.web4rail.actions.Action; import de.srsoftware.web4rail.actions.ActionList; +import de.srsoftware.web4rail.moving.Train; import de.srsoftware.web4rail.tags.Fieldset; import de.srsoftware.web4rail.tags.Input; import de.srsoftware.web4rail.tags.Select; @@ -84,11 +85,8 @@ public class Contact extends Tile{ LOG.debug("{} activated.",this); state = true; if (isSet(timer)) timer.abort(); - Context context = new Context(this); - if (isSet(train())) { - train.contact(this); - context.train(train); - } + Train train = train(); + Context context = isSet(train) ? train.contact(this) : new Context(this); actions.fire(context,"Contact("+addr+")"); for (Listener listener : listeners) listener.fired("Contact("+addr+")"); diff --git a/src/main/java/de/srsoftware/web4rail/tiles/Tile.java b/src/main/java/de/srsoftware/web4rail/tiles/Tile.java index f306a7f..d39b835 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/Tile.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/Tile.java @@ -38,109 +38,81 @@ import de.srsoftware.web4rail.tags.Window; * @author Stephan Richter, SRSoftware * */ -public abstract class Tile extends BaseClass implements Comparable{ - public enum Status{ - FREE("free"), - ALLOCATED("allocated"), - LOCKED("locked"), - OCCUPIED("occupied"); +public abstract class Tile extends BaseClass implements Comparable { + public enum Status { + DISABLED("disabled"), FREE("free"), RESERVED("reserved"), LOCKED("locked"), OCCUPIED("occupied"); private String tx; Status(String s) { tx = s; } - + @Override public String toString() { return tx; } } - protected static Logger LOG = LoggerFactory.getLogger(Tile.class); + + protected static Logger LOG = LoggerFactory.getLogger(Tile.class); private static int DEFAUT_LENGTH = 100; // 10cm - - private static final String LENGTH = "length"; - private static final String ONEW_WAY = "one_way"; - private static final String POS = "pos"; - private static final String TYPE = "type"; - private static final String X = "x"; - private static final String Y = "y"; - - private boolean disabled = false; - private boolean isTrack = true; - private int length = DEFAUT_LENGTH; - protected Direction oneWay = null; - private TreeSet routes = new TreeSet<>((r1,r2)->r1.toString().compareTo(r2.toString())); - private Status status = Status.FREE; - protected Train train = null; - public Integer x = null; - public Integer y = null; - + + private static final String LENGTH = "length"; + private static final String ONEW_WAY = "one_way"; + private static final String POS = "pos"; + private static final String TYPE = "type"; + private static final String X = "x"; + private static final String Y = "y"; + + private boolean isTrack = true; + private int length = DEFAUT_LENGTH; + protected Direction oneWay = null; + private TreeSet routes = new TreeSet<>((r1, r2) -> r1.toString().compareTo(r2.toString())); + private Train train = null; + protected Status status = Status.FREE; + public Integer x = null; + public Integer y = null; + public void add(Route route) { this.routes.add(route); } - - public boolean canNeEnteredBy(Train newTrain) { - LOG.debug("{}.canNeEnteredBy({})",this,newTrain); - if (disabled) { - LOG.debug("{} is disabled!",this); - return false; - } - - if (isNull(train)) { - LOG.debug("→ free"); - return true; - } - if (newTrain == train) { // during train.reserveNext, we may encounter, parts, that are already reserved by the respective train, but having another route. do not compare routes in that case! - LOG.debug("already reserved by {} → true",train); - return true; - } - - if (isSet(newTrain) && newTrain.isShunting()) { - LOG.debug("occupied by {}. Allowed for shunting {}",train,newTrain); - return true; - } - - LOG.debug("occupied by {} → false",train); - return false; - } - - protected HashSet classes(){ + protected HashSet classes() { HashSet classes = new HashSet(); classes.add("tile"); - classes.add(getClass().getSimpleName()); + classes.add(getClass().getSimpleName()); if (!is(Status.FREE)) classes.add(status.toString()); - if (disabled) classes.add(DISABLED); return classes; } public Object click(boolean shift) throws IOException { - LOG.debug("{}.click()",getClass().getSimpleName()); + LOG.debug("{}.click()", getClass().getSimpleName()); if (isSet(train) && shift) return train.properties(); return properties(); } - + @Override public int compareTo(Tile other) { - if (x == other.x) return y-other.y; + if (x == other.x) return y - other.y; return x - other.x; } - + public JSONObject config() { return new JSONObject(); } - - public Map connections(Direction from){ + + public Map connections(Direction from) { return new HashMap<>(); } - - public void free() { + + public boolean free(Train t) { + if (t != train) return false; train = null; status = Status.FREE; plan.place(this); + return true; } - + public int height() { return 1; } @@ -148,60 +120,89 @@ public abstract class Tile extends BaseClass implements Comparable{ public Id id() { return Tile.id(x, y); } - + public static Id id(int x, int y) { - return new Id(x+"-"+y); + return new Id(x + "-" + y); } - - private static void inflate(String clazz, JSONObject json, Plan plan) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, ClassNotFoundException, IOException { - clazz = Tile.class.getName().replace(".Tile", "."+clazz); + + private static void inflate(String clazz, JSONObject json, + Plan plan) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, ClassNotFoundException, IOException { + clazz = Tile.class.getName().replace(".Tile", "." + clazz); Tile tile = (Tile) Tile.class.getClassLoader().loadClass(clazz).getDeclaredConstructor().newInstance(); tile.load(json).register(); - if (tile instanceof TileWithShadow) ((TileWithShadow)tile).placeShadows(); + if (tile instanceof TileWithShadow) ((TileWithShadow) tile).placeShadows(); plan.place(tile); } - - public boolean is(Status...states) { - for (Status s: states) { + + public boolean is(Status... states) { + for (Status s : states) { if (status == s) return true; } return false; } + public boolean isFreeFor(Context newTrain) { + LOG.debug("{}.isFreeFor({})", this, newTrain); + if (is(Status.DISABLED)) { + LOG.debug("{} is disabled!", this); + return false; + } + + if (isNull(train)) { + LOG.debug("→ free"); + return true; + } + + if (newTrain.train() == train) { // during train.reserveNext, we may encounter, parts, that are already reserved + // by the respective train, but having another route. do not compare routes + // in that case! + LOG.debug("already reserved by {} → true", train); + return true; + } + + if (isSet(newTrain.train()) && newTrain.train().isShunting()) { + LOG.debug("occupied by {}. Allowed for shunting {}", train, newTrain.train()); + return true; + } + + LOG.debug("occupied by {} → false", train); + return false; + } + public JSONObject json() { JSONObject json = super.json(); json.put(TYPE, getClass().getSimpleName()); - if (isSet(x) && isSet(y)) json.put(POS, new JSONObject(Map.of(X,x,Y,y))); - if (isSet(oneWay)) json.put(ONEW_WAY, oneWay); - if (disabled) json.put(DISABLED, true); - if (isSet(train)) json.put(REALM_TRAIN, train.id()); + if (isSet(x) && isSet(y)) json.put(POS, new JSONObject(Map.of(X, x, Y, y))); + if (isSet(oneWay)) json.put(ONEW_WAY, oneWay); + if (is(Status.DISABLED)) json.put(DISABLED, true); + if (isSet(train)) json.put(REALM_TRAIN, train.id()); json.put(LENGTH, length); return json; } - + public int length() { return length; } - + public Tile length(int newLength) { length = Math.max(0, newLength); return this; } - + /** - * If arguments are given, the first is taken as content, the second as tag type. - * If no content is supplied, id() is set as content. - * If no type is supplied, "span" is preset. + * If arguments are given, the first is taken as content, the second as tag + * type. If no content is supplied, id() is set as content. If no type is + * supplied, "span" is preset. + * * @param args * @return */ - public Tag link(String...args) { - String tx = args.length<1 ? id()+NBSP : args[0]; - String type = args.length<2 ? "span" : args[1]; - return super.link(type, (Object)tx, Map.of(ACTION,ACTION_CLICK)); + public Tag link(String... args) { + String tx = args.length < 1 ? id() + NBSP : args[0]; + String type = args.length < 2 ? "span" : args[1]; + return super.link(type, (Object) tx, Map.of(ACTION, ACTION_CLICK)); } - - + public static void load(Object object, Plan plan) { if (object instanceof JSONObject) { JSONObject json = (JSONObject) object; @@ -210,10 +211,12 @@ public abstract class Tile extends BaseClass implements Comparable{ clazz = AlterDirection.class.getSimpleName(); } try { - Tile.inflate(clazz,json,plan); - } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException | ClassNotFoundException | IOException e) { + Tile.inflate(clazz, json, plan); + } catch (InstantiationException | IllegalAccessException | IllegalArgumentException + | InvocationTargetException | NoSuchMethodException | SecurityException | ClassNotFoundException + | IOException e) { e.printStackTrace(); - } + } } } @@ -223,89 +226,92 @@ public abstract class Tile extends BaseClass implements Comparable{ if (json.has(POS)) { JSONObject pos = json.getJSONObject(POS); x = pos.getInt(X); - y = pos.getInt(Y); + y = pos.getInt(Y); } - if (json.has(DISABLED)) disabled = json.getBoolean(DISABLED); - if (json.has(LENGTH)) length = json.getInt(LENGTH); - if (json.has(ONEW_WAY)) oneWay = Direction.valueOf(json.getString(ONEW_WAY)); + if (json.has(DISABLED) && json.getBoolean(DISABLED)) status = Status.DISABLED; + if (json.has(LENGTH)) length = json.getInt(LENGTH); + if (json.has(ONEW_WAY)) oneWay = Direction.valueOf(json.getString(ONEW_WAY)); return this; } - + public boolean move(int dx, int dy) { - int destX = x+(dx > 0 ? width() : dx); - int destY = y+(dy > 0 ? height() : dy); + int destX = x + (dx > 0 ? width() : dx); + int destY = y + (dy > 0 ? height() : dy); if (destX < 0 || destY < 0) return false; - - Tile tileAtDestination = plan.get(id(destX, destY),true); + + Tile tileAtDestination = plan.get(id(destX, destY), true); if (isSet(tileAtDestination) && !tileAtDestination.move(dx, dy)) return false; - plan.drop(this); - position(x+dx, y+dy); + plan.drop(this); + position(x + dx, y + dy); plan.place(this); return true; } - + protected void noTrack() { - isTrack = false; + isTrack = false; } - + public Tile position(int x, int y) { this.x = x; this.y = y; return this; } - + public List possibleDirections() { return new Vector(); } - + @Override - protected Window properties(List
preForm, FormInput formInputs, List
postForm,String...errors) { + protected Window properties(List
preForm, FormInput formInputs, List
postForm, + String... errors) { Fieldset fieldset = null; - + if (isSet(train)) { fieldset = new Fieldset(t("Train")); - train.link("span", t("Train")+":"+NBSP+train+NBSP).addTo(fieldset); + train.link("span", t("Train") + ":" + NBSP + train + NBSP).addTo(fieldset); if (isSet(train.route())) { - train.button(t("stop"), Map.of(ACTION,ACTION_STOP)).addTo(fieldset); + train.button(t("stop"), Map.of(ACTION, ACTION_STOP)).addTo(fieldset); } else { - train.button(t("depart"), Map.of(ACTION,ACTION_START)).addTo(fieldset); + train.button(t("depart"), Map.of(ACTION, ACTION_START)).addTo(fieldset); } if (train.usesAutopilot()) { - train.button(t("quit autopilot"), Map.of(ACTION,ACTION_QUIT)).addTo(fieldset); + train.button(t("quit autopilot"), Map.of(ACTION, ACTION_QUIT)).addTo(fieldset); } else { - train.button(t("auto"), Map.of(ACTION,ACTION_AUTO)).addTo(fieldset); + train.button(t("auto"), Map.of(ACTION, ACTION_AUTO)).addTo(fieldset); } } - + if (isSet(fieldset)) preForm.add(fieldset); - + if (isTrack) { - formInputs.add(t("Length"),new Input(LENGTH,length).numeric().addTo(new Tag("span")).content(NBSP+lengthUnit)); - Checkbox checkbox = new Checkbox(DISABLED, t("disabled"),disabled); - if (disabled) checkbox.clazz("disabled"); - formInputs.add(t("State"),checkbox); + formInputs.add(t("Length"), + new Input(LENGTH, length).numeric().addTo(new Tag("span")).content(NBSP + lengthUnit)); + Checkbox checkbox = new Checkbox(DISABLED, t("disabled"), is(Status.DISABLED)); + if (is(Status.DISABLED)) checkbox.clazz("disabled"); + formInputs.add(t("State"), checkbox); } - + List pd = possibleDirections(); if (!pd.isEmpty()) { Tag div = new Tag("div"); - new Radio("oneway","none",t("No"),isNull(oneWay)).addTo(div); - for (Direction d:pd) { - new Radio("oneway",d.toString(),t(d.toString()),d == oneWay).addTo(div); + new Radio("oneway", "none", t("No"), isNull(oneWay)).addTo(div); + for (Direction d : pd) { + new Radio("oneway", d.toString(), t(d.toString()), d == oneWay).addTo(div); } - formInputs.add(t("One way"),div); + formInputs.add(t("One way"), div); } - if (!routes.isEmpty()) { fieldset = new Fieldset(t("Routes")).id("props-routes"); Tag routeList = new Tag("ol"); boolean empty = true; for (Route route : routes) { if (route.isDisabled()) continue; - Tag li = route.link("span", route.name()+(route.isDisabled()?" ["+t("disabled")+"]" : "")+NBSP).addTo(new Tag("li").clazz("link")); - route.button(t("delete route"),Map.of(ACTION,ACTION_DROP)).addTo(li); - button(t("simplify name"), Map.of(ACTION,ACTION_AUTO,ROUTE,route.id().toString())).addTo(li); + Tag li = route + .link("span", route.name() + (route.isDisabled() ? " [" + t("disabled") + "]" : "") + NBSP) + .addTo(new Tag("li").clazz("link")); + route.button(t("delete route"), Map.of(ACTION, ACTION_DROP)).addTo(li); + button(t("simplify name"), Map.of(ACTION, ACTION_AUTO, ROUTE, route.id().toString())).addTo(li); li.addTo(routeList); empty = false; } @@ -314,14 +320,16 @@ public abstract class Tile extends BaseClass implements Comparable{ routeList.addTo(fieldset); postForm.add(fieldset); } - + routeList = new Tag("ol"); empty = true; for (Route route : routes) { if (!route.isDisabled()) continue; - Tag li = route.link("span", route.name()+(route.isDisabled()?" ["+t("disabled")+"]" : "")+NBSP).addTo(new Tag("li").clazz("link")); - route.button(t("delete route"),Map.of(ACTION,ACTION_DROP)).addTo(li); - button(t("simplify name"), Map.of(ACTION,ACTION_AUTO,ROUTE,route.id().toString())).addTo(li); + Tag li = route + .link("span", route.name() + (route.isDisabled() ? " [" + t("disabled") + "]" : "") + NBSP) + .addTo(new Tag("li").clazz("link")); + route.button(t("delete route"), Map.of(ACTION, ACTION_DROP)).addTo(li); + button(t("simplify name"), Map.of(ACTION, ACTION_AUTO, ROUTE, route.id().toString())).addTo(li); li.addTo(routeList); empty = false; } @@ -330,84 +338,90 @@ public abstract class Tile extends BaseClass implements Comparable{ routeList.addTo(fieldset); } } - - return super.properties(preForm, formInputs, postForm,errors); + + return super.properties(preForm, formInputs, postForm, errors); } - + private static String replace(String line, Entry replacement) { - String key = replacement.getKey(); + String key = replacement.getKey(); Object val = replacement.getValue(); int start = line.indexOf(key); int len = key.length(); - while (start>0) { - int end = line.indexOf("\"",start); - int end2 = line.indexOf("<",start); - if (end2>0 && (end<0 || end2 0) { + int end = line.indexOf("\"", start); + int end2 = line.indexOf("<", start); + if (end2 > 0 && (end < 0 || end2 < end)) end = end2; String tag = line.substring(start, end); - if (tag.length()>len) val = Integer.parseInt(tag.substring(len)) + (int) val; - line = line.replace(tag, ""+val); + if (tag.length() > len) val = Integer.parseInt(tag.substring(len)) + (int) val; + line = line.replace(tag, "" + val); start = line.indexOf(key); } return line; } - + public TreeSet routes() { return routes; } - + public static void saveAll(String filename) throws IOException { BufferedWriter file = new BufferedWriter(new FileWriter(filename)); for (Tile tile : BaseClass.listElements(Tile.class)) { if (isNull(tile) || tile instanceof Shadow || tile instanceof BlockContact) continue; - file.append(tile.json()+"\n"); + file.append(tile.json() + "\n"); } file.close(); } - - public boolean setState(Status newState,Train newTrain) { + + public boolean setTrain(Train newTrain) { if (isNull(newTrain)) return false; - if (isSet(train) && newTrain != train) return false; // already locked by other train - if (is(Status.OCCUPIED,newState)) return true; // do not downgrade occupied tiles, accept current state - train = newTrain; - status = newState; - plan.place(this); + if (isSet(train) && newTrain != train) return false; + switch (status) { // bisheriger Status + case DISABLED: + return false; + case FREE: + case RESERVED: + case LOCKED: + train = newTrain; + status = Status.OCCUPIED; + plan.place(this); + break; + case OCCUPIED: + break; + } return true; } - - public Tag tag(Map replacements) throws IOException { - int width = 100*width(); - int height = 100*height(); + + public Tag tag(Map replacements) throws IOException { + int width = 100 * width(); + int height = 100 * height(); if (isNull(replacements)) replacements = new HashMap(); - replacements.put("%width%",width); - replacements.put("%height%",height); + replacements.put("%width%", width); + replacements.put("%height%", height); String style = ""; - Tag svg = new Tag("svg") - .id(isSet(x) && isSet(y) ? id().toString() : getClass().getSimpleName()) - .clazz(classes()) - .size(100,100) - .attr("name", getClass().getSimpleName()) - .attr("viewbox", "0 0 "+width+" "+height); - if (isSet(x)) style="left: "+(30*x)+"px; top: "+(30*y)+"px;"; - if (width()>1) style+=" width: "+(30*width())+"px;"; - if (height()>1) style+=" height: "+(30*height())+"px;"; - + Tag svg = new Tag("svg").id(isSet(x) && isSet(y) ? id().toString() : getClass().getSimpleName()) + .clazz(classes()).size(100, 100).attr("name", getClass().getSimpleName()) + .attr("viewbox", "0 0 " + width + " " + height); + if (isSet(x)) style = "left: " + (30 * x) + "px; top: " + (30 * y) + "px;"; + if (width() > 1) style += " width: " + (30 * width()) + "px;"; + if (height() > 1) style += " height: " + (30 * height()) + "px;"; + if (!style.isEmpty()) svg.style(style); - File file = new File(System.getProperty("user.dir")+"/resources/svg/"+getClass().getSimpleName()+".svg"); + File file = new File(System.getProperty("user.dir") + "/resources/svg/" + getClass().getSimpleName() + ".svg"); if (file.exists()) { Scanner scanner = new Scanner(file, StandardCharsets.UTF_8); StringBuffer sb = new StringBuffer(); while (scanner.hasNextLine()) { String line = scanner.nextLine(); if (line.startsWith("")) continue; - for (Entry replacement : replacements.entrySet()) line = replace(line,replacement); - sb.append(line+"\n"); + for (Entry replacement : replacements.entrySet()) line = replace(line, replacement); + sb.append(line + "\n"); } scanner.close(); svg.content(sb.toString()); - + if (isSet(oneWay)) { - switch (oneWay) { + switch (oneWay) { case EAST: new Tag("polygon").clazz("oneway").attr("points", "100,50 75,35 75,65").addTo(svg); break; @@ -426,54 +440,97 @@ public abstract class Tile extends BaseClass implements Comparable{ String title = title(); if (isSet(title)) new Tag("title").content(title()).addTo(svg); } else { - new Tag("title").content(t("No display defined for this tile ({})",getClass().getSimpleName())).addTo(svg); - new Tag("text") - .pos(35,70) - .content("?") - .addTo(svg); + new Tag("title").content(t("No display defined for this tile ({})", getClass().getSimpleName())).addTo(svg); + new Tag("text").pos(35, 70).content("?").addTo(svg); } return svg; } - + public String title() { - return getClass().getSimpleName() + " @ ("+x+", "+y+")"; + return getClass().getSimpleName() + " @ (" + x + ", " + y + ")"; } - + @Override public String toString() { - return t("{}({},{})",getClass().getSimpleName(),x,y) ; + return t("{}({},{})", getClass().getSimpleName(), x, y); } - + public Train train() { return train; } - + @Override public BaseClass remove() { while (!routes.isEmpty()) routes.first().remove(); return super.remove(); } - + @Override public void removeChild(BaseClass child) { String childAsString = child.toString(); - if (childAsString.length()>20) childAsString = childAsString.substring(0, 20)+"…"; - LOG.debug("Removing {} from {}",childAsString,this); + if (childAsString.length() > 20) childAsString = childAsString.substring(0, 20) + "…"; + LOG.debug("Removing {} from {}", childAsString, this); if (child instanceof Route) routes.remove(child); - + if (child == train) train = null; super.removeChild(child); plan.place(this); } - public void setEnabled(boolean newState) { - disabled = !newState; + public boolean lockFor(Context context) { + Train newTrain = context.train(); + LOG.debug("{}.lockFor({})",this,newTrain); + if (isNull(newTrain)) return false; + if (isSet(train) && train != newTrain) return false; + switch (status) { + case DISABLED: + return false; + case FREE: + case RESERVED: + status = Status.LOCKED; + plan.place(this); + break; + case OCCUPIED: + case LOCKED: + break; // do not downgrade + } + return true; + } + + + public boolean reserveFor(Context context) { + Train newTrain = context.train(); + LOG.debug("{}.reserverFor({})",this,newTrain); + if (isNull(newTrain)) return false; + if (isSet(train) && train != newTrain) return false; + switch (status) { + case DISABLED: + return false; + case FREE: + status = Status.RESERVED; + train = newTrain; + plan.place(this); + break; + case OCCUPIED: + case LOCKED: + case RESERVED: + break; // do not downgrade + } + return true; + } + + public void setEnabled(boolean enabled) { + if (!enabled) { + status = Status.DISABLED; + } else if (is(Status.DISABLED)) { // Status nur ändern, wenn er bisher DISABLED war + status = isNull(train) ? Status.FREE : Status.OCCUPIED; + } plan.place(this); } - + public Tile update(HashMap params) { - LOG.debug("{}.update({})",getClass().getSimpleName(),params); + LOG.debug("{}.update({})", getClass().getSimpleName(), params); String oneWayDir = params.get("oneway"); if (isSet(oneWayDir)) { try { @@ -482,17 +539,15 @@ public abstract class Tile extends BaseClass implements Comparable{ oneWay = null; } } - disabled = "on".equals(params.get(DISABLED)); + if ("on".equals(params.get(DISABLED))) status = Status.DISABLED; String len = params.get(LENGTH); if (isSet(len)) length(Integer.parseInt(len)); super.update(params); plan.place(this); return this; } - + public int width() { return 1; } - - } diff --git a/src/main/java/de/srsoftware/web4rail/tiles/Turnout.java b/src/main/java/de/srsoftware/web4rail/tiles/Turnout.java index 8ca7fbb..0c60bc0 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/Turnout.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/Turnout.java @@ -159,7 +159,7 @@ public abstract class Turnout extends Tile implements Device{ } public Reply state(State newState) { - if (is(Status.LOCKED,Status.OCCUPIED) && newState != state) return new Reply(415, t("{} locked by {}!",this,train)); + if (is(Status.LOCKED,Status.OCCUPIED) && newState != state) return new Reply(415, t("{} locked by {}!",this,train())); if (address == 0) { state = newState; plan.place(this); diff --git a/src/main/java/de/srsoftware/web4rail/tiles/TurnoutL.java b/src/main/java/de/srsoftware/web4rail/tiles/TurnoutL.java index 26bdbfa..811fa5c 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/TurnoutL.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/TurnoutL.java @@ -4,6 +4,7 @@ import java.io.IOException; import java.util.HashMap; import java.util.List; +import de.srsoftware.web4rail.moving.Train; import de.srsoftware.web4rail.tags.Fieldset; import de.srsoftware.web4rail.tags.Input; import de.srsoftware.web4rail.tags.Window; @@ -16,6 +17,7 @@ public abstract class TurnoutL extends Turnout { public Object click(boolean shift) throws IOException { Object o = super.click(shift); if (!shift) { + Train train = train(); if (isSet(train)) { plan.stream(t("{} is locked by {}!",this,train)); } else state(state == State.STRAIGHT ? State.LEFT : State.STRAIGHT); diff --git a/src/main/java/de/srsoftware/web4rail/tiles/TurnoutR.java b/src/main/java/de/srsoftware/web4rail/tiles/TurnoutR.java index ec5a003..6c22028 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/TurnoutR.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/TurnoutR.java @@ -4,6 +4,7 @@ import java.io.IOException; import java.util.HashMap; import java.util.List; +import de.srsoftware.web4rail.moving.Train; import de.srsoftware.web4rail.tags.Fieldset; import de.srsoftware.web4rail.tags.Input; import de.srsoftware.web4rail.tags.Window; @@ -16,6 +17,7 @@ public abstract class TurnoutR extends Turnout { public Object click(boolean shift) throws IOException { Object o = super.click(shift); if (!shift) { + Train train = train(); if (isSet(train)) { plan.stream(t("{} is locked by {}!",this,train)); } else state(state == State.STRAIGHT ? State.RIGHT : State.STRAIGHT); From c4e57d2b8a18564ecbcd7418d5e7c82f88f82419 Mon Sep 17 00:00:00 2001 From: Stephan Richter Date: Sun, 14 Mar 2021 15:07:30 +0100 Subject: [PATCH 15/26] re-implemented trace update code --- pom.xml | 2 +- .../de/srsoftware/web4rail/BaseClass.java | 6 ++ .../java/de/srsoftware/web4rail/Plan.java | 2 +- .../java/de/srsoftware/web4rail/Route.java | 27 +++--- .../de/srsoftware/web4rail/moving/Train.java | 93 ++++++++++++------- .../de/srsoftware/web4rail/tiles/Block.java | 15 ++- .../de/srsoftware/web4rail/tiles/Tile.java | 7 +- 7 files changed, 99 insertions(+), 53 deletions(-) diff --git a/pom.xml b/pom.xml index 46fdadd..1d91752 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 de.srsoftware web4rail - 1.3.65 + 1.3.66 Web4Rail jar Java Model Railway Control diff --git a/src/main/java/de/srsoftware/web4rail/BaseClass.java b/src/main/java/de/srsoftware/web4rail/BaseClass.java index 1676969..c1a0e28 100644 --- a/src/main/java/de/srsoftware/web4rail/BaseClass.java +++ b/src/main/java/de/srsoftware/web4rail/BaseClass.java @@ -4,6 +4,7 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.AbstractMap; import java.util.ArrayList; +import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; @@ -521,6 +522,11 @@ public abstract class BaseClass implements Constants{ registry = new HashMap(); customFieldNames = new HashMap, Set>(); } + + public static > L reverse(L list){ + Collections.reverse(list); + return list; + } public void sleep(long ms) { try { diff --git a/src/main/java/de/srsoftware/web4rail/Plan.java b/src/main/java/de/srsoftware/web4rail/Plan.java index 657725c..4240ec4 100644 --- a/src/main/java/de/srsoftware/web4rail/Plan.java +++ b/src/main/java/de/srsoftware/web4rail/Plan.java @@ -356,7 +356,7 @@ public class Plan extends BaseClass{ new Input(ACTION,ACTION_UPDATE).hideIn(form); new Input(LENGTH_UNIT, lengthUnit).addTo(new Label(t("Length unit")+COL)).addTo(form); new Input(SPEED_UNIT, speedUnit).addTo(new Label(t("Speed unit")+COL)).addTo(form); - new Input(FINAL_SPEED, Train.defaultEndSpeed).addTo(new Label(t("Lower speed limit")+COL)).attr("title", t("Final speed after breaking, before halting")).addTo(form); // TODO + new Input(FINAL_SPEED, Train.defaultEndSpeed).addTo(new Label(t("Lower speed limit")+COL)).attr("title", t("Final speed after breaking, before halting")).addTo(form); new Checkbox(FREE_BEHIND_TRAIN, t("Free tiles behind train"), Route.freeBehindTrain).attr("title", t("If checked, tiles behind the train are freed according to the length of the train and the tiles. If it is unchecked, tiles will not get free before route is finished.")).addTo(form); new Button(t("Save"), form).addTo(form); form.addTo(fieldset); diff --git a/src/main/java/de/srsoftware/web4rail/Route.java b/src/main/java/de/srsoftware/web4rail/Route.java index 4afaa79..c280d5c 100644 --- a/src/main/java/de/srsoftware/web4rail/Route.java +++ b/src/main/java/de/srsoftware/web4rail/Route.java @@ -322,7 +322,9 @@ public class Route extends BaseClass { LOG.debug("Contact has id {} / trigger {} and is assigned with {}",contact.id(),contact.trigger(),isNull(actions)?t("nothing"):actions); if (isNull(actions)) return context; actions.fire(context,"Route.Contact("+contact.addr()+")"); - return context; + Context previousContext = context; + if (context.invalidated()) context = null; // route has been freed in between. + return previousContext; } public Vector contacts() { @@ -380,13 +382,10 @@ public class Route extends BaseClass { private void free() { context.invalidate(); Train train = context.train(); - for (int i=path.size(); i>0; i--) { - Tile tile = path.get(i-1); - if (isSet(train) && !train.onTrace(tile)) { - tile.free(train); - } + Vector reversedPath = reverse(path()); + for (Tile tile : reversedPath) { + if (isSet(train) && !train.onTrace(tile)) tile.free(train); } - context = null; } private String generateName() { @@ -504,7 +503,7 @@ public class Route extends BaseClass { if (json.has(ACTIONS)) { loadActions(json.getJSONObject(ACTIONS)); } - if (json.has("action_lists")) { // TODO: this is legacy! + if (json.has("action_lists")) { // Legacy JSONArray jarr = json.getJSONArray("action_lists"); for (Object o : jarr) { if (o instanceof JSONObject) { @@ -525,7 +524,7 @@ public class Route extends BaseClass { } } } - if (json.has("conditions")) { // TODO: this is legacy! + if (json.has("conditions")) { // Legacy JSONArray jConditions = json.getJSONArray("conditions"); for (Object o : jConditions) { if (o instanceof JSONObject) { @@ -540,7 +539,7 @@ public class Route extends BaseClass { } } if (json.has(CONDITION_LIST)) conditions.load(json.getJSONObject(CONDITION_LIST)).parent(this); - if (json.has(SETUP_ACTIONS)) { // TODO: this is legacy! + if (json.has(SETUP_ACTIONS)) { // Legacy Object so = json.get(SETUP_ACTIONS); if (so instanceof JSONObject) { JSONObject jo = (JSONObject) so; @@ -565,7 +564,7 @@ public class Route extends BaseClass { triggeredActions.put(ROUTE_SETUP, setupActions); } } - if (json.has(START_ACTIONS)) { // TODO: this is legacy! + if (json.has(START_ACTIONS)) { // Legacy Object so = json.get(START_ACTIONS); if (so instanceof JSONObject) { JSONObject jo = (JSONObject) so; @@ -645,9 +644,7 @@ public class Route extends BaseClass { } public Vector path() { - Vector result = new Vector(); - if (isSet(path)) result.addAll(path); - return result; + return isSet(path) ? new Vector<>(path) : new Vector<>(); } public boolean prepareAndLock() { @@ -660,7 +657,7 @@ public class Route extends BaseClass { } for (Tile tile : path) { - if (context.invalidated() || !tile.lockFor(context)) { + if (context.invalidated() || !tile.lockFor(context,false)) { LOG.debug("Was not able to allocate route for {}.",context); return false; } diff --git a/src/main/java/de/srsoftware/web4rail/moving/Train.java b/src/main/java/de/srsoftware/web4rail/moving/Train.java index 03c4393..5e685be 100644 --- a/src/main/java/de/srsoftware/web4rail/moving/Train.java +++ b/src/main/java/de/srsoftware/web4rail/moving/Train.java @@ -10,7 +10,6 @@ import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; -import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.SortedSet; @@ -38,7 +37,6 @@ import de.srsoftware.web4rail.tags.Table; import de.srsoftware.web4rail.tags.Window; import de.srsoftware.web4rail.threads.RouteManager; import de.srsoftware.web4rail.tiles.Block; -import de.srsoftware.web4rail.tiles.BlockContact; import de.srsoftware.web4rail.tiles.Contact; import de.srsoftware.web4rail.tiles.Tile; @@ -84,7 +82,7 @@ public class Train extends BaseClass implements Comparable { private boolean f1,f2,f3,f4; private Block currentBlock,destination = null; - LinkedList trace = new LinkedList(); + HashSet trace = new HashSet(); private Vector lastBlocks = new Vector(); public int speed = 0; @@ -300,9 +298,7 @@ public class Train extends BaseClass implements Comparable { public Context contact(Contact contact) { if (isNull(route)) return new Context(contact).train(this); - Context context = route.contact(contact); - traceFrom(context); - return context; + return updateTrace(route.contact(contact)); } @@ -398,11 +394,17 @@ public class Train extends BaseClass implements Comparable { public void dropTrace() { - while (!trace.isEmpty()) trace.removeFirst().free(this); + trace.forEach(tile -> tile.free(this)); + trace.clear(); } - public void endRoute(Block newBlock, Direction newDirection) { - + public void endRoute(Block endBlock, Direction endDirection) { + setSpeed(0); + route = null; + direction = endDirection; + endBlock.add(this, direction); + currentBlock = endBlock; + trace.add(endBlock); } private Tag faster(int steps) { @@ -713,22 +715,14 @@ public class Train extends BaseClass implements Comparable { public Train reverse() { LOG.debug("train.reverse();"); - if (isSet(direction)) { - direction = direction.inverse(); - reverseTrace(); + if (isSet(direction)) direction = direction.inverse(); + if (isSet(currentBlock)) { + currentBlock.set(this,direction); + plan.place(currentBlock); } - if (isSet(currentBlock)) plan.place(currentBlock); return this; } - private void reverseTrace() { - LinkedList reversed = new LinkedList(); - LOG.debug("Trace: {}",trace); - while (!trace.isEmpty()) reversed.addFirst(trace.removeFirst()); - trace = reversed; - LOG.debug("reversed: {}",trace); - } - public Route route() { return route; } @@ -903,15 +897,6 @@ public class Train extends BaseClass implements Comparable { return name(); } - public void traceFrom(Context context) { - // TOSO: neu implementieren! - // Beachten: Route aus Context, plan.freeBehindTrain - } - - public Tile traceHead() { - return trace == null || trace.isEmpty() ? null : trace.getFirst(); - } - /** * this inverts the direction the train is heading to. Example: * before: CabCar→ MiddleCar→ Loco→ @@ -922,7 +907,7 @@ public class Train extends BaseClass implements Comparable { LOG.debug("{}.turn()",this); setSpeed(0); for (Car car : cars) car.turn(); - Collections.reverse(cars); + reverse(cars); return reverse(); } @@ -941,6 +926,52 @@ public class Train extends BaseClass implements Comparable { } return properties(); } + + public Context updateTrace(Context context) { + // TOSO: neu implementieren! + // Beachten: Route aus Context, plan.freeBehindTrain + LOG.debug("updateTrace({})",context); + Tile from = context.tile(); + if (isNull(from)) from = context.contact(); + if (isNull(from)) { + LOG.debug("no starting point for trace given in {}",context); + return context; + } + trace.add(from); + Route route = context.route(); + LOG.debug("Route: {}",route); + if (isNull(route)) return context; + Vector reversedPath = reverse(route.path()); + HashSet newTrace = new HashSet(); + Integer remainingLength = null; + + for (Tile tile : reversedPath) { + if (isNull(remainingLength) && onTrace(tile)) remainingLength = length(); + if (remainingLength == null) { // ahead of train + LOG.debug("{} is ahead of train and will not be touched.",tile); + trace.remove(tile); // old trace will be cleared afterwards. but this tile shall not be cleared, so remove it from old trace + } else if (remainingLength > 0) { // within train + LOG.debug("{} is occupied by train and will be marked as \"occupied\"",tile); + remainingLength -= tile.length(); + newTrace.add(tile); + trace.remove(tile); // old trace will be cleared afterwards. but this tile shall not be cleared, so remove it from old trace + tile.setTrain(this); + LOG.debug("remaining length: {}",remainingLength); + } else { // behind train + if (Route.freeBehindTrain) { + LOG.debug("{} is behind train and will be freed in the next step",tile); + trace.add(tile); // old trace will be cleared afterwards + } else { + LOG.debug("{} is behind train and will be reset to \"locked\" state",tile); + tile.lockFor(context,true); + trace.remove(tile); // old trace will be cleared afterwards. but this tile shall not be cleared, so remove it from old trace + } + } + } + for (Tile tile : trace) tile.free(this); + trace = newTrace; + return context; + } public boolean usesAutopilot() { return isSet(routeManager) && routeManager.autoEnabled(); diff --git a/src/main/java/de/srsoftware/web4rail/tiles/Block.java b/src/main/java/de/srsoftware/web4rail/tiles/Block.java index d53e0c8..ca8000a 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/Block.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/Block.java @@ -80,6 +80,11 @@ public abstract class Block extends StretchableTile{ return trains.remove(b); } + + public void set(Train train, Direction newDirection) { + dirs.put(train, newDirection); + } + @Override public String toString() { return trains.toString(); @@ -388,7 +393,7 @@ public abstract class Block extends StretchableTile{ } @Override - public boolean lockFor(Context context) { + public boolean lockFor(Context context, boolean downgrade) { Train newTrain = context.train(); Route route = context.route(); LOG.debug("{}.lock({})",this,newTrain); @@ -398,6 +403,8 @@ public abstract class Block extends StretchableTile{ switch (status) { case DISABLED: return false; + case OCCUPIED: + if (!downgrade) break; case FREE: case RESERVED: status = Status.LOCKED; @@ -406,7 +413,6 @@ public abstract class Block extends StretchableTile{ add(newTrain,dir); plan.place(this); break; - case OCCUPIED: case LOCKED: break; // do not downgrade } @@ -482,6 +488,11 @@ public abstract class Block extends StretchableTile{ return routes().stream().filter(route -> route.startBlock() == Block.this).collect(Collectors.toList()); } + public void set(Train train, Direction direction) { + trains.set(train,direction); + } + + public abstract List startPoints(); @Override diff --git a/src/main/java/de/srsoftware/web4rail/tiles/Tile.java b/src/main/java/de/srsoftware/web4rail/tiles/Tile.java index d39b835..318238f 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/Tile.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/Tile.java @@ -478,7 +478,7 @@ public abstract class Tile extends BaseClass implements Comparable { plan.place(this); } - public boolean lockFor(Context context) { + public boolean lockFor(Context context,boolean downgrade) { Train newTrain = context.train(); LOG.debug("{}.lockFor({})",this,newTrain); if (isNull(newTrain)) return false; @@ -486,14 +486,15 @@ public abstract class Tile extends BaseClass implements Comparable { switch (status) { case DISABLED: return false; + case OCCUPIED: + if (!downgrade) break; case FREE: case RESERVED: status = Status.LOCKED; plan.place(this); break; - case OCCUPIED: case LOCKED: - break; // do not downgrade + break; // already locked } return true; } From d86b6dcbcc68c2393af39dd7129a2e21143e2d40 Mon Sep 17 00:00:00 2001 From: Stephan Richter Date: Sun, 14 Mar 2021 16:15:14 +0100 Subject: [PATCH 16/26] implemented handling of stuck trains during route search --- pom.xml | 2 +- src/main/java/de/srsoftware/web4rail/Route.java | 4 +++- .../java/de/srsoftware/web4rail/moving/Train.java | 12 ++++++++++++ .../de/srsoftware/web4rail/threads/RouteManager.java | 8 ++++++++ .../java/de/srsoftware/web4rail/tiles/Block.java | 5 +++-- src/main/java/de/srsoftware/web4rail/tiles/Tile.java | 2 +- 6 files changed, 28 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 1d91752..31af9b3 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 de.srsoftware web4rail - 1.3.66 + 1.3.67 Web4Rail jar Java Model Railway Control diff --git a/src/main/java/de/srsoftware/web4rail/Route.java b/src/main/java/de/srsoftware/web4rail/Route.java index c280d5c..62d4c29 100644 --- a/src/main/java/de/srsoftware/web4rail/Route.java +++ b/src/main/java/de/srsoftware/web4rail/Route.java @@ -380,7 +380,8 @@ public class Route extends BaseClass { } private void free() { - context.invalidate(); + context.invalidate(); // do not set to null: + // this action may be called from route.contact → finishRoute, which calls train.updateTrace afterwards, which in turn requires context Train train = context.train(); Vector reversedPath = reverse(path()); for (Tile tile : reversedPath) { @@ -750,6 +751,7 @@ public class Route extends BaseClass { Train train = context.train(); free(); train.drop(this); + context = null; return true; } diff --git a/src/main/java/de/srsoftware/web4rail/moving/Train.java b/src/main/java/de/srsoftware/web4rail/moving/Train.java index 5e685be..357a0c9 100644 --- a/src/main/java/de/srsoftware/web4rail/moving/Train.java +++ b/src/main/java/de/srsoftware/web4rail/moving/Train.java @@ -90,6 +90,8 @@ public class Train extends BaseClass implements Comparable { private boolean shunting = false; private RouteManager routeManager = null; + private HashSet stuckTrace = null; + public static Object action(HashMap params, Plan plan) throws IOException { String action = params.get(ACTION); if (isNull(action)) return t("No action passed to Train.action!"); @@ -405,6 +407,7 @@ public class Train extends BaseClass implements Comparable { endBlock.add(this, direction); currentBlock = endBlock; trace.add(endBlock); + stuckTrace = null; } private Tag faster(int steps) { @@ -866,10 +869,19 @@ public class Train extends BaseClass implements Comparable { setSpeed(0); quitAutopilot(); if (isSet(route)) { + stuckTrace = new HashSet(); + for (Tile tile : route.path()) { // collect occupied tiles of route. stuckTrace is considered during next route search + if (trace.contains(tile)) stuckTrace.add(tile); + } route.reset(); } return properties(); } + + public HashSet stuckTrace() { + return stuckTrace; + } + public SortedSet tags() { TreeSet list = new TreeSet(tags); diff --git a/src/main/java/de/srsoftware/web4rail/threads/RouteManager.java b/src/main/java/de/srsoftware/web4rail/threads/RouteManager.java index e12a1a7..ca811cb 100644 --- a/src/main/java/de/srsoftware/web4rail/threads/RouteManager.java +++ b/src/main/java/de/srsoftware/web4rail/threads/RouteManager.java @@ -14,6 +14,7 @@ import de.srsoftware.web4rail.Plan.Direction; import de.srsoftware.web4rail.Route; import de.srsoftware.web4rail.moving.Train; import de.srsoftware.web4rail.tiles.Block; +import de.srsoftware.web4rail.tiles.Tile; public class RouteManager extends BaseClass implements Runnable { @@ -66,6 +67,13 @@ public class RouteManager extends BaseClass implements Runnable { LOG.debug("{}→ Candidate {} would create loop, skipping",inset,routeCandidate.shortName()); continue; } + + HashSet stuckTrace = train.stuckTrace(); // if train has been stopped in between two blocks lastly: only allow routes that do not conflict with current train position + if (isSet(stuckTrace) && !routeCandidate.path().containsAll(stuckTrace)) { + LOG.debug("Stuck train occupies tiles ({}) outside of {} – not allowed.",stuckTrace,routeCandidate); + continue; + } + if (!routeCandidate.allowed(context)) { if (routeCandidate.endBlock() != destination) { // allowance may be overridden by destination LOG.debug("{} not allowed for {}",routeCandidate,context); diff --git a/src/main/java/de/srsoftware/web4rail/tiles/Block.java b/src/main/java/de/srsoftware/web4rail/tiles/Block.java index ca8000a..e2865d6 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/Block.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/Block.java @@ -278,8 +278,9 @@ public abstract class Block extends StretchableTile{ if (isNull(firstTrain)) return true; if (firstTrain != train) return false; trains.remove(train); - status = trains.isEmpty() ? Status.FREE : Status.OCCUPIED; - plan.place(this); + if (isSet(firstTrain)) { + super.free(train); + } else super.setTrain(firstTrain); return true; } diff --git a/src/main/java/de/srsoftware/web4rail/tiles/Tile.java b/src/main/java/de/srsoftware/web4rail/tiles/Tile.java index 318238f..992243d 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/Tile.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/Tile.java @@ -106,7 +106,7 @@ public abstract class Tile extends BaseClass implements Comparable { } public boolean free(Train t) { - if (t != train) return false; + if (isSet(train) && t != train) return false; train = null; status = Status.FREE; plan.place(this); From 9c86955d8d207d526fb5206b914ce25c382be64d Mon Sep 17 00:00:00 2001 From: Stephan Richter Date: Sun, 14 Mar 2021 19:12:17 +0100 Subject: [PATCH 17/26] - implemented load callback - added waitTime field to context, wait time now determined in route.start --- pom.xml | 2 +- .../de/srsoftware/web4rail/BaseClass.java | 12 +++ .../de/srsoftware/web4rail/LoadCallback.java | 18 ++++ .../java/de/srsoftware/web4rail/Plan.java | 21 +++-- .../java/de/srsoftware/web4rail/Route.java | 6 +- .../actions/AddRemoveDestination.java | 20 ++--- .../actions/DetermineTrainInBlock.java | 19 ++--- .../web4rail/actions/DisableEnableBlock.java | 27 ++---- .../web4rail/actions/EngageDecoupler.java | 56 ++++++------- .../web4rail/actions/PreserveRoute.java | 7 +- .../web4rail/actions/SetContextTrain.java | 22 ++--- .../web4rail/actions/SetDisplayText.java | 17 ++-- .../web4rail/actions/SetRelayOrSwitch.java | 42 ++++------ .../web4rail/actions/SetTurnout.java | 23 ++--- .../web4rail/actions/WaitForContact.java | 20 ++--- .../web4rail/conditions/BlockFree.java | 21 ++--- .../web4rail/conditions/RouteEndBlock.java | 25 ++---- .../web4rail/conditions/SwitchIsOn.java | 21 ++--- .../de/srsoftware/web4rail/moving/Train.java | 24 ++++-- .../web4rail/threads/RouteManager.java | 17 ++-- .../de/srsoftware/web4rail/tiles/Block.java | 83 +++++++++++-------- .../de/srsoftware/web4rail/tiles/Bridge.java | 16 ++-- 22 files changed, 253 insertions(+), 266 deletions(-) create mode 100644 src/main/java/de/srsoftware/web4rail/LoadCallback.java diff --git a/pom.xml b/pom.xml index 31af9b3..384da23 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 de.srsoftware web4rail - 1.3.67 + 1.3.68 Web4Rail jar Java Model Railway Control diff --git a/src/main/java/de/srsoftware/web4rail/BaseClass.java b/src/main/java/de/srsoftware/web4rail/BaseClass.java index c1a0e28..3d3689a 100644 --- a/src/main/java/de/srsoftware/web4rail/BaseClass.java +++ b/src/main/java/de/srsoftware/web4rail/BaseClass.java @@ -68,6 +68,7 @@ public abstract class BaseClass implements Constants{ private Car car; private Contact contact; private Direction direction; + private Integer waitTime; public Context(BaseClass object) { setMain(object); @@ -102,6 +103,7 @@ public abstract class BaseClass implements Constants{ route = null; tile = null; train = null; + waitTime = null; } public Context clone() { @@ -115,6 +117,7 @@ public abstract class BaseClass implements Constants{ clone.route = route; clone.tile = tile; clone.train = train; + clone.waitTime = waitTime; return clone; } @@ -187,6 +190,7 @@ public abstract class BaseClass implements Constants{ if (isSet(block)) sb.append(", "+t("Block: {}",block)); if (isSet(route)) sb.append(", "+t("Route: {}",route)); if (isSet(contact)) sb.append(", "+t("Contact: {}",contact)); + if (isSet(waitTime)) sb.append(", "+t("Wait time: {} ms",waitTime)); sb.append(")"); return sb.toString(); } @@ -200,6 +204,14 @@ public abstract class BaseClass implements Constants{ train = newTrain; return this; } + + public Integer waitTime() { + return waitTime; + } + + public void waitTime(int ms) { + waitTime = ms; + } } public class FormInput extends ArrayList>{ diff --git a/src/main/java/de/srsoftware/web4rail/LoadCallback.java b/src/main/java/de/srsoftware/web4rail/LoadCallback.java new file mode 100644 index 0000000..945262a --- /dev/null +++ b/src/main/java/de/srsoftware/web4rail/LoadCallback.java @@ -0,0 +1,18 @@ +package de.srsoftware.web4rail; + +import java.util.LinkedList; + +public abstract class LoadCallback { + + private static LinkedList callbacks = new LinkedList(); + + public LoadCallback() { + callbacks.add(this); + } + + public abstract void afterLoad(); + + public static void fire() { + for (LoadCallback callback : callbacks) callback.afterLoad(); + } +} diff --git a/src/main/java/de/srsoftware/web4rail/Plan.java b/src/main/java/de/srsoftware/web4rail/Plan.java index 4240ec4..bed753e 100644 --- a/src/main/java/de/srsoftware/web4rail/Plan.java +++ b/src/main/java/de/srsoftware/web4rail/Plan.java @@ -513,25 +513,29 @@ public class Plan extends BaseClass{ public static void load(String name) throws IOException { plan = new Plan(); plan.name = name; + + String content = new String(Files.readAllBytes(new File(name+".plan").toPath()),UTF8); + JSONObject json = new JSONObject(content); + + if (json.has(LENGTH_UNIT)) lengthUnit = json.getString(LENGTH_UNIT); + if (json.has(SPEED_UNIT)) speedUnit = json.getString(SPEED_UNIT); + if (json.has(FINAL_SPEED)) Train.defaultEndSpeed = json.getInt(FINAL_SPEED); + if (json.has(FREE_BEHIND_TRAIN)) Route.freeBehindTrain = json.getBoolean(FREE_BEHIND_TRAIN); + try { Car.loadAll(name+".cars",plan); } catch (Exception e) { LOG.warn("Was not able to load cars!",e); } - String content = new String(Files.readAllBytes(new File(name+".plan").toPath()),UTF8); - JSONObject json = new JSONObject(content); - if (json.has(TILE)) json.getJSONArray(TILE).forEach(object -> Tile.load(object, plan)); - if (json.has(LENGTH_UNIT)) lengthUnit = json.getString(LENGTH_UNIT); - if (json.has(SPEED_UNIT)) speedUnit = json.getString(SPEED_UNIT); - if (json.has(FINAL_SPEED)) Train.defaultEndSpeed = json.getInt(FINAL_SPEED); - if (json.has(FREE_BEHIND_TRAIN)) Route.freeBehindTrain = json.getBoolean(FREE_BEHIND_TRAIN); - try { Train.loadAll(name+".trains",plan); } catch (Exception e) { LOG.warn("Was not able to load trains!",e); } + + if (json.has(TILE)) json.getJSONArray(TILE).forEach(object -> Tile.load(object, plan)); + try { Route.loadAll(name+".routes",plan); } catch (Exception e) { @@ -547,6 +551,7 @@ public class Plan extends BaseClass{ } catch (Exception e) { LOG.warn("Was not able to establish connection to control unit!"); } + LoadCallback.fire(); } /** diff --git a/src/main/java/de/srsoftware/web4rail/Route.java b/src/main/java/de/srsoftware/web4rail/Route.java index 62d4c29..b029928 100644 --- a/src/main/java/de/srsoftware/web4rail/Route.java +++ b/src/main/java/de/srsoftware/web4rail/Route.java @@ -790,7 +790,7 @@ public class Route extends BaseClass { public Route simplyfyName() { String[] parts = name().split("-"); - if (parts.length>1) name(parts[0]+" - "+parts[parts.length-1]); + if (parts.length>1) name(parts[0].trim()+" - "+parts[parts.length-1].trim()); return this; } @@ -802,6 +802,8 @@ public class Route extends BaseClass { if (isNull(train)) return false; // can't set route's train to null train.setRoute(this); // set new train + triggeredContacts.clear(); + ActionList startActions = triggeredActions.get(ROUTE_START); if (isSet(startActions)) { @@ -810,7 +812,7 @@ public class Route extends BaseClass { if (!startActions.fire(context,cause)) return false; // start actions failed } - triggeredContacts.clear(); + context.waitTime(endBlock.getWaitTime(train, endDirection).random()); return true; } diff --git a/src/main/java/de/srsoftware/web4rail/actions/AddRemoveDestination.java b/src/main/java/de/srsoftware/web4rail/actions/AddRemoveDestination.java index 96a8698..1fead2e 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/AddRemoveDestination.java +++ b/src/main/java/de/srsoftware/web4rail/actions/AddRemoveDestination.java @@ -9,11 +9,11 @@ import org.json.JSONObject; import de.srsoftware.tools.Tag; import de.srsoftware.web4rail.BaseClass; +import de.srsoftware.web4rail.LoadCallback; import de.srsoftware.web4rail.moving.Train; import de.srsoftware.web4rail.tags.Checkbox; import de.srsoftware.web4rail.tags.Fieldset; import de.srsoftware.web4rail.tags.Window; -import de.srsoftware.web4rail.threads.DelayedExecution; import de.srsoftware.web4rail.tiles.Block; import de.srsoftware.web4rail.tiles.Tile; @@ -69,19 +69,13 @@ public class AddRemoveDestination extends Action { public Action load(JSONObject json) { if (json.has(TURN)) turnAtDestination = json.getBoolean(TURN); if (json.has(SHUNTING)) shunting = json.getBoolean(SHUNTING); - if (json.has(Train.DESTINATION)) { - Id blockId = new Id(json.getString(Train.DESTINATION)); - destination = BaseClass.get(blockId); - if (isNull(destination)) { - new DelayedExecution(this) { - - @Override - public void execute() { - destination = BaseClass.get(blockId); - } - }; + if (json.has(Train.DESTINATION)) new LoadCallback() { + @Override + public void afterLoad() { + destination = BaseClass.get(Id.from(json, Train.DESTINATION)); } - } + }; + return super.load(json); } diff --git a/src/main/java/de/srsoftware/web4rail/actions/DetermineTrainInBlock.java b/src/main/java/de/srsoftware/web4rail/actions/DetermineTrainInBlock.java index 1c70ef7..ed71f16 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/DetermineTrainInBlock.java +++ b/src/main/java/de/srsoftware/web4rail/actions/DetermineTrainInBlock.java @@ -7,9 +7,9 @@ import java.util.Map; import org.json.JSONObject; import de.srsoftware.web4rail.BaseClass; +import de.srsoftware.web4rail.LoadCallback; import de.srsoftware.web4rail.tags.Fieldset; import de.srsoftware.web4rail.tags.Window; -import de.srsoftware.web4rail.threads.DelayedExecution; import de.srsoftware.web4rail.tiles.Block; import de.srsoftware.web4rail.tiles.Tile; @@ -39,18 +39,13 @@ public class DetermineTrainInBlock extends Action { public Action load(JSONObject json) { super.load(json); Id blockId = Id.from(json,BLOCK); - if (isSet(blockId)) { - block = Block.get(blockId); - if (isNull(block)) { - new DelayedExecution(this) { - - @Override - public void execute() { - block = Block.get(blockId); - } - }; + if (isSet(blockId)) new LoadCallback() { + + @Override + public void afterLoad() { + block = Block.get(blockId); } - } + }; return this; } diff --git a/src/main/java/de/srsoftware/web4rail/actions/DisableEnableBlock.java b/src/main/java/de/srsoftware/web4rail/actions/DisableEnableBlock.java index a84f217..e022403 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/DisableEnableBlock.java +++ b/src/main/java/de/srsoftware/web4rail/actions/DisableEnableBlock.java @@ -8,10 +8,10 @@ import org.json.JSONObject; import de.srsoftware.tools.Tag; import de.srsoftware.web4rail.BaseClass; +import de.srsoftware.web4rail.LoadCallback; import de.srsoftware.web4rail.tags.Fieldset; import de.srsoftware.web4rail.tags.Radio; import de.srsoftware.web4rail.tags.Window; -import de.srsoftware.web4rail.threads.DelayedExecution; import de.srsoftware.web4rail.tiles.Block; import de.srsoftware.web4rail.tiles.Shadow; import de.srsoftware.web4rail.tiles.Tile; @@ -43,24 +43,15 @@ public class DisableEnableBlock extends Action { @Override public Action load(JSONObject json) { - super.load(json); - Id blockId = Id.from(json,BLOCK); - if (isSet(blockId)) { - block = Block.get(blockId); - if (isNull(block)) { - new DelayedExecution(this) { - - @Override - public void execute() { - block = Block.get(blockId); - } - }; + if (json.has(STATE)) disable = !json.getBoolean(STATE); + if (json.has(BLOCK)) new LoadCallback() { + @Override + public void afterLoad() { + block = Block.get(Id.from(json,BLOCK)); } - } - if (json.has(STATE)) { - disable = !json.getBoolean(STATE); - } - return this; + }; + return super.load(json); + } @Override diff --git a/src/main/java/de/srsoftware/web4rail/actions/EngageDecoupler.java b/src/main/java/de/srsoftware/web4rail/actions/EngageDecoupler.java index 7152a03..240cc44 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/EngageDecoupler.java +++ b/src/main/java/de/srsoftware/web4rail/actions/EngageDecoupler.java @@ -7,14 +7,14 @@ import java.util.Map; import org.json.JSONObject; import de.srsoftware.web4rail.BaseClass; +import de.srsoftware.web4rail.LoadCallback; import de.srsoftware.web4rail.tags.Fieldset; import de.srsoftware.web4rail.tags.Window; -import de.srsoftware.web4rail.threads.DelayedExecution; import de.srsoftware.web4rail.tiles.Decoupler; import de.srsoftware.web4rail.tiles.Tile; public class EngageDecoupler extends Action { - + private static final String DECOUPLER = Decoupler.class.getSimpleName(); public EngageDecoupler(BaseClass parent) { @@ -24,12 +24,12 @@ public class EngageDecoupler extends Action { private Decoupler decoupler = null; @Override - public boolean fire(Context context,Object cause) { + public boolean fire(Context context, Object cause) { if (isNull(decoupler)) return false; decoupler.engage(); return true; } - + @Override public JSONObject json() { JSONObject json = super.json(); @@ -38,50 +38,44 @@ public class EngageDecoupler extends Action { } return json; } - + @Override public Action load(JSONObject json) { - super.load(json); - if (json.has(DECOUPLER)) { - String decouplerId = json.getString(DECOUPLER); - decoupler = BaseClass.get(new Id(decouplerId)); - if (isNull(decoupler)) new DelayedExecution(this) { - - @Override - public void execute() { - decoupler = BaseClass.get(new Id(decouplerId)); - } - }; - } - return this; + if (json.has(DECOUPLER)) new LoadCallback() { + @Override + public void afterLoad() { + decoupler = BaseClass.get(Id.from(json, DECOUPLER)); + } + }; + return super.load(json); } - + @Override - protected Window properties(List
preForm, FormInput formInputs, List
postForm,String...errors) { - formInputs.add(t("Decoupler")+": "+(isNull(decoupler) ? t("unset") : decoupler),button(t("Select from plan"),Map.of(ACTION,ACTION_UPDATE,ASSIGN,DECOUPLER))); - - return super.properties(preForm, formInputs, postForm,errors); + protected Window properties(List
preForm, FormInput formInputs, List
postForm, String... errors) { + formInputs.add(t("Decoupler") + ": " + (isNull(decoupler) ? t("unset") : decoupler), button(t("Select from plan"), Map.of(ACTION, ACTION_UPDATE, ASSIGN, DECOUPLER))); + + return super.properties(preForm, formInputs, postForm, errors); } - + @Override protected void removeChild(BaseClass child) { if (child == decoupler) decoupler = null; super.removeChild(child); } - + public String toString() { - if (isNull(decoupler)) return "["+t("Click here to setup decoupler")+"]"; - return t("Engage {}",decoupler); + if (isNull(decoupler)) return "[" + t("Click here to setup decoupler") + "]"; + return t("Engage {}", decoupler); }; - + @Override protected Object update(HashMap params) { - LOG.debug("update: {}",params); + LOG.debug("update: {}", params); if (params.containsKey(DECOUPLER)) { Tile tile = BaseClass.get(new Id(params.get(DECOUPLER))); if (tile instanceof Decoupler) { - decoupler = (Decoupler) tile; - } else return t("Clicked tile is not a {}!",t("decoupler")); + decoupler = (Decoupler) tile; + } else return t("Clicked tile is not a {}!", t("decoupler")); } return context().properties(); } diff --git a/src/main/java/de/srsoftware/web4rail/actions/PreserveRoute.java b/src/main/java/de/srsoftware/web4rail/actions/PreserveRoute.java index a4f4670..20b49d0 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/PreserveRoute.java +++ b/src/main/java/de/srsoftware/web4rail/actions/PreserveRoute.java @@ -1,7 +1,6 @@ package de.srsoftware.web4rail.actions; import de.srsoftware.web4rail.BaseClass; -import de.srsoftware.web4rail.Range; import de.srsoftware.web4rail.Route; import de.srsoftware.web4rail.moving.Train; import de.srsoftware.web4rail.tiles.Block; @@ -25,10 +24,10 @@ public class PreserveRoute extends Action { Block endBlock = route.endBlock(); if (train.destination() == endBlock) return true; // do not reserve routes, when destination has been reached - Range waitTime = endBlock.getWaitTime(train,route.endDirection); - if (waitTime.max > 0) { + Integer waitTime = context.waitTime(); + if (isSet(waitTime) && waitTime > 0) { LOG.debug("Not preserving route, as train needs to stop for {} ms at {}!",waitTime,endBlock); - return true; // train is expected to wait in next block. + return false; // train is expected to wait in next block. } train.reserveNext(); diff --git a/src/main/java/de/srsoftware/web4rail/actions/SetContextTrain.java b/src/main/java/de/srsoftware/web4rail/actions/SetContextTrain.java index 6cb6a70..d28407d 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/SetContextTrain.java +++ b/src/main/java/de/srsoftware/web4rail/actions/SetContextTrain.java @@ -6,10 +6,10 @@ import java.util.List; import org.json.JSONObject; import de.srsoftware.web4rail.BaseClass; +import de.srsoftware.web4rail.LoadCallback; import de.srsoftware.web4rail.moving.Train; import de.srsoftware.web4rail.tags.Fieldset; import de.srsoftware.web4rail.tags.Window; -import de.srsoftware.web4rail.threads.DelayedExecution; public class SetContextTrain extends Action { @@ -34,21 +34,13 @@ public class SetContextTrain extends Action { @Override public Action load(JSONObject json) { - super.load(json); - if (json.has(REALM_TRAIN)) { - Id trainId = Id.from(json,REALM_TRAIN); - if (isSet(trainId)) { - train = Train.get(trainId); - if (isNull(train)) new DelayedExecution(this) { - - @Override - public void execute() { - train = Train.get(trainId); - } - }; + if (json.has(REALM_TRAIN)) new LoadCallback() { + @Override + public void afterLoad() { + train = Train.get(Id.from(json,REALM_TRAIN)); } - } - return this; + }; + return super.load(json); } @Override diff --git a/src/main/java/de/srsoftware/web4rail/actions/SetDisplayText.java b/src/main/java/de/srsoftware/web4rail/actions/SetDisplayText.java index 1818331..8353483 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/SetDisplayText.java +++ b/src/main/java/de/srsoftware/web4rail/actions/SetDisplayText.java @@ -7,10 +7,10 @@ import java.util.Map; import org.json.JSONObject; import de.srsoftware.web4rail.BaseClass; +import de.srsoftware.web4rail.LoadCallback; import de.srsoftware.web4rail.tags.Fieldset; import de.srsoftware.web4rail.tags.Label; import de.srsoftware.web4rail.tags.Window; -import de.srsoftware.web4rail.threads.DelayedExecution; import de.srsoftware.web4rail.tiles.TextDisplay; import de.srsoftware.web4rail.tiles.Tile; @@ -43,15 +43,12 @@ public class SetDisplayText extends TextAction{ @Override public Action load(JSONObject json) { - if (json.has(DISPLAY)) { - new DelayedExecution(this) { - - @Override - public void execute() { - display = (TextDisplay) plan.get(Id.from(json,DISPLAY), false); - }; - }; - } + if (json.has(DISPLAY)) new LoadCallback() { + @Override + public void afterLoad() { + display = (TextDisplay) plan.get(Id.from(json,DISPLAY), false); + } + }; return super.load(json); } diff --git a/src/main/java/de/srsoftware/web4rail/actions/SetRelayOrSwitch.java b/src/main/java/de/srsoftware/web4rail/actions/SetRelayOrSwitch.java index a437833..8dd21ba 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/SetRelayOrSwitch.java +++ b/src/main/java/de/srsoftware/web4rail/actions/SetRelayOrSwitch.java @@ -8,10 +8,10 @@ import org.json.JSONObject; import de.srsoftware.tools.Tag; import de.srsoftware.web4rail.BaseClass; +import de.srsoftware.web4rail.LoadCallback; import de.srsoftware.web4rail.tags.Fieldset; import de.srsoftware.web4rail.tags.Select; import de.srsoftware.web4rail.tags.Window; -import de.srsoftware.web4rail.threads.DelayedExecution; import de.srsoftware.web4rail.tiles.Relay; import de.srsoftware.web4rail.tiles.Switch; import de.srsoftware.web4rail.tiles.Tile; @@ -51,31 +51,23 @@ public class SetRelayOrSwitch extends Action { @Override public Action load(JSONObject json) { - super.load(json); - if (json.has(RELAY)) { - String relayId = json.getString(RELAY); - relayOrSwitch = BaseClass.get(new Id(relayId)); - if (isNull(relayOrSwitch)) new DelayedExecution(this) { - - @Override - public void execute() { - relayOrSwitch = BaseClass.get(new Id(relayId)); - }; - }; - } - if (json.has(SWITCH)) { - String relayId = json.getString(SWITCH); - relayOrSwitch = BaseClass.get(new Id(relayId)); - if (isNull(relayOrSwitch)) new DelayedExecution(this) { - - @Override - public void execute() { - relayOrSwitch = BaseClass.get(new Id(relayId)); - } - }; - } if (json.has(STATE)) state = json.getBoolean(STATE); - return this; + + if (json.has(RELAY)) new LoadCallback() { + @Override + public void afterLoad() { + relayOrSwitch = BaseClass.get(Id.from(json, RELAY)); + }; + }; + + if (json.has(SWITCH)) new LoadCallback() { + @Override + public void afterLoad() { + relayOrSwitch = BaseClass.get(Id.from(json, SWITCH)); + }; + }; + + return super.load(json); } @Override diff --git a/src/main/java/de/srsoftware/web4rail/actions/SetTurnout.java b/src/main/java/de/srsoftware/web4rail/actions/SetTurnout.java index f8cd546..07d1c0f 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/SetTurnout.java +++ b/src/main/java/de/srsoftware/web4rail/actions/SetTurnout.java @@ -8,10 +8,10 @@ import org.json.JSONObject; import de.srsoftware.tools.Tag; import de.srsoftware.web4rail.BaseClass; +import de.srsoftware.web4rail.LoadCallback; import de.srsoftware.web4rail.tags.Fieldset; import de.srsoftware.web4rail.tags.Select; import de.srsoftware.web4rail.tags.Window; -import de.srsoftware.web4rail.threads.DelayedExecution; import de.srsoftware.web4rail.tiles.Turnout; import de.srsoftware.web4rail.tiles.Turnout.State; @@ -49,20 +49,15 @@ public class SetTurnout extends Action { @Override public Action load(JSONObject json) { - super.load(json); - Id turnoutId = json.has(TURNOUT) ? new Id(json.getString(TURNOUT)) : null; - if (isSet(turnoutId)) { - turnout = BaseClass.get(turnoutId); - if (isNull(turnout)) new DelayedExecution(this) { - - @Override - public void execute() { - turnout = BaseClass.get(turnoutId); - } - }; - } if (json.has(Turnout.STATE)) state = Turnout.State.valueOf(json.getString(Turnout.STATE)); - return this; + if (json.has(TURNOUT)) new LoadCallback() { + + @Override + public void afterLoad() { + turnout = BaseClass.get(Id.from(json, TURNOUT)); + } + }; + return super.load(json); } @Override diff --git a/src/main/java/de/srsoftware/web4rail/actions/WaitForContact.java b/src/main/java/de/srsoftware/web4rail/actions/WaitForContact.java index a33083c..f5f9c83 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/WaitForContact.java +++ b/src/main/java/de/srsoftware/web4rail/actions/WaitForContact.java @@ -8,6 +8,7 @@ import org.json.JSONObject; import de.srsoftware.tools.Tag; import de.srsoftware.web4rail.BaseClass; +import de.srsoftware.web4rail.LoadCallback; import de.srsoftware.web4rail.tags.Fieldset; import de.srsoftware.web4rail.tags.Input; import de.srsoftware.web4rail.tags.Window; @@ -82,19 +83,16 @@ public class WaitForContact extends ActionList { @Override public Action load(JSONObject json) { - if (json.has(CONTACT)) { - String cid = json.getString(CONTACT); - contact = BaseClass.get(new Id(cid)); - if (isNull(contact)) new DelayedExecution(this) { - - @Override - public void execute() { - contact = BaseClass.get(new Id(cid)); - } - }; - } if (json.has(TIMEOUT)) timeout = json.getInt(TIMEOUT); if (json.has(TIMEOUT_ACTIONS)) timeoutActions.load(json.getJSONObject(TIMEOUT_ACTIONS)); + + if (json.has(CONTACT)) new LoadCallback() { + + @Override + public void afterLoad() { + contact = BaseClass.get(Id.from(json, CONTACT)); + } + }; return super.load(json); } diff --git a/src/main/java/de/srsoftware/web4rail/conditions/BlockFree.java b/src/main/java/de/srsoftware/web4rail/conditions/BlockFree.java index 8b6152b..6cb9d99 100644 --- a/src/main/java/de/srsoftware/web4rail/conditions/BlockFree.java +++ b/src/main/java/de/srsoftware/web4rail/conditions/BlockFree.java @@ -7,9 +7,9 @@ import java.util.Map; import org.json.JSONObject; import de.srsoftware.web4rail.BaseClass; +import de.srsoftware.web4rail.LoadCallback; import de.srsoftware.web4rail.tags.Fieldset; import de.srsoftware.web4rail.tags.Window; -import de.srsoftware.web4rail.threads.DelayedExecution; import de.srsoftware.web4rail.tiles.Block; import de.srsoftware.web4rail.tiles.Tile; @@ -34,20 +34,13 @@ public class BlockFree extends Condition { } public Condition load(JSONObject json) { - super.load(json); - if (json.has(BLOCK)) { - Id bid = new Id(json.getString(BLOCK)); - block(BaseClass.get(bid)); - if (isNull(block)) { - new DelayedExecution(this) { - @Override - public void execute() { - block(BaseClass.get(bid)); - } - }; + if (json.has(BLOCK)) new LoadCallback() { + @Override + public void afterLoad() { + block(BaseClass.get(Id.from(json, BLOCK))); } - } - return this; + }; + return super.load(json); } @Override diff --git a/src/main/java/de/srsoftware/web4rail/conditions/RouteEndBlock.java b/src/main/java/de/srsoftware/web4rail/conditions/RouteEndBlock.java index a055459..6451f8e 100644 --- a/src/main/java/de/srsoftware/web4rail/conditions/RouteEndBlock.java +++ b/src/main/java/de/srsoftware/web4rail/conditions/RouteEndBlock.java @@ -7,10 +7,10 @@ import java.util.Map; import org.json.JSONObject; import de.srsoftware.web4rail.BaseClass; +import de.srsoftware.web4rail.LoadCallback; import de.srsoftware.web4rail.Route; import de.srsoftware.web4rail.tags.Fieldset; import de.srsoftware.web4rail.tags.Window; -import de.srsoftware.web4rail.threads.DelayedExecution; import de.srsoftware.web4rail.tiles.Block; import de.srsoftware.web4rail.tiles.Tile; @@ -38,22 +38,15 @@ public class RouteEndBlock extends Condition{ } public Condition load(JSONObject json) { - super.load(json); - Id bid = new Id(json.getString(BLOCK)); - Block block = BaseClass.get(bid); - if (isSet(block)) { - block(block); - } else { - new DelayedExecution(this) { - - @Override - public void execute() { - block(BaseClass.get(bid)); - } - }; - } + new LoadCallback() { + + @Override + public void afterLoad() { + block(BaseClass.get(Id.from(json, BLOCK))); + } + }; - return this; + return super.load(json); } @Override diff --git a/src/main/java/de/srsoftware/web4rail/conditions/SwitchIsOn.java b/src/main/java/de/srsoftware/web4rail/conditions/SwitchIsOn.java index 69f2c92..54eba23 100644 --- a/src/main/java/de/srsoftware/web4rail/conditions/SwitchIsOn.java +++ b/src/main/java/de/srsoftware/web4rail/conditions/SwitchIsOn.java @@ -8,10 +8,10 @@ import org.json.JSONObject; import de.srsoftware.tools.Tag; import de.srsoftware.web4rail.BaseClass; +import de.srsoftware.web4rail.LoadCallback; import de.srsoftware.web4rail.tags.Fieldset; import de.srsoftware.web4rail.tags.Radio; import de.srsoftware.web4rail.tags.Window; -import de.srsoftware.web4rail.threads.DelayedExecution; import de.srsoftware.web4rail.tiles.Switch; import de.srsoftware.web4rail.tiles.Tile; @@ -44,20 +44,13 @@ public class SwitchIsOn extends Condition { } public Condition load(JSONObject json) { - super.load(json); - if (json.has(SWITCH)) { - swtch = BaseClass.get(new Id(json.getString(SWITCH))); - if (isNull(swtch)) { - new DelayedExecution(this) { - - @Override - public void execute() { - swtch = BaseClass.get(new Id(json.getString(SWITCH))); - } - }; + if (json.has(SWITCH)) new LoadCallback() { + @Override + public void afterLoad() { + swtch = BaseClass.get(Id.from(json,SWITCH)); } - } - return this; + }; + return super.load(json); } @Override diff --git a/src/main/java/de/srsoftware/web4rail/moving/Train.java b/src/main/java/de/srsoftware/web4rail/moving/Train.java index 357a0c9..3892bdf 100644 --- a/src/main/java/de/srsoftware/web4rail/moving/Train.java +++ b/src/main/java/de/srsoftware/web4rail/moving/Train.java @@ -23,6 +23,7 @@ import org.slf4j.LoggerFactory; import de.srsoftware.tools.Tag; import de.srsoftware.web4rail.BaseClass; +import de.srsoftware.web4rail.LoadCallback; import de.srsoftware.web4rail.Plan; import de.srsoftware.web4rail.Plan.Direction; import de.srsoftware.web4rail.Route; @@ -514,18 +515,23 @@ public class Train extends BaseClass implements Comparable { if (json.has(DIRECTION)) direction = Direction.valueOf(json.getString(DIRECTION)); if (json.has(NAME)) name = json.getString(NAME); if (json.has(TAGS)) json.getJSONArray(TAGS ).forEach(elem -> { tags.add(elem.toString()); }); - if (json.has(TRACE)) json.getJSONArray(TRACE).forEach(elem -> { - Tile tile = plan.get(new Id(elem.toString()), false); - if (tile.setTrain(this)) trace.add(tile); - }); - if (json.has(BLOCK)) {// do not move this up! during set, other fields will be referenced! - currentBlock = (Block) plan.get(new Id(json.getString(BLOCK)), false); - currentBlock.add(this, direction); - } if (json.has(LOCOS)) { // for downward compatibility for (Object id : json.getJSONArray(LOCOS)) add(BaseClass.get(new Id(""+id))); } for (Object id : json.getJSONArray(CARS)) add(BaseClass.get(new Id(""+id))); + new LoadCallback() { + @Override + public void afterLoad() { + if (json.has(TRACE)) json.getJSONArray(TRACE).forEach(elem -> { + Tile tile = plan.get(new Id(elem.toString()), false); + if (tile.setTrain(Train.this)) trace.add(tile); + }); + if (json.has(BLOCK)) {// do not move this up! during set, other fields will be referenced! + currentBlock = (Block) plan.get(Id.from(json, BLOCK), false); + if (isSet(currentBlock)) currentBlock.add(Train.this, direction); + } + } + }; super.load(json); return this; } @@ -853,7 +859,7 @@ public class Train extends BaseClass implements Comparable { public String start(boolean auto) { if (isNull(routeManager)) routeManager = new RouteManager(this); - routeManager.setAuto(auto); + routeManager.setAuto(auto).start(); plan.stream(t("Started {}",this)); return null; } diff --git a/src/main/java/de/srsoftware/web4rail/threads/RouteManager.java b/src/main/java/de/srsoftware/web4rail/threads/RouteManager.java index ca811cb..1d01de2 100644 --- a/src/main/java/de/srsoftware/web4rail/threads/RouteManager.java +++ b/src/main/java/de/srsoftware/web4rail/threads/RouteManager.java @@ -31,9 +31,6 @@ public class RouteManager extends BaseClass implements Runnable { public RouteManager(Train train) { context = new Context(train); state = State.STARTED; - Thread thread = new Thread(this); - thread.setName(train.name()); - thread.start(); } private static TreeMap> availableRoutes(Context context,HashSet visitedRoutes){ @@ -61,7 +58,7 @@ public class RouteManager extends BaseClass implements Runnable { if (isSet(destination) && visitedRoutes.isEmpty()) LOG.debug("{}- Destination: {}",inset,destination); - for (Route routeCandidate : block.routes(startDirection)) { + for (Route routeCandidate : block.leavingRoutes()) { if (context.invalidated()) return availableRoutes; if (visitedRoutes.contains(routeCandidate)) { LOG.debug("{}→ Candidate {} would create loop, skipping",inset,routeCandidate.shortName()); @@ -179,7 +176,8 @@ public class RouteManager extends BaseClass implements Runnable { try { do { pause(); - if (context.invalidated()) return; + if (isSet(train.route())) continue; + if (context.invalidated()) return; context.block(train.currentBlock()).direction(train.direction()); Route route = chooseRoute(context); if (isNull(route)) continue; @@ -205,8 +203,15 @@ public class RouteManager extends BaseClass implements Runnable { } } - public void setAuto(boolean auto) { + public RouteManager setAuto(boolean auto) { LOG.debug("{}abled autopilot of {}",auto?"En":"Dis"); autopilot = auto; + return this; + } + + public void start() { + Thread thread = new Thread(this); + thread.setName(context.train().name()); + thread.start(); } } diff --git a/src/main/java/de/srsoftware/web4rail/tiles/Block.java b/src/main/java/de/srsoftware/web4rail/tiles/Block.java index e2865d6..bdb71e7 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/Block.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/Block.java @@ -20,6 +20,7 @@ import org.slf4j.LoggerFactory; import de.srsoftware.tools.Tag; import de.srsoftware.web4rail.BaseClass; import de.srsoftware.web4rail.Connector; +import de.srsoftware.web4rail.LoadCallback; import de.srsoftware.web4rail.Plan.Direction; import de.srsoftware.web4rail.Range; import de.srsoftware.web4rail.Route; @@ -99,7 +100,7 @@ public abstract class Block extends StretchableTile{ private static final String RAISE = "raise"; public static final String ACTION_ADD_CONTACT = "add_contact"; private static final String PARKED_TRAINS = "parked_trains"; - private static final String TRAINS = "parked_trains"; + private static final String TRAINS = "trains"; public String name = "Block"; public boolean turnAllowed = false; @@ -202,15 +203,6 @@ public abstract class Block extends StretchableTile{ return t("Trigger contact to learn new contact"); } - @Override - public boolean isFreeFor(Context context) { - Train train = context.train(); - if (is(Status.DISABLED)) return false; - if (trains.isEmpty()) return true; - if (trains.first() == train) return true; - return train.isShunting(); // block contains train(s), thus it is only free for shunting train - } - @Override public Object click(boolean shift) throws IOException { if (!trains.isEmpty() && !shift) return trains.first().properties(); @@ -309,6 +301,15 @@ public abstract class Block extends StretchableTile{ return 1+internalContacts.indexOf(contact); } + @Override + public boolean isFreeFor(Context context) { + Train train = context.train(); + if (is(Status.DISABLED)) return false; + if (trains.isEmpty()) return true; + if (trains.first() == train) return true; + return train.isShunting(); // block contains train(s), thus it is only free for shunting train + } + @Override public JSONObject json() { JSONObject json = super.json(); @@ -325,12 +326,17 @@ public abstract class Block extends StretchableTile{ } } if (isSet(jContacts)) json.put(CONTACT, jContacts); + json.remove(REALM_TRAIN); // is set by TRAINS field for blocks if (!trains.isEmpty()) { - JSONArray ptids = new JSONArray(); - for (Train parked : trains) { - if (isSet(parked)) ptids.put(parked.id().toString()); + JSONArray jTrains = new JSONArray(); + for (Train train : trains) { + JSONObject to = new JSONObject(); + to.put(ID, train.id()); + Direction dir = trains.directionOf(train); + if (isSet(dir)) to.put(DIRECTION, dir.toString()); + jTrains.put(to); } - json.put(PARKED_TRAINS, ptids); + json.put(TRAINS, jTrains); } return json; } @@ -339,6 +345,10 @@ public abstract class Block extends StretchableTile{ return trains.last(); } + public List leavingRoutes() { + return routes().stream().filter(route -> route.startBlock() == Block.this).collect(Collectors.toList()); + } + /** * If arguments are given, the first is taken as content, the second as tag type. @@ -372,24 +382,33 @@ public abstract class Block extends StretchableTile{ } catch (JSONException e) {} } } - if (json.has(TRAINS)) { - JSONArray jTrains = json.getJSONArray(TRAINS); - for (Object o : jTrains) { - if (o instanceof JSONObject) { - JSONObject to = (JSONObject) o; - Train train = BaseClass.get(new Id(to.getString(ID))); - Direction direction = to.has(DIRECTION) ? Direction.valueOf(to.getString(DIRECTION)) : null; - if (isSet(train)) trains.add(train, direction); + + new LoadCallback() { + @Override + public void afterLoad() { + if (json.has(TRAINS)) { + JSONArray jTrains = json.getJSONArray(TRAINS); + for (Object o : jTrains) { + if (o instanceof JSONObject) { + JSONObject to = (JSONObject) o; + Id tID = new Id(to.getString(ID)); + Train train = BaseClass.get(tID); + Direction direction = to.has(DIRECTION) ? Direction.valueOf(to.getString(DIRECTION)) : null; + if (isSet(train)) { + trains.add(train, direction); + train.set(Block.this); + } + } + } + } else if (json.has(PARKED_TRAINS)) { // legacy + for (Object id : json.getJSONArray(PARKED_TRAINS)) { + Train train = BaseClass.get(new Id(id.toString())); + if (isSet(train)) trains.add(train,null); + } } } - } - if (json.has(PARKED_TRAINS)) { // legacy - JSONArray ptids = json.getJSONArray(PARKED_TRAINS); - for (Object id : ptids) { - Train train = BaseClass.get(new Id(id.toString())); - if (isSet(train)) trains.add(train,null); - } - } + }; + return super.load(json); } @@ -485,10 +504,6 @@ public abstract class Block extends StretchableTile{ internalContacts.remove(blockContact); } - public List routes(Direction direction) { - return routes().stream().filter(route -> route.startBlock() == Block.this).collect(Collectors.toList()); - } - public void set(Train train, Direction direction) { trains.set(train,direction); } diff --git a/src/main/java/de/srsoftware/web4rail/tiles/Bridge.java b/src/main/java/de/srsoftware/web4rail/tiles/Bridge.java index 845d2c1..4dcbff2 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/Bridge.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/Bridge.java @@ -9,10 +9,10 @@ import org.json.JSONObject; import de.srsoftware.tools.Tag; import de.srsoftware.web4rail.BaseClass; import de.srsoftware.web4rail.Connector; +import de.srsoftware.web4rail.LoadCallback; import de.srsoftware.web4rail.moving.Train; import de.srsoftware.web4rail.tags.Fieldset; import de.srsoftware.web4rail.tags.Window; -import de.srsoftware.web4rail.threads.DelayedExecution; public abstract class Bridge extends Tile { private static final String COUNTERPART = "counterpart"; @@ -56,14 +56,12 @@ public abstract class Bridge extends Tile { @Override public Tile load(JSONObject json) { - if (json.has(COUNTERPART)) { - new DelayedExecution(this) { - @Override - public void execute() { - counterpart = (Bridge) plan.get(Id.from(json, COUNTERPART), false); - } - }; - } + if (json.has(COUNTERPART)) new LoadCallback() { + @Override + public void afterLoad() { + counterpart = (Bridge) plan.get(Id.from(json, COUNTERPART), false); + } + }; return super.load(json); } From 3c53bd192fe6f99652a5a26bd41fbbd0d61ee46b Mon Sep 17 00:00:00 2001 From: Stephan Richter Date: Sun, 14 Mar 2021 19:45:17 +0100 Subject: [PATCH 18/26] bugfixes --- pom.xml | 2 +- src/main/java/de/srsoftware/web4rail/moving/Train.java | 10 ++++++++-- src/main/java/de/srsoftware/web4rail/tiles/Block.java | 3 ++- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 384da23..d569abd 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 de.srsoftware web4rail - 1.3.68 + 1.3.69 Web4Rail jar Java Model Railway Control diff --git a/src/main/java/de/srsoftware/web4rail/moving/Train.java b/src/main/java/de/srsoftware/web4rail/moving/Train.java index 3892bdf..62ad28d 100644 --- a/src/main/java/de/srsoftware/web4rail/moving/Train.java +++ b/src/main/java/de/srsoftware/web4rail/moving/Train.java @@ -524,7 +524,10 @@ public class Train extends BaseClass implements Comparable { public void afterLoad() { if (json.has(TRACE)) json.getJSONArray(TRACE).forEach(elem -> { Tile tile = plan.get(new Id(elem.toString()), false); - if (tile.setTrain(Train.this)) trace.add(tile); + if (tile instanceof Block) { + ((Block)tile).add(Train.this, direction); + } else if (tile.setTrain(Train.this)); + trace.add(tile); }); if (json.has(BLOCK)) {// do not move this up! during set, other fields will be referenced! currentBlock = (Block) plan.get(Id.from(json, BLOCK), false); @@ -759,7 +762,10 @@ public class Train extends BaseClass implements Comparable { public void set(Block newBlock) { LOG.debug("{}.set({})",this,newBlock); - if (isSet(currentBlock)) currentBlock.free(this); + if (isSet(currentBlock)) { + if (newBlock == currentBlock) return; + currentBlock.free(this); + } currentBlock = newBlock; if (isSet(currentBlock)) { currentBlock.setTrain(this); diff --git a/src/main/java/de/srsoftware/web4rail/tiles/Block.java b/src/main/java/de/srsoftware/web4rail/tiles/Block.java index bdb71e7..bb09a94 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/Block.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/Block.java @@ -395,8 +395,9 @@ public abstract class Block extends StretchableTile{ Train train = BaseClass.get(tID); Direction direction = to.has(DIRECTION) ? Direction.valueOf(to.getString(DIRECTION)) : null; if (isSet(train)) { - trains.add(train, direction); train.set(Block.this); + trains.add(train, direction); + status = Status.OCCUPIED; } } } From fd254db358c7becb28936a18aa6784b33fb60cc4 Mon Sep 17 00:00:00 2001 From: Stephan Richter Date: Sun, 14 Mar 2021 20:07:36 +0100 Subject: [PATCH 19/26] minor bugfix. next step: implementing the preserveNextRoute action. Must work in the following conditions: - called from route.setupActions - called from route.startActions - called from route.contactActions - called from autopilot after reaching route.endBlock --- pom.xml | 2 +- .../java/de/srsoftware/web4rail/actions/PreserveRoute.java | 2 +- src/main/java/de/srsoftware/web4rail/moving/Train.java | 7 ++++--- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index d569abd..7007ba1 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 de.srsoftware web4rail - 1.3.69 + 1.3.70 Web4Rail jar Java Model Railway Control diff --git a/src/main/java/de/srsoftware/web4rail/actions/PreserveRoute.java b/src/main/java/de/srsoftware/web4rail/actions/PreserveRoute.java index 20b49d0..4e9ab0c 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/PreserveRoute.java +++ b/src/main/java/de/srsoftware/web4rail/actions/PreserveRoute.java @@ -30,7 +30,7 @@ public class PreserveRoute extends Action { return false; // train is expected to wait in next block. } - train.reserveNext(); + train.reserveRouteAfter(route); return true; } } diff --git a/src/main/java/de/srsoftware/web4rail/moving/Train.java b/src/main/java/de/srsoftware/web4rail/moving/Train.java index 62ad28d..d52f5d1 100644 --- a/src/main/java/de/srsoftware/web4rail/moving/Train.java +++ b/src/main/java/de/srsoftware/web4rail/moving/Train.java @@ -715,10 +715,10 @@ public class Train extends BaseClass implements Comparable { return tags().iterator(); } - public void reserveNext() { - LOG.debug("{}.reserveNext()",this); + public void reserveRouteAfter(Route r) { + LOG.debug("reserveRouteAfter({})",r); } - + /** * This turns the train as if it went through a loop. Example: * before: CabCar→ MiddleCar→ Loco→ @@ -886,6 +886,7 @@ public class Train extends BaseClass implements Comparable { if (trace.contains(tile)) stuckTrace.add(tile); } route.reset(); + route = null; } return properties(); } From 030b29499f1caee0b7e0144f9d0e07e3effc6850 Mon Sep 17 00:00:00 2001 From: Stephan Richter Date: Tue, 16 Mar 2021 21:39:34 +0100 Subject: [PATCH 20/26] dunno what to write, this is a non-clean state... --- .../java/de/srsoftware/web4rail/Route.java | 15 ++ .../web4rail/actions/PreserveRoute.java | 3 +- .../de/srsoftware/web4rail/moving/Train.java | 34 ++- .../web4rail/threads/RouteManager.java | 231 +++++++++--------- 4 files changed, 152 insertions(+), 131 deletions(-) diff --git a/src/main/java/de/srsoftware/web4rail/Route.java b/src/main/java/de/srsoftware/web4rail/Route.java index b029928..6d49e18 100644 --- a/src/main/java/de/srsoftware/web4rail/Route.java +++ b/src/main/java/de/srsoftware/web4rail/Route.java @@ -91,6 +91,8 @@ public class Route extends BaseClass { private Block startBlock = null; public Direction startDirection; private HashSet triggeredContacts = new HashSet<>(); + + private Route nextRoute; public Route() { conditions = new ConditionList(); @@ -404,6 +406,10 @@ public class Route extends BaseClass { return sb.toString().trim(); } + public Route getNextRoute() { + return nextRoute; + } + public Id id() { if (isNull(id)) id = new Id(""+(generateName().hashCode())); return id; @@ -775,6 +781,11 @@ public class Route extends BaseClass { if (lastTile instanceof Turnout) addTurnout((Turnout) lastTile,state); } + public void setNextRoute(Route nextRoute) { + this.nextRoute = nextRoute; + } + + public boolean setSignals(String state) { LOG.debug("{}.setSignals({})",this,state); for (Signal signal : signals) { @@ -862,4 +873,8 @@ public class Route extends BaseClass { super.update(params); return properties(); } + + public Integer waitTime() { + return isNull(context) ? null : context.waitTime(); + } } diff --git a/src/main/java/de/srsoftware/web4rail/actions/PreserveRoute.java b/src/main/java/de/srsoftware/web4rail/actions/PreserveRoute.java index 4e9ab0c..d23f56c 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/PreserveRoute.java +++ b/src/main/java/de/srsoftware/web4rail/actions/PreserveRoute.java @@ -30,7 +30,6 @@ public class PreserveRoute extends Action { return false; // train is expected to wait in next block. } - train.reserveRouteAfter(route); - return true; + return route.prepareNext(train); } } diff --git a/src/main/java/de/srsoftware/web4rail/moving/Train.java b/src/main/java/de/srsoftware/web4rail/moving/Train.java index d52f5d1..083bef9 100644 --- a/src/main/java/de/srsoftware/web4rail/moving/Train.java +++ b/src/main/java/de/srsoftware/web4rail/moving/Train.java @@ -37,6 +37,7 @@ import de.srsoftware.web4rail.tags.Select; import de.srsoftware.web4rail.tags.Table; import de.srsoftware.web4rail.tags.Window; import de.srsoftware.web4rail.threads.RouteManager; +import de.srsoftware.web4rail.threads.RouteManager.Callback; import de.srsoftware.web4rail.tiles.Block; import de.srsoftware.web4rail.tiles.Contact; import de.srsoftware.web4rail.tiles.Tile; @@ -61,7 +62,7 @@ public class Train extends BaseClass implements Comparable { private Route route; private Direction direction; - + private boolean autopilot; private static final String PUSH_PULL = "pushPull"; public boolean pushPull = false; @@ -93,6 +94,8 @@ public class Train extends BaseClass implements Comparable { private HashSet stuckTrace = null; + private Route nextRoute; + public static Object action(HashMap params, Plan plan) throws IOException { String action = params.get(ACTION); if (isNull(action)) return t("No action passed to Train.action!"); @@ -403,12 +406,18 @@ public class Train extends BaseClass implements Comparable { public void endRoute(Block endBlock, Direction endDirection) { setSpeed(0); + nextRoute = route.getNextRoute(); + Integer waitTime = route.waitTime(); route = null; direction = endDirection; endBlock.add(this, direction); currentBlock = endBlock; trace.add(endBlock); stuckTrace = null; + if (autopilot) { + if (isSet(waitTime) && waitTime > 0) sleep(waitTime); + start(false); + } } private Tag faster(int steps) { @@ -715,10 +724,6 @@ public class Train extends BaseClass implements Comparable { return tags().iterator(); } - public void reserveRouteAfter(Route r) { - LOG.debug("reserveRouteAfter({})",r); - } - /** * This turns the train as if it went through a loop. Example: * before: CabCar→ MiddleCar→ Loco→ @@ -864,9 +869,20 @@ public class Train extends BaseClass implements Comparable { public String start(boolean auto) { - if (isNull(routeManager)) routeManager = new RouteManager(this); - routeManager.setAuto(auto).start(); - plan.stream(t("Started {}",this)); + autopilot |= auto; + if (isSet(nextRoute) && nextRoute.start()) { + nextRoute = null; + return null; + } + if (isNull(routeManager)) routeManager = new RouteManager(); + routeManager.setContext(new Context(this).block(currentBlock).direction(direction)); + routeManager.start(new Callback() { + @Override + public void routePrepared(Route route) { + route.start(); + plan.stream(t("Started {}",Train.this)); + } + }); return null; } @@ -999,6 +1015,6 @@ public class Train extends BaseClass implements Comparable { } public boolean usesAutopilot() { - return isSet(routeManager) && routeManager.autoEnabled(); + return autopilot; } } diff --git a/src/main/java/de/srsoftware/web4rail/threads/RouteManager.java b/src/main/java/de/srsoftware/web4rail/threads/RouteManager.java index 1d01de2..9bba11e 100644 --- a/src/main/java/de/srsoftware/web4rail/threads/RouteManager.java +++ b/src/main/java/de/srsoftware/web4rail/threads/RouteManager.java @@ -16,202 +16,193 @@ import de.srsoftware.web4rail.moving.Train; import de.srsoftware.web4rail.tiles.Block; import de.srsoftware.web4rail.tiles.Tile; -public class RouteManager extends BaseClass implements Runnable { - - enum State { - ENDED,IDLE,STARTED; - } - private static final Logger LOG = LoggerFactory.getLogger(RouteManager.class); - private static final int DEFAULT_PAUSE_TIME = 250; // ms - private State state = State.IDLE; - private boolean autopilot; - private Context context; - private int time = 0; +public class RouteManager extends BaseClass { - public RouteManager(Train train) { - context = new Context(train); - state = State.STARTED; + public static abstract class Callback { + public abstract void routePrepared(Route route); } - - private static TreeMap> availableRoutes(Context context,HashSet visitedRoutes){ + + private static final Logger LOG = LoggerFactory.getLogger(RouteManager.class); + private Context ctx; + private Callback callback; + private boolean active; + + public RouteManager() { + active = false; + } + + private static TreeMap> availableRoutes(Context context, HashSet visitedRoutes) { String inset = ""; - for (int i=0; i> availableRoutes = new TreeMap>(); - + TreeMap> availableRoutes = new TreeMap>(); + boolean error = false; - if (isNull(block) && (error = true)) LOG.warn("{} → {}.availableRoutes called without context.block!",inset,Train.class.getSimpleName()); - if (isNull(train) && (error = true)) LOG.warn("{}→ {}.availableRoutes called without context.train!", inset,Train.class.getSimpleName()); + if (isNull(block) && (error = true)) + LOG.warn("{} → {}.availableRoutes called without context.block!", inset, Train.class.getSimpleName()); + if (isNull(train) && (error = true)) + LOG.warn("{}→ {}.availableRoutes called without context.train!", inset, Train.class.getSimpleName()); if (error) return availableRoutes; - + Block destination = train.destination(); if (isSet(startDirection)) { - LOG.debug("{}- Looking for {}-bound routes from {}",inset,startDirection,block); + LOG.debug("{}- Looking for {}-bound routes from {}", inset, startDirection, block); } else { - LOG.debug("{}- Looking for all routes from {}",inset,block); + LOG.debug("{}- Looking for all routes from {}", inset, block); } - - if (isSet(destination) && visitedRoutes.isEmpty()) LOG.debug("{}- Destination: {}",inset,destination); - + + if (isSet(destination) && visitedRoutes.isEmpty()) LOG.debug("{}- Destination: {}", inset, destination); + for (Route routeCandidate : block.leavingRoutes()) { if (context.invalidated()) return availableRoutes; if (visitedRoutes.contains(routeCandidate)) { - LOG.debug("{}→ Candidate {} would create loop, skipping",inset,routeCandidate.shortName()); + LOG.debug("{}→ Candidate {} would create loop, skipping", inset, routeCandidate.shortName()); continue; } - - HashSet stuckTrace = train.stuckTrace(); // if train has been stopped in between two blocks lastly: only allow routes that do not conflict with current train position + + HashSet stuckTrace = train.stuckTrace(); // if train has been stopped in between two blocks lastly: + // only allow routes that do not conflict with current train + // position if (isSet(stuckTrace) && !routeCandidate.path().containsAll(stuckTrace)) { - LOG.debug("Stuck train occupies tiles ({}) outside of {} – not allowed.",stuckTrace,routeCandidate); + LOG.debug("Stuck train occupies tiles ({}) outside of {} – not allowed.", stuckTrace, routeCandidate); continue; } - - if (!routeCandidate.allowed(context)) { + + if (!routeCandidate.allowed(context)) { if (routeCandidate.endBlock() != destination) { // allowance may be overridden by destination - LOG.debug("{} not allowed for {}",routeCandidate,context); + LOG.debug("{} not allowed for {}", routeCandidate, context); continue; // Zug darf auf Grund einer nicht erfüllten Bedingung nicht auf die Route } - LOG.debug("{} not allowed for {} – overridden by selected destination",routeCandidate,context); + LOG.debug("{} not allowed for {} – overridden by selected destination", routeCandidate, context); } int priority = 0; - if (isSet(startDirection) && routeCandidate.startDirection != startDirection) { // Route startet entgegen der aktuellen Fahrtrichtung des Zuges + if (isSet(startDirection) && routeCandidate.startDirection != startDirection) { // Route startet entgegen + // der aktuellen + // Fahrtrichtung des Zuges if (!train.pushPull) continue; // Zug kann nicht wenden if (!block.turnAllowed) continue; // Wenden im Block nicht gestattet priority -= 5; } - if (routeCandidate == currentRoute) priority-=10; // möglichst andere Route als zuvor wählen // TODO: den Routen einen "last-used" Zeitstempel hinzufügen, und diesen mit in die Priorisierung einbeziehen + if (routeCandidate == currentRoute) priority -= 10; // möglichst andere Route als zuvor wählen // TODO: den + // Routen einen "last-used" Zeitstempel hinzufügen, und + // diesen mit in die Priorisierung einbeziehen if (isSet(destination)) { if (routeCandidate.endBlock() == destination) { // route goes directly to destination - LOG.debug("{}→ Candidate {} directly leads to {}",inset,routeCandidate.shortName(),destination); + LOG.debug("{}→ Candidate {} directly leads to {}", inset, routeCandidate.shortName(), destination); priority = 1_000_000; } else { - LOG.debug("{}- Candidate: {}",inset,routeCandidate.shortName()); - Context forwardContext = new Context(train).block(routeCandidate.endBlock()).route(null).direction(routeCandidate.endDirection); + LOG.debug("{}- Candidate: {}", inset, routeCandidate.shortName()); + Context forwardContext = new Context(train).block(routeCandidate.endBlock()).route(null) + .direction(routeCandidate.endDirection); visitedRoutes.add(routeCandidate); - TreeMap> forwardRoutes = availableRoutes(forwardContext,visitedRoutes); + TreeMap> forwardRoutes = availableRoutes(forwardContext, visitedRoutes); visitedRoutes.remove(routeCandidate); - if (forwardRoutes.isEmpty()) continue; // the candidate does not lead to a block, from which routes to the destination exist + if (forwardRoutes.isEmpty()) continue; // the candidate does not lead to a block, from which routes + // to the destination exist Entry> entry = forwardRoutes.lastEntry(); - LOG.debug("{}→ The following routes have connections to {}:",inset,destination); - for (Route rt: entry.getValue()) LOG.debug("{} - {}",inset,rt.shortName()); - priority += entry.getKey()-10; + LOG.debug("{}→ The following routes have connections to {}:", inset, destination); + for (Route rt : entry.getValue()) LOG.debug("{} - {}", inset, rt.shortName()); + priority += entry.getKey() - 10; } } - + List routeSet = availableRoutes.get(priority); if (isNull(routeSet)) { routeSet = new Vector(); availableRoutes.put(priority, routeSet); } routeSet.add(routeCandidate); - if (routeCandidate.endBlock() == destination) break; // direct connection to destination discovered, quit search + if (routeCandidate.endBlock() == destination) break; // direct connection to destination discovered, quit + // search } - if (!availableRoutes.isEmpty()) LOG.debug("{}→ Routes from {}: {}",inset,block,availableRoutes.isEmpty()?"none":""); + if (!availableRoutes.isEmpty()) + LOG.debug("{}→ Routes from {}: {}", inset, block, availableRoutes.isEmpty() ? "none" : ""); for (Entry> entry : availableRoutes.entrySet()) { - LOG.debug("{} - Priority {}:",inset,entry.getKey()); - for (Route r : entry.getValue()) LOG.debug("{} - {}",inset,r.shortName()); + LOG.debug("{} - Priority {}:", inset, entry.getKey()); + for (Route r : entry.getValue()) LOG.debug("{} - {}", inset, r.shortName()); } return availableRoutes; } - public boolean autoEnabled() { - return autopilot && isActive(); - } - public static Route chooseRoute(Context context) { - LOG.debug("{}.chooseRoute({})",RouteManager.class.getSimpleName(),context); - TreeMap> availableRoutes = availableRoutes(context,new HashSet()); + LOG.debug("{}.chooseRoute({})", RouteManager.class.getSimpleName(), context); + TreeMap> availableRoutes = availableRoutes(context, new HashSet()); while (!availableRoutes.isEmpty()) { if (context.invalidated()) break; - LOG.debug("availableRoutes: {}",availableRoutes); + LOG.debug("availableRoutes: {}", availableRoutes); Entry> entry = availableRoutes.lastEntry(); List preferredRoutes = entry.getValue(); - LOG.debug("preferredRoutes: {}",preferredRoutes); + LOG.debug("preferredRoutes: {}", preferredRoutes); Route selectedRoute = preferredRoutes.get(random.nextInt(preferredRoutes.size())); if (selectedRoute.isFreeFor(context)) { - LOG.debug("Chose \"{}\" with priority {}.",selectedRoute,entry.getKey()); + LOG.debug("Chose \"{}\" with priority {}.", selectedRoute, entry.getKey()); return selectedRoute; } - - LOG.debug("Selected route \"{}\" is not free for {}",selectedRoute,context); + + LOG.debug("Selected route \"{}\" is not free for {}", selectedRoute, context); preferredRoutes.remove(selectedRoute); if (preferredRoutes.isEmpty()) availableRoutes.remove(availableRoutes.lastKey()); } return null; } - - public boolean isActive() { - switch (state) { - case ENDED: - return false; - case IDLE: - return false; - default: - return true; - } - } - - private void pause(){ - if (time == 0) { - time = DEFAULT_PAUSE_TIME; - } else sleep(time); - } - + public void quit() { - LOG.debug("{}.quit",this); - autopilot = false; - context.invalidate(); + LOG.debug("{}.quit", this); + callback = null; + if (isSet(ctx)) ctx.invalidate(); } - - @Override - public void run() { - Train train = context.train(); + + public Route prepareRoute(Context context) { try { - do { - pause(); - if (isSet(train.route())) continue; - if (context.invalidated()) return; - context.block(train.currentBlock()).direction(train.direction()); - Route route = chooseRoute(context); - if (isNull(route)) continue; - context.route(route); - if (!route.reserveFor(context)) { - route.reset(); - continue; - } - if (!route.prepareAndLock()) { - route.reset(); - continue; - } - // Route reserved, prepared and locked: - if (!route.start()) { - route.reset(); - continue; - } - } while (autopilot); + if (isNull(context) || context.invalidated()) return null; + Route route = chooseRoute(context); + if (isNull(route)) return null; + context.route(route); + if (!route.reserveFor(context)) { + route.reset(); + return null; + } + if (!route.prepareAndLock()) { + route.reset(); + return null; + } + + return route; } finally { - // do not invalidate context here: may be used in delayed actions called from (successful) start - state = State.ENDED; - train.removeChild(this); + active = false; } } - public RouteManager setAuto(boolean auto) { - LOG.debug("{}abled autopilot of {}",auto?"En":"Dis"); - autopilot = auto; - return this; + public void setContext(Context context) { + ctx = context; } - public void start() { - Thread thread = new Thread(this); - thread.setName(context.train().name()); + public void setCallback(Callback callback) { + if (ctx.invalidated()) return; + if (isNull(this.callback)) { + Route route = prepareRoute(ctx); + if (isSet(route) && isSet(callback)) callback.routePrepared(route); + } + } + + public boolean isActive() { + return active; + } + + public void start(Callback callback) { + Thread thread = new Thread() { + public void run() { + setCallback(callback); + }; + }; + thread.setName(getClass().getSimpleName() + "(" + ctx.train() + ")"); thread.start(); } } From c62f75fa0293c03e9edb2a53346d28de27b81cf1 Mon Sep 17 00:00:00 2001 From: Stephan Richter Date: Tue, 16 Mar 2021 22:28:29 +0100 Subject: [PATCH 21/26] =?UTF-8?q?Code=20zum=20Reservieren=20der=20n=C3=A4c?= =?UTF-8?q?hsten=20Route=20=C3=BCberarbeitet.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit *Könnte* jetzt funktionieren, hatte jedoch keine Zeit zum Testen. --- .../web4rail/actions/PreserveRoute.java | 2 +- .../de/srsoftware/web4rail/moving/Train.java | 35 ++++++++++-- .../web4rail/threads/RouteManager.java | 53 +++++++++---------- 3 files changed, 57 insertions(+), 33 deletions(-) diff --git a/src/main/java/de/srsoftware/web4rail/actions/PreserveRoute.java b/src/main/java/de/srsoftware/web4rail/actions/PreserveRoute.java index d23f56c..ef99cdd 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/PreserveRoute.java +++ b/src/main/java/de/srsoftware/web4rail/actions/PreserveRoute.java @@ -30,6 +30,6 @@ public class PreserveRoute extends Action { return false; // train is expected to wait in next block. } - return route.prepareNext(train); + return train.reserveRouteAfter(route); } } diff --git a/src/main/java/de/srsoftware/web4rail/moving/Train.java b/src/main/java/de/srsoftware/web4rail/moving/Train.java index 083bef9..4b3365f 100644 --- a/src/main/java/de/srsoftware/web4rail/moving/Train.java +++ b/src/main/java/de/srsoftware/web4rail/moving/Train.java @@ -36,8 +36,8 @@ import de.srsoftware.web4rail.tags.Label; import de.srsoftware.web4rail.tags.Select; import de.srsoftware.web4rail.tags.Table; import de.srsoftware.web4rail.tags.Window; -import de.srsoftware.web4rail.threads.RouteManager; import de.srsoftware.web4rail.threads.RouteManager.Callback; +import de.srsoftware.web4rail.threads.RouteManager; import de.srsoftware.web4rail.tiles.Block; import de.srsoftware.web4rail.tiles.Contact; import de.srsoftware.web4rail.tiles.Tile; @@ -460,7 +460,7 @@ public class Train extends BaseClass implements Comparable { public boolean isStoppable() { if (speed > 0) return true; - if (isSet(routeManager) && routeManager.isActive()) return true; + if (isSet(routeManager) && routeManager.isSearching()) return true; if (isSet(route)) return true; return false; } @@ -724,6 +724,21 @@ public class Train extends BaseClass implements Comparable { return tags().iterator(); } + public boolean reserveRouteAfter(Route r) { + LOG.debug("reserveRouteAfter({})",r); + if (isNull(routeManager)) routeManager = new RouteManager(); + Context newContext = new Context(this).block(r.endBlock()).direction(r.endDirection); + if (routeManager.isSearching()) return false; + routeManager.setContext(newContext); + return (routeManager.setCallback(new Callback() { + + @Override + public void routePrepared(Route route) { + r.setNextRoute(route); + } + })); + } + /** * This turns the train as if it went through a loop. Example: * before: CabCar→ MiddleCar→ Loco→ @@ -875,14 +890,24 @@ public class Train extends BaseClass implements Comparable { return null; } if (isNull(routeManager)) routeManager = new RouteManager(); - routeManager.setContext(new Context(this).block(currentBlock).direction(direction)); - routeManager.start(new Callback() { + + Callback callback = new Callback() { @Override public void routePrepared(Route route) { route.start(); plan.stream(t("Started {}",Train.this)); } - }); + }; + + if (routeManager.isSearching()) { // es wird bereits eine Anschlussroute gesucht + // in diesem Fall muss bloß dass Callback des Route-Managers aktualisiert werden + routeManager.setCallback(callback); + } else { // routeManager nicht aktiv →> neuen Context setzen und starten + routeManager.setContext(new Context(this).block(currentBlock).direction(direction)); + routeManager.start(callback); + } + + return null; } diff --git a/src/main/java/de/srsoftware/web4rail/threads/RouteManager.java b/src/main/java/de/srsoftware/web4rail/threads/RouteManager.java index 9bba11e..01fc984 100644 --- a/src/main/java/de/srsoftware/web4rail/threads/RouteManager.java +++ b/src/main/java/de/srsoftware/web4rail/threads/RouteManager.java @@ -25,10 +25,10 @@ public class RouteManager extends BaseClass { private static final Logger LOG = LoggerFactory.getLogger(RouteManager.class); private Context ctx; private Callback callback; - private boolean active; + private boolean searching; public RouteManager() { - active = false; + searching = false; } private static TreeMap> availableRoutes(Context context, HashSet visitedRoutes) { @@ -160,40 +160,39 @@ public class RouteManager extends BaseClass { } public Route prepareRoute(Context context) { - try { - if (isNull(context) || context.invalidated()) return null; - Route route = chooseRoute(context); - if (isNull(route)) return null; - context.route(route); - if (!route.reserveFor(context)) { - route.reset(); - return null; - } - if (!route.prepareAndLock()) { - route.reset(); - return null; - } - - return route; - } finally { - active = false; + if (isNull(context) || context.invalidated()) return null; + Route route = chooseRoute(context); + if (isNull(route)) return null; + context.route(route); + if (!route.reserveFor(context)) { + route.reset(); + return null; } + if (!route.prepareAndLock()) { + route.reset(); + return null; + } + return route; } public void setContext(Context context) { ctx = context; } - public void setCallback(Callback callback) { - if (ctx.invalidated()) return; - if (isNull(this.callback)) { - Route route = prepareRoute(ctx); - if (isSet(route) && isSet(callback)) callback.routePrepared(route); - } + public boolean setCallback(Callback callback) { + if (ctx.invalidated()) return false; + this.callback = callback; + if (searching) return false; // search already running, do not start again! + searching = true; + Route route = prepareRoute(ctx); + searching = false; + if (isNull(route) || isNull(callback)) return false; + callback.routePrepared(route); + return true; } - public boolean isActive() { - return active; + public boolean isSearching() { + return searching; } public void start(Callback callback) { From 5e50259d247b8628262d18be162db4e323569c54 Mon Sep 17 00:00:00 2001 From: Stephan Richter Date: Wed, 17 Mar 2021 23:58:38 +0100 Subject: [PATCH 22/26] Implementierung des neuen Routen-Algorithmus weitgehend abgeschlossen. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Was noch fehlt ist der Brems-Prozessor; außerdem muss der neue Code getestet werden. --- pom.xml | 2 +- .../de/srsoftware/web4rail/BaseClass.java | 2 +- .../java/de/srsoftware/web4rail/Command.java | 8 +- .../java/de/srsoftware/web4rail/Plan.java | 8 +- .../java/de/srsoftware/web4rail/Route.java | 36 +++++--- .../web4rail/actions/PreserveRoute.java | 3 +- .../web4rail/actions/SetTurnout.java | 6 +- .../de/srsoftware/web4rail/moving/Train.java | 78 +++++++++++----- .../web4rail/threads/BrakeProcess.java | 67 ++++++++++++++ .../web4rail/threads/ControlUnit.java | 12 +-- .../web4rail/threads/DelayedExecution.java | 3 +- .../web4rail/threads/RouteManager.java | 89 +++++++++++-------- .../de/srsoftware/web4rail/tiles/Block.java | 3 +- .../de/srsoftware/web4rail/tiles/Contact.java | 9 +- .../de/srsoftware/web4rail/tiles/Switch.java | 6 +- .../de/srsoftware/web4rail/tiles/Turnout.java | 3 +- 16 files changed, 225 insertions(+), 110 deletions(-) create mode 100644 src/main/java/de/srsoftware/web4rail/threads/BrakeProcess.java diff --git a/pom.xml b/pom.xml index 7007ba1..9effb7f 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 de.srsoftware web4rail - 1.3.70 + 1.3.71 Web4Rail jar Java Model Railway Control diff --git a/src/main/java/de/srsoftware/web4rail/BaseClass.java b/src/main/java/de/srsoftware/web4rail/BaseClass.java index 3d3689a..5622b23 100644 --- a/src/main/java/de/srsoftware/web4rail/BaseClass.java +++ b/src/main/java/de/srsoftware/web4rail/BaseClass.java @@ -542,7 +542,7 @@ public abstract class BaseClass implements Constants{ public void sleep(long ms) { try { - Thread.sleep(1000); + Thread.sleep(ms); } catch (InterruptedException e) { e.printStackTrace(); } diff --git a/src/main/java/de/srsoftware/web4rail/Command.java b/src/main/java/de/srsoftware/web4rail/Command.java index e2a5d67..99b2351 100644 --- a/src/main/java/de/srsoftware/web4rail/Command.java +++ b/src/main/java/de/srsoftware/web4rail/Command.java @@ -12,7 +12,7 @@ import org.slf4j.LoggerFactory; * @author Stephan Richter, SRSoftware * */ -public class Command { +public class Command extends BaseClass { private static final Logger LOG = LoggerFactory.getLogger(Command.class); private String command; @@ -143,11 +143,9 @@ public class Command { */ public Reply reply(int timeout) throws TimeoutException { int counter = 0; - while (reply == null) try { + while (reply == null) { if (counter++ > timeout) timeout(); - Thread.sleep(10); - } catch (InterruptedException e) { - LOG.warn("wait() interrupted!",e); + sleep(10); } return reply; } diff --git a/src/main/java/de/srsoftware/web4rail/Plan.java b/src/main/java/de/srsoftware/web4rail/Plan.java index bed753e..5bfe737 100644 --- a/src/main/java/de/srsoftware/web4rail/Plan.java +++ b/src/main/java/de/srsoftware/web4rail/Plan.java @@ -300,7 +300,7 @@ public class Plan extends BaseClass{ return win; } - Thread analyzer = new Thread() { + new Thread(Application.threadName("Plan.Analyzer")) { public void run() { Vector newRoutes = new Vector(); for (Block block : BaseClass.listElements(Block.class)) { @@ -317,9 +317,7 @@ public class Plan extends BaseClass{ stream(t("Found {} routes.",newRoutes.size())); } - }; - analyzer.setName(Application.threadName("Plan.Analyzer")); - analyzer.start(); + }.start(); return t("Analyzing plan..."); } @@ -652,7 +650,7 @@ public class Plan extends BaseClass{ */ public Tile place(Tile tile) { try { - tile.parent(this); +// tile.parent(this); tile.register(); stream("place "+tile.tag(null)); } catch (IOException e) { diff --git a/src/main/java/de/srsoftware/web4rail/Route.java b/src/main/java/de/srsoftware/web4rail/Route.java index 6d49e18..d380827 100644 --- a/src/main/java/de/srsoftware/web4rail/Route.java +++ b/src/main/java/de/srsoftware/web4rail/Route.java @@ -4,6 +4,7 @@ import java.io.BufferedWriter; import java.io.FileInputStream; import java.io.FileWriter; import java.io.IOException; +import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -36,6 +37,7 @@ import de.srsoftware.web4rail.tags.Fieldset; import de.srsoftware.web4rail.tags.Input; import de.srsoftware.web4rail.tags.Table; import de.srsoftware.web4rail.tags.Window; +import de.srsoftware.web4rail.threads.BrakeProcess; import de.srsoftware.web4rail.tiles.Block; import de.srsoftware.web4rail.tiles.BlockContact; import de.srsoftware.web4rail.tiles.Contact; @@ -92,7 +94,7 @@ public class Route extends BaseClass { public Direction startDirection; private HashSet triggeredContacts = new HashSet<>(); - private Route nextRoute; + private Route nextPreparedRoute; public Route() { conditions = new ConditionList(); @@ -227,7 +229,9 @@ public class Route extends BaseClass { } public Integer brakeTime(String brakeId) { - return brakeTimes.get(brakeId); + Integer result = brakeTimes.get(brakeId); + Collection values = brakeTimes.values(); + return values.isEmpty() ? BrakeProcess.defaultTimeStep : values.stream().mapToInt(Integer::intValue).sum()/values.size(); } public void brakeTime(String brakeId, Integer newTimeStep) { @@ -280,18 +284,18 @@ public class Route extends BaseClass { Contact nextToLastContact = contacts.get(contacts.size()-2); String trigger = nextToLastContact.trigger(); add(trigger,new BrakeStart(this)); - add(trigger,new PreserveRoute(this)); int count = 0; for (int i=0;i entry : turnouts.entrySet()) { @@ -370,6 +374,14 @@ public class Route extends BaseClass { for (String brakeId : brakeIds) brakeTimes.remove(brakeId); } + public Route dropNextPreparedRoute() { + try { + return nextPreparedRoute; + } finally { + nextPreparedRoute = null; + } + } + public Block endBlock() { return endBlock; } @@ -406,8 +418,8 @@ public class Route extends BaseClass { return sb.toString().trim(); } - public Route getNextRoute() { - return nextRoute; + public Route getNextPreparedRoute() { + return nextPreparedRoute; } public Id id() { @@ -781,8 +793,8 @@ public class Route extends BaseClass { if (lastTile instanceof Turnout) addTurnout((Turnout) lastTile,state); } - public void setNextRoute(Route nextRoute) { - this.nextRoute = nextRoute; + public void setNextPreparedRoute(Route route) { + nextPreparedRoute = route; } diff --git a/src/main/java/de/srsoftware/web4rail/actions/PreserveRoute.java b/src/main/java/de/srsoftware/web4rail/actions/PreserveRoute.java index ef99cdd..0cbbcbd 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/PreserveRoute.java +++ b/src/main/java/de/srsoftware/web4rail/actions/PreserveRoute.java @@ -29,7 +29,6 @@ public class PreserveRoute extends Action { LOG.debug("Not preserving route, as train needs to stop for {} ms at {}!",waitTime,endBlock); return false; // train is expected to wait in next block. } - - return train.reserveRouteAfter(route); + return isSet(route.getNextPreparedRoute()) || train.reserveRouteAfter(route); } } diff --git a/src/main/java/de/srsoftware/web4rail/actions/SetTurnout.java b/src/main/java/de/srsoftware/web4rail/actions/SetTurnout.java index 07d1c0f..d4280f8 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/SetTurnout.java +++ b/src/main/java/de/srsoftware/web4rail/actions/SetTurnout.java @@ -29,11 +29,7 @@ public class SetTurnout extends Action { if (isNull(turnout)) return false; if (!turnout.state(state).succeeded()) return false; if (turnout.address() == 0) return true; - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - e.printStackTrace(); - } + sleep(1000); return true; } diff --git a/src/main/java/de/srsoftware/web4rail/moving/Train.java b/src/main/java/de/srsoftware/web4rail/moving/Train.java index 4b3365f..20845cc 100644 --- a/src/main/java/de/srsoftware/web4rail/moving/Train.java +++ b/src/main/java/de/srsoftware/web4rail/moving/Train.java @@ -37,6 +37,8 @@ import de.srsoftware.web4rail.tags.Select; import de.srsoftware.web4rail.tags.Table; import de.srsoftware.web4rail.tags.Window; import de.srsoftware.web4rail.threads.RouteManager.Callback; +import de.srsoftware.web4rail.threads.BrakeProcess; +import de.srsoftware.web4rail.threads.DelayedExecution; import de.srsoftware.web4rail.threads.RouteManager; import de.srsoftware.web4rail.tiles.Block; import de.srsoftware.web4rail.tiles.Contact; @@ -94,7 +96,9 @@ public class Train extends BaseClass implements Comparable { private HashSet stuckTrace = null; - private Route nextRoute; + private Route nextPreparedRoute; + + private BrakeProcess brake; public static Object action(HashMap params, Plan plan) throws IOException { String action = params.get(ACTION); @@ -404,10 +408,21 @@ public class Train extends BaseClass implements Comparable { trace.clear(); } + private BrakeProcess endBrake() { + if (isNull(brake)) return null; + try { + return brake.end(); + } finally { + brake = null; + } + } + public void endRoute(Block endBlock, Direction endDirection) { - setSpeed(0); - nextRoute = route.getNextRoute(); + BrakeProcess brake = endBrake(); + if (isSet(brake)) brake.updateTime(); Integer waitTime = route.waitTime(); + nextPreparedRoute = route.dropNextPreparedRoute(); + if (isNull(nextPreparedRoute) || (isSet(waitTime) && waitTime > 0)) setSpeed(0); route = null; direction = endDirection; endBlock.add(this, direction); @@ -415,8 +430,18 @@ public class Train extends BaseClass implements Comparable { trace.add(endBlock); stuckTrace = null; if (autopilot) { - if (isSet(waitTime) && waitTime > 0) sleep(waitTime); - start(false); + if (isNull(waitTime) || waitTime == 0) { + start(false); + } else if (isSet(waitTime) && waitTime > 0) { + new DelayedExecution(waitTime,this) { + + @Override + public void execute() { + if (autopilot) Train.this.start(false); + } + }; + plan.stream(t("{} waiting {} secs",this,(int)(waitTime/1000))); + } } } @@ -446,6 +471,11 @@ public class Train extends BaseClass implements Comparable { } return false; } + + public boolean hasNextPreparedRoute() { + return isSet(nextPreparedRoute) || (isSet(route) && isSet(route.getNextPreparedRoute())); + } + public Train heading(Direction dir) { LOG.debug("{}.heading({})",this,dir); @@ -460,7 +490,7 @@ public class Train extends BaseClass implements Comparable { public boolean isStoppable() { if (speed > 0) return true; - if (isSet(routeManager) && routeManager.isSearching()) return true; + if (isSet(routeManager) && routeManager.isActive()) return true; if (isSet(route)) return true; return false; } @@ -689,6 +719,7 @@ public class Train extends BaseClass implements Comparable { public String quitAutopilot() { if (isSet(routeManager)) routeManager.quit(); + autopilot = false; return t("Autopilot already was disabled!"); } @@ -724,19 +755,19 @@ public class Train extends BaseClass implements Comparable { return tags().iterator(); } - public boolean reserveRouteAfter(Route r) { - LOG.debug("reserveRouteAfter({})",r); + public boolean reserveRouteAfter(Route route) { + LOG.debug("reserveRouteAfter({})",route); if (isNull(routeManager)) routeManager = new RouteManager(); - Context newContext = new Context(this).block(r.endBlock()).direction(r.endDirection); + Context newContext = new Context(this).block(route.endBlock()).direction(route.endDirection); if (routeManager.isSearching()) return false; routeManager.setContext(newContext); - return (routeManager.setCallback(new Callback() { - + routeManager.setCallback(new Callback() { @Override - public void routePrepared(Route route) { - r.setNextRoute(route); + public void routePrepared(Route nextRoute) { + route.setNextPreparedRoute(nextRoute); } - })); + }); + return routeManager.prepareRoute(); } /** @@ -869,9 +900,7 @@ public class Train extends BaseClass implements Comparable { } else if (remaining.name.length()+car.name().length()<30){ remaining.name += ", "+car.name(); } - } else { - LOG.debug("Skipping {}",cars.get(i)); - } + } else LOG.debug("Skipping {}",cars.get(i)); } if (remaining.cars.isEmpty()) return false; remaining.direction = this.direction; @@ -885,8 +914,8 @@ public class Train extends BaseClass implements Comparable { public String start(boolean auto) { autopilot |= auto; - if (isSet(nextRoute) && nextRoute.start()) { - nextRoute = null; + if (isSet(nextPreparedRoute) && nextPreparedRoute.start()) { + nextPreparedRoute = null; return null; } if (isNull(routeManager)) routeManager = new RouteManager(); @@ -899,7 +928,7 @@ public class Train extends BaseClass implements Comparable { } }; - if (routeManager.isSearching()) { // es wird bereits eine Anschlussroute gesucht + if (routeManager.isActive()) { // es wird bereits eine Anschlussroute gesucht // in diesem Fall muss bloß dass Callback des Route-Managers aktualisiert werden routeManager.setCallback(callback); } else { // routeManager nicht aktiv →> neuen Context setzen und starten @@ -916,9 +945,14 @@ public class Train extends BaseClass implements Comparable { for (Train train : BaseClass.listElements(Train.class)) LOG.info(train.start(true)); } - public void startBrake() {} + public void startBrake() { + LOG.debug("{}.startBrake()",this); + if (isSet(nextPreparedRoute)) return; + brake = new BrakeProcess(this); + } public Window stopNow() { + endBrake(); setSpeed(0); quitAutopilot(); if (isSet(route)) { @@ -994,8 +1028,6 @@ public class Train extends BaseClass implements Comparable { } public Context updateTrace(Context context) { - // TOSO: neu implementieren! - // Beachten: Route aus Context, plan.freeBehindTrain LOG.debug("updateTrace({})",context); Tile from = context.tile(); if (isNull(from)) from = context.contact(); diff --git a/src/main/java/de/srsoftware/web4rail/threads/BrakeProcess.java b/src/main/java/de/srsoftware/web4rail/threads/BrakeProcess.java new file mode 100644 index 0000000..2e92eae --- /dev/null +++ b/src/main/java/de/srsoftware/web4rail/threads/BrakeProcess.java @@ -0,0 +1,67 @@ +package de.srsoftware.web4rail.threads; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import de.srsoftware.web4rail.Application; +import de.srsoftware.web4rail.BaseClass; +import de.srsoftware.web4rail.moving.Train; + +public class BrakeProcess extends BaseClass implements Runnable{ + + private static final Logger LOG = LoggerFactory.getLogger(BrakeProcess.class); + + public static int defaultTimeStep = 500; + private Train train; + private int targetSpeed = Train.defaultEndSpeed; + boolean ended = false; + long distance = 0; + private int startSpeed; + private int lastSpeed; + private long lastTime; + + public BrakeProcess(Train train) { + this.train = train; + new Thread(this, Application.threadName(this)).start(); + } + + public BrakeProcess end() { + LOG.debug("{}.end()",this); + ended = true; + return this; + } + + @Override + public void run() { + Integer delay = train.route().brakeTime(train.brakeId()); + startSpeed = train.speed; + lastTime = timestamp(); + while (!train.hasNextPreparedRoute()) { + sleep(delay); + lastSpeed = train.speed; + updateDistance(); + if (lastSpeed > targetSpeed) lastSpeed -= 10; + if (lastSpeed < targetSpeed && (ended = true)) lastSpeed = targetSpeed; + if (ended) break; + if (lastSpeed != train.speed) train.setSpeed(lastSpeed); + } + } + + private void updateDistance() { + long newTime = timestamp(); + distance += (newTime-lastTime)*lastSpeed; + lastTime = newTime; + LOG.debug("measured distance: {} units",distance); + } + + @Override + public String toString() { + return getClass().getSimpleName()+"("+train+")"; + } + + public void updateTime() { + updateDistance(); + LOG.debug("updateTime(): start speed was {} {}.",startSpeed,BaseClass.speedUnit); + // TODO + } +} diff --git a/src/main/java/de/srsoftware/web4rail/threads/ControlUnit.java b/src/main/java/de/srsoftware/web4rail/threads/ControlUnit.java index e2dc9fc..2aae71c 100644 --- a/src/main/java/de/srsoftware/web4rail/threads/ControlUnit.java +++ b/src/main/java/de/srsoftware/web4rail/threads/ControlUnit.java @@ -300,7 +300,7 @@ public class ControlUnit extends Thread implements Constants{ private void startInfoThread() { infoSocket = commandSocket; // handshake läuft immer über commandSocket und commandScanner infoScanner = commandScanner; - Thread infoThread = new Thread() { + new Thread(Application.threadName("CU.InfoThread")) { @Override public void run() { @@ -318,14 +318,12 @@ public class ControlUnit extends Thread implements Constants{ case FEEDBACK: int addr = Integer.parseInt(parts[5]); boolean active = !parts[6].equals("0"); - Thread thread = new Thread() { + new Thread(Application.threadName("CU.FeedBack("+addr+")")) { @Override public void run() { plan.sensor(addr,active); } - }; - thread.setName(Application.threadName("CU.FeedBack("+addr+")")); - thread.start(); + }.start(); case ACESSORY: break; default: @@ -351,9 +349,7 @@ public class ControlUnit extends Thread implements Constants{ public String toString() { return "CU.InfoThread"; } - }; - infoThread.setName(Application.threadName("CU.InfoThread")); - infoThread.start(); + }.start(); } /** diff --git a/src/main/java/de/srsoftware/web4rail/threads/DelayedExecution.java b/src/main/java/de/srsoftware/web4rail/threads/DelayedExecution.java index fb95d01..b05390f 100644 --- a/src/main/java/de/srsoftware/web4rail/threads/DelayedExecution.java +++ b/src/main/java/de/srsoftware/web4rail/threads/DelayedExecution.java @@ -10,9 +10,8 @@ public abstract class DelayedExecution extends Thread { } public DelayedExecution(int delay,Object cause) { + super(Application.threadName(cause)); this.delay = delay; - - setName(Application.threadName(cause)); start(); } diff --git a/src/main/java/de/srsoftware/web4rail/threads/RouteManager.java b/src/main/java/de/srsoftware/web4rail/threads/RouteManager.java index 01fc984..e1d81e0 100644 --- a/src/main/java/de/srsoftware/web4rail/threads/RouteManager.java +++ b/src/main/java/de/srsoftware/web4rail/threads/RouteManager.java @@ -9,6 +9,7 @@ import java.util.Vector; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import de.srsoftware.web4rail.Application; import de.srsoftware.web4rail.BaseClass; import de.srsoftware.web4rail.Plan.Direction; import de.srsoftware.web4rail.Route; @@ -17,6 +18,10 @@ import de.srsoftware.web4rail.tiles.Block; import de.srsoftware.web4rail.tiles.Tile; public class RouteManager extends BaseClass { + + private enum State{ + IDLE,SEARCHING,PREPARING + } public static abstract class Callback { public abstract void routePrepared(Route route); @@ -24,11 +29,11 @@ public class RouteManager extends BaseClass { private static final Logger LOG = LoggerFactory.getLogger(RouteManager.class); private Context ctx; + private State state; private Callback callback; - private boolean searching; - + public RouteManager() { - searching = false; + state =State.IDLE; } private static TreeMap> availableRoutes(Context context, HashSet visitedRoutes) { @@ -152,6 +157,38 @@ public class RouteManager extends BaseClass { } return null; } + + public boolean isActive() { + return state != State.IDLE; + } + + public boolean isSearching() { + return state == State.SEARCHING; + } + + public boolean prepareRoute() { + try { + if (isNull(ctx) || ctx.invalidated()) return false; + state = State.SEARCHING; + Route route = chooseRoute(ctx); + if (isNull(route)) return false; + ctx.route(route); + if (!route.reserveFor(ctx)) { + route.reset(); + return false; + } + state = State.PREPARING; + if (!route.prepareAndLock()) { + route.reset(); + return false; + } + if (isNull(route) || isNull(callback)) return false; + callback.routePrepared(route); + return true; + } finally { + state = State.IDLE; + } + } public void quit() { LOG.debug("{}.quit", this); @@ -159,49 +196,25 @@ public class RouteManager extends BaseClass { if (isSet(ctx)) ctx.invalidate(); } - public Route prepareRoute(Context context) { - if (isNull(context) || context.invalidated()) return null; - Route route = chooseRoute(context); - if (isNull(route)) return null; - context.route(route); - if (!route.reserveFor(context)) { - route.reset(); - return null; - } - if (!route.prepareAndLock()) { - route.reset(); - return null; - } - return route; - } - public void setContext(Context context) { ctx = context; } - public boolean setCallback(Callback callback) { - if (ctx.invalidated()) return false; - this.callback = callback; - if (searching) return false; // search already running, do not start again! - searching = true; - Route route = prepareRoute(ctx); - searching = false; - if (isNull(route) || isNull(callback)) return false; - callback.routePrepared(route); - return true; - } - - public boolean isSearching() { - return searching; + public void setCallback(Callback callback) { + if (!ctx.invalidated()) this.callback = callback; } public void start(Callback callback) { - Thread thread = new Thread() { + setCallback(callback); + new Thread(Application.threadName(this)) { public void run() { - setCallback(callback); + prepareRoute(); }; - }; - thread.setName(getClass().getSimpleName() + "(" + ctx.train() + ")"); - thread.start(); + }.start(); + } + + @Override + public String toString() { + return getClass().getSimpleName() + "(" + ctx.train() + ")"; } } diff --git a/src/main/java/de/srsoftware/web4rail/tiles/Block.java b/src/main/java/de/srsoftware/web4rail/tiles/Block.java index bb09a94..a494ee7 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/Block.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/Block.java @@ -88,7 +88,7 @@ public abstract class Block extends StretchableTile{ @Override public String toString() { - return trains.toString(); + return trains.stream().map(t -> t+"→"+directionOf(t)).reduce((s1,s2) -> s1+", "+s2).orElse(t("empty")); } } private static final String ALLOW_TURN = "allowTurn"; @@ -192,6 +192,7 @@ public abstract class Block extends StretchableTile{ private Vector waitTimes = new Vector(); public void add(Train train,Direction direction) { + if (isNull(train)) return; train.register(); trains.add(train,direction); } diff --git a/src/main/java/de/srsoftware/web4rail/tiles/Contact.java b/src/main/java/de/srsoftware/web4rail/tiles/Contact.java index a12c376..e36953f 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/Contact.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/Contact.java @@ -53,7 +53,7 @@ public class Contact extends Tile{ boolean aborted = false; public OffTimer() { - setName(Application.threadName("OffTimer("+Contact.this+")")); + super(Application.threadName("OffTimer("+Contact.this+")")); start(); } @@ -112,7 +112,12 @@ public class Contact extends Tile{ @Override public Object click(boolean shift) throws IOException { - if (!shift) trigger(200); + if (!shift) new Thread(Application.threadName(this)) { + @Override + public void run() { + trigger(200); + } + }.start(); return super.click(shift); } diff --git a/src/main/java/de/srsoftware/web4rail/tiles/Switch.java b/src/main/java/de/srsoftware/web4rail/tiles/Switch.java index 5a14f43..7a00a3d 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/Switch.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/Switch.java @@ -164,7 +164,7 @@ public class Switch extends Tile{ public void state(boolean newState) { state = newState; - Thread thread = new Thread() { + new Thread(Application.threadName(this)) { @Override public void run() { @@ -173,9 +173,7 @@ public class Switch extends Tile{ actionsOn.fire(context,Switch.this); } else actionsOff.fire(context,Switch.this); } - }; - thread.setName(Application.threadName(this)); - thread.start(); + }.start(); stream(); } diff --git a/src/main/java/de/srsoftware/web4rail/tiles/Turnout.java b/src/main/java/de/srsoftware/web4rail/tiles/Turnout.java index 0c60bc0..b0be00b 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/Turnout.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/Turnout.java @@ -161,7 +161,8 @@ public abstract class Turnout extends Tile implements Device{ public Reply state(State newState) { if (is(Status.LOCKED,Status.OCCUPIED) && newState != state) return new Reply(415, t("{} locked by {}!",this,train())); if (address == 0) { - state = newState; + sleep(300); + state = newState; plan.place(this); return new Reply(200,"OK"); } From de19b8594a39657dc67856244cebb0c7c0f57838 Mon Sep 17 00:00:00 2001 From: Stephan Richter Date: Thu, 18 Mar 2021 00:00:02 +0100 Subject: [PATCH 23/26] logback aktualisiert --- pom.xml | 2 +- resources/logback.xml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9effb7f..5a8fa55 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 de.srsoftware web4rail - 1.3.71 + 1.3.72 Web4Rail jar Java Model Railway Control diff --git a/resources/logback.xml b/resources/logback.xml index e941c08..54dfeb3 100644 --- a/resources/logback.xml +++ b/resources/logback.xml @@ -37,6 +37,7 @@ + From 9b9c65542cff40aece3fe10fc313b9ac034cda72 Mon Sep 17 00:00:00 2001 From: Stephan Richter Date: Thu, 18 Mar 2021 00:57:10 +0100 Subject: [PATCH 24/26] bugfixes --- pom.xml | 2 +- resources/css/style.css | 33 +++++++++---------- .../web4rail/actions/ActionList.java | 4 --- .../web4rail/actions/PreserveRoute.java | 1 + .../web4rail/actions/SetSignal.java | 1 + .../srsoftware/web4rail/actions/SetSpeed.java | 1 + .../web4rail/actions/SetTurnout.java | 1 + .../de/srsoftware/web4rail/moving/Train.java | 5 ++- .../web4rail/threads/BrakeProcess.java | 4 +-- 9 files changed, 27 insertions(+), 25 deletions(-) diff --git a/pom.xml b/pom.xml index 5a8fa55..e6ad36e 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 de.srsoftware web4rail - 1.3.72 + 1.3.73 Web4Rail jar Java Model Railway Control diff --git a/resources/css/style.css b/resources/css/style.css index 217659d..2d980de 100644 --- a/resources/css/style.css +++ b/resources/css/style.css @@ -78,21 +78,32 @@ svg.Relay rect{ } svg.reserved polygon, -svg.reserved rect:not(.sig_a):not(.sig_b){ +svg.reserved rect{ fill: yellow; } svg.locked polygon, -svg.locked rect:not(.sig_a):not(.sig_b){ +svg.locked rect{ fill: lime; } -.occupied .block, svg.occupied polygon, -svg.occupied rect:not(.sig_a):not(.sig_b){ +svg.occupied rect{ fill: orange; } +svg.preview circle, +svg.preview line, +svg.preview polygon, +svg.preview rect{ + fill:cyan; +} + +svg rect.sig_a, +svg rect.sig_b{ + fill: inherit; +} + svg text{ font-size: 38px; font-family: sans-serif; @@ -365,18 +376,6 @@ textarea.json { margin-left: 5%; } -svg.preview circle, -svg.preview line, -svg.preview polygon, -svg.preview rect{ - fill:cyan; -} - -svg.preview rect.sig_a, -svg.preview rect.sig_b{ - fill: inherit; -} - svg.Block text{ fill: black; } @@ -431,4 +430,4 @@ svg.Block text{ } .Switch.on rect.enabled{ fill: forestgreen; -} \ No newline at end of file +} diff --git a/src/main/java/de/srsoftware/web4rail/actions/ActionList.java b/src/main/java/de/srsoftware/web4rail/actions/ActionList.java index f73cefa..d662cda 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/ActionList.java +++ b/src/main/java/de/srsoftware/web4rail/actions/ActionList.java @@ -82,10 +82,6 @@ public class ActionList extends Action implements Iterable{ public boolean fire(Context context,Object cause) { for (Action action : actions) { - if (context.invalidated()) { - LOG.debug("Context has been invalidated, aborting {}",this); - return false; - } LOG.debug("firing \"{}\"",action); if (!action.fire(context,cause)) { LOG.warn("{} failed",action); diff --git a/src/main/java/de/srsoftware/web4rail/actions/PreserveRoute.java b/src/main/java/de/srsoftware/web4rail/actions/PreserveRoute.java index 0cbbcbd..2464c34 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/PreserveRoute.java +++ b/src/main/java/de/srsoftware/web4rail/actions/PreserveRoute.java @@ -13,6 +13,7 @@ public class PreserveRoute extends Action { @Override public boolean fire(Context context,Object cause) { + if (context.invalidated()) return false; Train train = context.train(); Route route = context.route(); // These are errors: diff --git a/src/main/java/de/srsoftware/web4rail/actions/SetSignal.java b/src/main/java/de/srsoftware/web4rail/actions/SetSignal.java index 883f9b3..e776bed 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/SetSignal.java +++ b/src/main/java/de/srsoftware/web4rail/actions/SetSignal.java @@ -35,6 +35,7 @@ public class SetSignal extends Action { @Override public boolean fire(Context context,Object cause) { + if (context.invalidated()) return false; if (isNull(signal)) return false; return signal.state(state); } diff --git a/src/main/java/de/srsoftware/web4rail/actions/SetSpeed.java b/src/main/java/de/srsoftware/web4rail/actions/SetSpeed.java index 0d335c5..0d52c21 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/SetSpeed.java +++ b/src/main/java/de/srsoftware/web4rail/actions/SetSpeed.java @@ -27,6 +27,7 @@ public class SetSpeed extends Action{ @Override public boolean fire(Context context,Object cause) { + if (context.invalidated()) return false; if (isNull(context.train())) return false; context.train().setSpeed(speed); return true; diff --git a/src/main/java/de/srsoftware/web4rail/actions/SetTurnout.java b/src/main/java/de/srsoftware/web4rail/actions/SetTurnout.java index d4280f8..dcdf36f 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/SetTurnout.java +++ b/src/main/java/de/srsoftware/web4rail/actions/SetTurnout.java @@ -26,6 +26,7 @@ public class SetTurnout extends Action { @Override public boolean fire(Context context,Object cause) { + if (context.invalidated()) return false; if (isNull(turnout)) return false; if (!turnout.state(state).succeeded()) return false; if (turnout.address() == 0) return true; diff --git a/src/main/java/de/srsoftware/web4rail/moving/Train.java b/src/main/java/de/srsoftware/web4rail/moving/Train.java index 20845cc..315e007 100644 --- a/src/main/java/de/srsoftware/web4rail/moving/Train.java +++ b/src/main/java/de/srsoftware/web4rail/moving/Train.java @@ -570,7 +570,10 @@ public class Train extends BaseClass implements Comparable { }); if (json.has(BLOCK)) {// do not move this up! during set, other fields will be referenced! currentBlock = (Block) plan.get(Id.from(json, BLOCK), false); - if (isSet(currentBlock)) currentBlock.add(Train.this, direction); + if (isSet(currentBlock)) { + currentBlock.setTrain(Train.this); + currentBlock.add(Train.this, direction); + } } } }; diff --git a/src/main/java/de/srsoftware/web4rail/threads/BrakeProcess.java b/src/main/java/de/srsoftware/web4rail/threads/BrakeProcess.java index 2e92eae..16fd781 100644 --- a/src/main/java/de/srsoftware/web4rail/threads/BrakeProcess.java +++ b/src/main/java/de/srsoftware/web4rail/threads/BrakeProcess.java @@ -41,9 +41,9 @@ public class BrakeProcess extends BaseClass implements Runnable{ lastSpeed = train.speed; updateDistance(); if (lastSpeed > targetSpeed) lastSpeed -= 10; - if (lastSpeed < targetSpeed && (ended = true)) lastSpeed = targetSpeed; if (ended) break; - if (lastSpeed != train.speed) train.setSpeed(lastSpeed); + if (lastSpeed <= targetSpeed && (ended = true)) lastSpeed = targetSpeed; + train.setSpeed(lastSpeed); } } From 540d1be4898ddd53720c0d4fcc3a7f7ea736bca9 Mon Sep 17 00:00:00 2001 From: Stephan Richter Date: Thu, 18 Mar 2021 17:27:09 +0100 Subject: [PATCH 25/26] Another refactoring step... --- .../de/srsoftware/web4rail/BaseClass.java | 8 ++ .../de/srsoftware/web4rail/EventListener.java | 5 + .../java/de/srsoftware/web4rail/Route.java | 26 +++- .../web4rail/actions/PreserveRoute.java | 5 +- .../web4rail/conditions/BlockFree.java | 2 +- .../de/srsoftware/web4rail/moving/Train.java | 57 +++----- .../{RouteManager.java => RoutePrepper.java} | 127 +++++++++--------- 7 files changed, 126 insertions(+), 104 deletions(-) create mode 100644 src/main/java/de/srsoftware/web4rail/EventListener.java rename src/main/java/de/srsoftware/web4rail/threads/{RouteManager.java => RoutePrepper.java} (75%) diff --git a/src/main/java/de/srsoftware/web4rail/BaseClass.java b/src/main/java/de/srsoftware/web4rail/BaseClass.java index 5622b23..887e4aa 100644 --- a/src/main/java/de/srsoftware/web4rail/BaseClass.java +++ b/src/main/java/de/srsoftware/web4rail/BaseClass.java @@ -8,6 +8,7 @@ import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Random; @@ -69,6 +70,7 @@ public abstract class BaseClass implements Constants{ private Contact contact; private Direction direction; private Integer waitTime; + List invalidationListeners = new LinkedList<>(); public Context(BaseClass object) { setMain(object); @@ -147,12 +149,18 @@ public abstract class BaseClass implements Constants{ public void invalidate() { setMain(null); + invalidationListeners.forEach(EventListener::fire); } public boolean invalidated() { return isNull(main); } + public void onInvalidate(EventListener listener) { + invalidationListeners.add(listener); + } + + public Context setMain(BaseClass object) { main = object; LOG.debug("{}.setMain({})",this,object); diff --git a/src/main/java/de/srsoftware/web4rail/EventListener.java b/src/main/java/de/srsoftware/web4rail/EventListener.java new file mode 100644 index 0000000..4c275fa --- /dev/null +++ b/src/main/java/de/srsoftware/web4rail/EventListener.java @@ -0,0 +1,5 @@ +package de.srsoftware.web4rail; + +public interface EventListener { + public void fire(); +} diff --git a/src/main/java/de/srsoftware/web4rail/Route.java b/src/main/java/de/srsoftware/web4rail/Route.java index d380827..57d710f 100644 --- a/src/main/java/de/srsoftware/web4rail/Route.java +++ b/src/main/java/de/srsoftware/web4rail/Route.java @@ -38,6 +38,7 @@ import de.srsoftware.web4rail.tags.Input; import de.srsoftware.web4rail.tags.Table; import de.srsoftware.web4rail.tags.Window; import de.srsoftware.web4rail.threads.BrakeProcess; +import de.srsoftware.web4rail.threads.RoutePrepper; import de.srsoftware.web4rail.tiles.Block; import de.srsoftware.web4rail.tiles.BlockContact; import de.srsoftware.web4rail.tiles.Contact; @@ -95,6 +96,8 @@ public class Route extends BaseClass { private HashSet triggeredContacts = new HashSet<>(); private Route nextPreparedRoute; + + private RoutePrepper nextRoutePrepper; public Route() { conditions = new ConditionList(); @@ -394,8 +397,6 @@ public class Route extends BaseClass { } private void free() { - context.invalidate(); // do not set to null: - // this action may be called from route.contact → finishRoute, which calls train.updateTrace afterwards, which in turn requires context Train train = context.train(); Vector reversedPath = reverse(path()); for (Tile tile : reversedPath) { @@ -684,6 +685,19 @@ public class Route extends BaseClass { return true; } + public boolean prepareNext(Context context) { + if (isSet(nextRoutePrepper)) return false; + nextRoutePrepper = new RoutePrepper(context); + nextRoutePrepper.onRoutePrepared(() -> { + nextPreparedRoute = nextRoutePrepper.route(); + nextRoutePrepper = null; + }); + nextRoutePrepper.onFail(() -> { + nextRoutePrepper = null; + }); + return nextRoutePrepper.prepareRoute(); + } + private Tag previewScript() { Tag script = new Tag("script").attr("type", "text/javascript"); for (Tile tile : path) { @@ -752,7 +766,7 @@ public class Route extends BaseClass { public boolean reserveFor(Context newContext) { LOG.debug("{}.reserverFor({})",this,newContext); - if (isSet(context)) return false; // route already has context! +// if (isSet(context)) return false; // route already has context! context = newContext; for (Tile tile : path) { if (newContext.invalidated() || !tile.reserveFor(newContext)) { @@ -765,6 +779,7 @@ public class Route extends BaseClass { public boolean reset() { LOG.debug("{}.reset()",this); + resetNext(); setSignals(Signal.RED); Train train = context.train(); free(); @@ -773,6 +788,11 @@ public class Route extends BaseClass { return true; } + public void resetNext() { + if (isSet(nextRoutePrepper)) nextRoutePrepper.stop(); + if (isSet(nextPreparedRoute)) nextPreparedRoute.reset(); + } + public static void saveAll(String filename) throws IOException { BufferedWriter file = new BufferedWriter(new FileWriter(filename)); file.write("{\""+ROUTES+"\":[\n"); diff --git a/src/main/java/de/srsoftware/web4rail/actions/PreserveRoute.java b/src/main/java/de/srsoftware/web4rail/actions/PreserveRoute.java index 2464c34..3bebb91 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/PreserveRoute.java +++ b/src/main/java/de/srsoftware/web4rail/actions/PreserveRoute.java @@ -30,6 +30,9 @@ public class PreserveRoute extends Action { LOG.debug("Not preserving route, as train needs to stop for {} ms at {}!",waitTime,endBlock); return false; // train is expected to wait in next block. } - return isSet(route.getNextPreparedRoute()) || train.reserveRouteAfter(route); + if (isSet(route.getNextPreparedRoute())) return true; + Context nextContext = new Context(train).block(route.endBlock()).direction(route.endDirection); + context.onInvalidate(nextContext::invalidate); + return route.prepareNext(nextContext); } } diff --git a/src/main/java/de/srsoftware/web4rail/conditions/BlockFree.java b/src/main/java/de/srsoftware/web4rail/conditions/BlockFree.java index 6cb9d99..4784e60 100644 --- a/src/main/java/de/srsoftware/web4rail/conditions/BlockFree.java +++ b/src/main/java/de/srsoftware/web4rail/conditions/BlockFree.java @@ -25,7 +25,7 @@ public class BlockFree extends Condition { @Override public boolean fulfilledBy(Context context) { - return block.isFreeFor(null) != inverted; + return block.isFreeFor(context) != inverted; } @Override diff --git a/src/main/java/de/srsoftware/web4rail/moving/Train.java b/src/main/java/de/srsoftware/web4rail/moving/Train.java index 315e007..0f5ed32 100644 --- a/src/main/java/de/srsoftware/web4rail/moving/Train.java +++ b/src/main/java/de/srsoftware/web4rail/moving/Train.java @@ -36,10 +36,9 @@ import de.srsoftware.web4rail.tags.Label; import de.srsoftware.web4rail.tags.Select; import de.srsoftware.web4rail.tags.Table; import de.srsoftware.web4rail.tags.Window; -import de.srsoftware.web4rail.threads.RouteManager.Callback; import de.srsoftware.web4rail.threads.BrakeProcess; import de.srsoftware.web4rail.threads.DelayedExecution; -import de.srsoftware.web4rail.threads.RouteManager; +import de.srsoftware.web4rail.threads.RoutePrepper; import de.srsoftware.web4rail.tiles.Block; import de.srsoftware.web4rail.tiles.Contact; import de.srsoftware.web4rail.tiles.Tile; @@ -92,7 +91,7 @@ public class Train extends BaseClass implements Comparable { public int speed = 0; private static final String SHUNTING = "shunting"; private boolean shunting = false; - private RouteManager routeManager = null; + private RoutePrepper routePrepper = null; private HashSet stuckTrace = null; @@ -422,7 +421,7 @@ public class Train extends BaseClass implements Comparable { if (isSet(brake)) brake.updateTime(); Integer waitTime = route.waitTime(); nextPreparedRoute = route.dropNextPreparedRoute(); - if (isNull(nextPreparedRoute) || (isSet(waitTime) && waitTime > 0)) setSpeed(0); + if ((!autopilot)|| isNull(nextPreparedRoute) || (isSet(waitTime) && waitTime > 0)) setSpeed(0); route = null; direction = endDirection; endBlock.add(this, direction); @@ -490,7 +489,7 @@ public class Train extends BaseClass implements Comparable { public boolean isStoppable() { if (speed > 0) return true; - if (isSet(routeManager) && routeManager.isActive()) return true; + if (isSet(routePrepper)) return true; if (isSet(route)) return true; return false; } @@ -721,7 +720,8 @@ public class Train extends BaseClass implements Comparable { } public String quitAutopilot() { - if (isSet(routeManager)) routeManager.quit(); + if (isSet(routePrepper)) routePrepper.stop(); +// if (isSet(route)) route.resetNext(); autopilot = false; return t("Autopilot already was disabled!"); } @@ -747,7 +747,7 @@ public class Train extends BaseClass implements Comparable { //if (child == nextRoute) nextRoute = null; // TODO if (child == currentBlock) currentBlock = null; if (child == destination) destination = null; - if (child == routeManager) routeManager = null; + if (child == routePrepper) routePrepper.stop(); cars.remove(child); trace.remove(child); super.removeChild(child); @@ -758,21 +758,6 @@ public class Train extends BaseClass implements Comparable { return tags().iterator(); } - public boolean reserveRouteAfter(Route route) { - LOG.debug("reserveRouteAfter({})",route); - if (isNull(routeManager)) routeManager = new RouteManager(); - Context newContext = new Context(this).block(route.endBlock()).direction(route.endDirection); - if (routeManager.isSearching()) return false; - routeManager.setContext(newContext); - routeManager.setCallback(new Callback() { - @Override - public void routePrepared(Route nextRoute) { - route.setNextPreparedRoute(nextRoute); - } - }); - return routeManager.prepareRoute(); - } - /** * This turns the train as if it went through a loop. Example: * before: CabCar→ MiddleCar→ Loco→ @@ -921,24 +906,20 @@ public class Train extends BaseClass implements Comparable { nextPreparedRoute = null; return null; } - if (isNull(routeManager)) routeManager = new RouteManager(); + if (isSet(routePrepper)) return t("Already searching route for {}",this); + routePrepper = new RoutePrepper(new Context(this).block(currentBlock).direction(direction)); - Callback callback = new Callback() { - @Override - public void routePrepared(Route route) { - route.start(); - plan.stream(t("Started {}",Train.this)); - } - }; + routePrepper.onRoutePrepared(() -> { + routePrepper.route().start(); + routePrepper = null; + plan.stream(t("Started {}",Train.this)); + }); - if (routeManager.isActive()) { // es wird bereits eine Anschlussroute gesucht - // in diesem Fall muss bloß dass Callback des Route-Managers aktualisiert werden - routeManager.setCallback(callback); - } else { // routeManager nicht aktiv →> neuen Context setzen und starten - routeManager.setContext(new Context(this).block(currentBlock).direction(direction)); - routeManager.start(callback); - } + routePrepper.onFail(() -> { + routePrepper = null; + }); + routePrepper.start(); return null; } @@ -950,7 +931,7 @@ public class Train extends BaseClass implements Comparable { public void startBrake() { LOG.debug("{}.startBrake()",this); - if (isSet(nextPreparedRoute)) return; + if (autopilot && isSet(nextPreparedRoute)) return; brake = new BrakeProcess(this); } diff --git a/src/main/java/de/srsoftware/web4rail/threads/RouteManager.java b/src/main/java/de/srsoftware/web4rail/threads/RoutePrepper.java similarity index 75% rename from src/main/java/de/srsoftware/web4rail/threads/RouteManager.java rename to src/main/java/de/srsoftware/web4rail/threads/RoutePrepper.java index e1d81e0..c2e0c93 100644 --- a/src/main/java/de/srsoftware/web4rail/threads/RouteManager.java +++ b/src/main/java/de/srsoftware/web4rail/threads/RoutePrepper.java @@ -1,45 +1,42 @@ package de.srsoftware.web4rail.threads; import java.util.HashSet; +import java.util.LinkedList; import java.util.List; import java.util.Map.Entry; import java.util.TreeMap; import java.util.Vector; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import de.srsoftware.web4rail.Application; import de.srsoftware.web4rail.BaseClass; +import de.srsoftware.web4rail.EventListener; import de.srsoftware.web4rail.Plan.Direction; import de.srsoftware.web4rail.Route; import de.srsoftware.web4rail.moving.Train; import de.srsoftware.web4rail.tiles.Block; import de.srsoftware.web4rail.tiles.Tile; -public class RouteManager extends BaseClass { +public class RoutePrepper extends BaseClass implements Runnable{ + private Context context; + private Route route; + private List failListeners = new LinkedList<>(); + private List foundListeners = new LinkedList<>(); + private List lockedListeners = new LinkedList<>(); + private List preparedListeners= new LinkedList<>(); - private enum State{ - IDLE,SEARCHING,PREPARING + public RoutePrepper(Context c) { + List errors = new LinkedList<>(); + if (isNull(c.train())) errors.add(t("No train in context for {}",getClass().getSimpleName())); + if (isNull(c.block())) errors.add(t("No block in context for {}",getClass().getSimpleName())); + if (isNull(c.direction())) errors.add(t("No direction in context for {}",getClass().getSimpleName())); + if (!errors.isEmpty()) throw new NullPointerException(String.join(", ", errors)); + context = c; } - - public static abstract class Callback { - public abstract void routePrepared(Route route); - } - - private static final Logger LOG = LoggerFactory.getLogger(RouteManager.class); - private Context ctx; - private State state; - private Callback callback; - public RouteManager() { - state =State.IDLE; - } - private static TreeMap> availableRoutes(Context context, HashSet visitedRoutes) { String inset = ""; for (int i = 0; i < visitedRoutes.size(); i++) inset += " "; - LOG.debug("{}{}.availableRoutes({})", inset, RouteManager.class.getSimpleName(), context); + LOG.debug("{}{}.availableRoutes({})", inset, RoutePrepper.class.getSimpleName(), context); Block block = context.block(); Train train = context.train(); @@ -135,9 +132,9 @@ public class RouteManager extends BaseClass { } return availableRoutes; } - - public static Route chooseRoute(Context context) { - LOG.debug("{}.chooseRoute({})", RouteManager.class.getSimpleName(), context); + + private static Route chooseRoute(Context context) { + LOG.debug("{}.chooseRoute({})", RoutePrepper.class.getSimpleName(), context); TreeMap> availableRoutes = availableRoutes(context, new HashSet()); while (!availableRoutes.isEmpty()) { if (context.invalidated()) break; @@ -158,63 +155,71 @@ public class RouteManager extends BaseClass { return null; } - public boolean isActive() { - return state != State.IDLE; + + private void notify(List listeners) { + for (EventListener listener: listeners) { + listener.fire(); + } } - - public boolean isSearching() { - return state == State.SEARCHING; + + public void onFail(EventListener l) { + failListeners.add(l); } + + public void onRouteFound(EventListener l) { + foundListeners.add(l); + } + + public void onRouteLocked(EventListener l) { + lockedListeners.add(l); + } + + public void onRoutePrepared(EventListener l) { + preparedListeners.add(l); + } + + public boolean prepareRoute() { try { - if (isNull(ctx) || ctx.invalidated()) return false; - state = State.SEARCHING; - Route route = chooseRoute(ctx); - if (isNull(route)) return false; - ctx.route(route); - if (!route.reserveFor(ctx)) { + if (isNull(context) || context.invalidated()) return false; + route = chooseRoute(context); + if (isNull(route)) return false; + context.route(route); + notify(foundListeners); + if (!route.reserveFor(context)) { route.reset(); + route = null; return false; } - state = State.PREPARING; + notify(lockedListeners); if (!route.prepareAndLock()) { route.reset(); + route = null; return false; } - if (isNull(route) || isNull(callback)) return false; - callback.routePrepared(route); + notify(preparedListeners); return true; } finally { - state = State.IDLE; + if (isNull(route)) notify(failListeners); } } - public void quit() { - LOG.debug("{}.quit", this); - callback = null; - if (isSet(ctx)) ctx.invalidate(); + + public Route route() { + return route; } - public void setContext(Context context) { - ctx = context; - } - - public void setCallback(Callback callback) { - if (!ctx.invalidated()) this.callback = callback; - } - - public void start(Callback callback) { - setCallback(callback); - new Thread(Application.threadName(this)) { - public void run() { - prepareRoute(); - }; - }.start(); - } - @Override - public String toString() { - return getClass().getSimpleName() + "(" + ctx.train() + ")"; + public void run() { + prepareRoute(); + } + + public void start() { + new Thread(this,Application.threadName(this)).start(); + } + + public void stop() { + context.invalidate(); } } From 937e061892b24dc642caa8afbb5375b2986b7b5a Mon Sep 17 00:00:00 2001 From: Stephan Richter Date: Thu, 18 Mar 2021 18:56:38 +0100 Subject: [PATCH 26/26] Routenplaner scheint nun endlich sauber zu funktionieren. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Es müssen natürlich wieder einige Dinge nachimplementiert werden. --- pom.xml | 2 +- .../java/de/srsoftware/web4rail/Route.java | 20 ++++++---- .../de/srsoftware/web4rail/moving/Train.java | 20 ++++++++-- .../web4rail/threads/RoutePrepper.java | 39 ++++++++----------- .../de/srsoftware/web4rail/tiles/Block.java | 2 +- 5 files changed, 49 insertions(+), 34 deletions(-) diff --git a/pom.xml b/pom.xml index e6ad36e..8a35fac 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 de.srsoftware web4rail - 1.3.73 + 1.4.0 Web4Rail jar Java Model Railway Control diff --git a/src/main/java/de/srsoftware/web4rail/Route.java b/src/main/java/de/srsoftware/web4rail/Route.java index 57d710f..d6df5fc 100644 --- a/src/main/java/de/srsoftware/web4rail/Route.java +++ b/src/main/java/de/srsoftware/web4rail/Route.java @@ -392,13 +392,14 @@ public class Route extends BaseClass { public void finish(Train train) { LOG.debug("{}.finish()",this); train.endRoute(endBlock,endDirection); - free(); + freeIgnoring(null); train = null; } - private void free() { + private void freeIgnoring(Vector skipTiles) { Train train = context.train(); Vector reversedPath = reverse(path()); + if (isSet(skipTiles)) reversedPath.removeAll(skipTiles); for (Tile tile : reversedPath) { if (isSet(train) && !train.onTrace(tile)) tile.free(train); } @@ -693,6 +694,8 @@ public class Route extends BaseClass { nextRoutePrepper = null; }); nextRoutePrepper.onFail(() -> { + Route rt = nextRoutePrepper.route(); // Nachfolgeroute kann ja schon reserviert sein, oder gar schon teilweise vorbereitet! + if (isSet(rt)) rt.resetIgnoring(this); // angefangene Route freigeben ohne Teile der aktuellen Route freizugeben nextRoutePrepper = null; }); return nextRoutePrepper.prepareRoute(); @@ -766,7 +769,7 @@ public class Route extends BaseClass { public boolean reserveFor(Context newContext) { LOG.debug("{}.reserverFor({})",this,newContext); -// if (isSet(context)) return false; // route already has context! + context = newContext; for (Tile tile : path) { if (newContext.invalidated() || !tile.reserveFor(newContext)) { @@ -777,15 +780,18 @@ public class Route extends BaseClass { return true; } - public boolean reset() { - LOG.debug("{}.reset()",this); + public void reset() { + resetIgnoring(null); + } + + public void resetIgnoring(Route otherRoute) { + LOG.debug("{}.resetIgnoring({})",this,otherRoute); resetNext(); setSignals(Signal.RED); Train train = context.train(); - free(); + freeIgnoring(isSet(otherRoute) ? otherRoute.path : null); train.drop(this); context = null; - return true; } public void resetNext() { diff --git a/src/main/java/de/srsoftware/web4rail/moving/Train.java b/src/main/java/de/srsoftware/web4rail/moving/Train.java index 0f5ed32..f1a920d 100644 --- a/src/main/java/de/srsoftware/web4rail/moving/Train.java +++ b/src/main/java/de/srsoftware/web4rail/moving/Train.java @@ -721,9 +721,11 @@ public class Train extends BaseClass implements Comparable { public String quitAutopilot() { if (isSet(routePrepper)) routePrepper.stop(); -// if (isSet(route)) route.resetNext(); - autopilot = false; - return t("Autopilot already was disabled!"); + try { + return autopilot ? t("Autopilot disabled") : t("Autopilot already was disabled!"); + } finally { + autopilot = false; + } } @Override @@ -916,7 +918,19 @@ public class Train extends BaseClass implements Comparable { }); routePrepper.onFail(() -> { + Route failedRoute = routePrepper.route(); routePrepper = null; + if (isSet(failedRoute)) failedRoute.reset(); + LOG.debug("Starting {} failed due to unavailable route!",this); + if (autopilot) new DelayedExecution(250,this) { + + @Override + public void execute() { + if (autopilot) { + Train.this.start(false); + } + } + }; }); routePrepper.start(); diff --git a/src/main/java/de/srsoftware/web4rail/threads/RoutePrepper.java b/src/main/java/de/srsoftware/web4rail/threads/RoutePrepper.java index c2e0c93..b6b1139 100644 --- a/src/main/java/de/srsoftware/web4rail/threads/RoutePrepper.java +++ b/src/main/java/de/srsoftware/web4rail/threads/RoutePrepper.java @@ -162,6 +162,13 @@ public class RoutePrepper extends BaseClass implements Runnable{ } } + private boolean fail() { + notify(failListeners); + // if (isSet(route) route.reset(); + route = null; + return false; + } + public void onFail(EventListener l) { failListeners.add(l); } @@ -181,28 +188,16 @@ public class RoutePrepper extends BaseClass implements Runnable{ public boolean prepareRoute() { - try { - if (isNull(context) || context.invalidated()) return false; - route = chooseRoute(context); - if (isNull(route)) return false; - context.route(route); - notify(foundListeners); - if (!route.reserveFor(context)) { - route.reset(); - route = null; - return false; - } - notify(lockedListeners); - if (!route.prepareAndLock()) { - route.reset(); - route = null; - return false; - } - notify(preparedListeners); - return true; - } finally { - if (isNull(route)) notify(failListeners); - } + if (isNull(context) || context.invalidated()) return fail(); + route = chooseRoute(context); + if (isNull(route)) return fail(); + context.route(route); + notify(foundListeners); + if (!route.reserveFor(context)) return fail(); + notify(lockedListeners); + if (!route.prepareAndLock()) return fail(); + notify(preparedListeners); + return true; } diff --git a/src/main/java/de/srsoftware/web4rail/tiles/Block.java b/src/main/java/de/srsoftware/web4rail/tiles/Block.java index a494ee7..5dcaea8 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/Block.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/Block.java @@ -266,7 +266,7 @@ public abstract class Block extends StretchableTile{ @Override public boolean free(Train train) { - LOG.debug("{}.free()"); + LOG.debug("{}.free({})",this,train); Train firstTrain = trains.first(); if (isNull(firstTrain)) return true; if (firstTrain != train) return false;