diff --git a/pom.xml b/pom.xml index 4ea3aed..67dffc8 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 de.srsoftware web4rail - 1.4.8 + 1.4.9 Web4Rail jar Java Model Railway Control diff --git a/src/main/java/de/srsoftware/web4rail/Constants.java b/src/main/java/de/srsoftware/web4rail/Constants.java index a02f77d..b5fe6bd 100644 --- a/src/main/java/de/srsoftware/web4rail/Constants.java +++ b/src/main/java/de/srsoftware/web4rail/Constants.java @@ -21,6 +21,7 @@ public interface Constants { public static final String ACTION_DROP = "drop"; public static final String ACTION_EMERGENCY = "emergency"; public static final String ACTION_FASTER10 = "faster10"; + public static final String ACTION_FREE = "free"; public static final String ACTION_MOVE = "move"; public static final String ACTION_OPEN = "open"; public static final String ACTION_POWER = "power"; diff --git a/src/main/java/de/srsoftware/web4rail/Plan.java b/src/main/java/de/srsoftware/web4rail/Plan.java index c35e879..dbe6a82 100644 --- a/src/main/java/de/srsoftware/web4rail/Plan.java +++ b/src/main/java/de/srsoftware/web4rail/Plan.java @@ -203,6 +203,10 @@ public class Plan extends BaseClass{ Tile tile = get(Id.from(params), false); if (tile instanceof Bridge) return ((Bridge)tile).requestConnect(); break; + case ACTION_FREE: + Tile t = get(Id.from(params), false); + t.free(t.lockingTrain()); + return t.properties(); case ACTION_MOVE: return moveTile(params.get(DIRECTION),Id.from(params)); case ACTION_PROPS: diff --git a/src/main/java/de/srsoftware/web4rail/Route.java b/src/main/java/de/srsoftware/web4rail/Route.java index cb99f6c..de479b1 100644 --- a/src/main/java/de/srsoftware/web4rail/Route.java +++ b/src/main/java/de/srsoftware/web4rail/Route.java @@ -873,6 +873,7 @@ public class Route extends BaseClass { triggeredContacts.clear(); ActionList startActions = triggeredActions.get(ROUTE_START); + if (train.direction() != startDirection) train.turn(); if (isSet(startActions)) { context.route(this); diff --git a/src/main/java/de/srsoftware/web4rail/moving/Train.java b/src/main/java/de/srsoftware/web4rail/moving/Train.java index 7d1d7fe..599cedc 100644 --- a/src/main/java/de/srsoftware/web4rail/moving/Train.java +++ b/src/main/java/de/srsoftware/web4rail/moving/Train.java @@ -497,7 +497,7 @@ public class Train extends BaseClass implements Comparable { Integer waitTime = route.waitTime(); nextPreparedRoute = route.dropNextPreparedRoute(); if (isSet(nextPreparedRoute)) LOG.debug("nextPreparedRoute is now {}",nextPreparedRoute); - if ((!autopilot)|| isNull(nextPreparedRoute) || (isSet(waitTime) && waitTime > 0)) setSpeed(0); + if ((!autopilot) || isNull(nextPreparedRoute) || (isSet(waitTime) && waitTime > 0)) setSpeed(0); route = null; endBlock.setTrain(this); currentBlock = endBlock; diff --git a/src/main/java/de/srsoftware/web4rail/threads/RoutePrepper.java b/src/main/java/de/srsoftware/web4rail/threads/RoutePrepper.java index 87201f4..b2370b3 100644 --- a/src/main/java/de/srsoftware/web4rail/threads/RoutePrepper.java +++ b/src/main/java/de/srsoftware/web4rail/threads/RoutePrepper.java @@ -6,6 +6,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Map.Entry; import java.util.TreeMap; +import java.util.stream.Collectors; import de.srsoftware.web4rail.Application; import de.srsoftware.web4rail.BaseClass; @@ -30,7 +31,7 @@ public class RoutePrepper extends BaseClass implements Runnable{ @Override public String toString() { - return route+"(score: "+score+")"; + return route.toString().replace(")", ", score: "+score+")"); } } @@ -55,17 +56,17 @@ public class RoutePrepper extends BaseClass implements Runnable{ boolean error = false; Block startBlock = c.block(); - if (isNull(startBlock) && (error=true)) LOG.warn("RoutePrepper.findRoute(…) called without a startBlock!"); + if (isNull(startBlock) && (error=true)) LOG.warn("RoutePrepper.availableRoutes(…) called without a startBlock!"); Train train = c.train(); - if (isNull(train) && (error=true)) LOG.warn("RoutePrepper.findRoute(…) called without a startBlock!"); + if (isNull(train) && (error=true)) LOG.warn("RoutePrepper.availableRoutes(…) called without a startBlock!"); if (error) return new TreeMap<>(); Block destination = train.destination(); Direction startDirection = c.direction(); - LOG.debug("RoutePrepper.findRoute({},{},{}), dest = {}",startBlock,startDirection,train,destination); + LOG.debug("RoutePrepper.availableRoutes({},{},{}), dest = {}",startBlock,startDirection,train,destination); TreeMap> candidates = routesFrom(c); @@ -76,12 +77,23 @@ public class RoutePrepper extends BaseClass implements Runnable{ LOG.debug("{} is heading for {}, starting breadth-first search…",train,destination); - HashMap predecessors = new HashMap<>(); + HashMap predecessors = new HashMap<>() { + public String toString() { + return entrySet().stream() + .sorted((e1,e2) -> e1.getValue().toString().compareTo(e2.getValue().toString())) + .map(entry -> entry.getValue()+" → "+entry.getKey()) + .collect(Collectors.joining("\n")); + }; + }; + candidates.entrySet().stream().flatMap(entry -> entry.getValue().stream()).forEach(route -> predecessors.put(route, null)); TreeMap> routesToDest = new TreeMap<>(); int level = 0; while (!candidates.isEmpty()) { + LOG.debug("Candidates for level {}:",level); + candidates.entrySet().stream().flatMap(entry -> entry.getValue().stream()).forEach(route -> LOG.debug(" - {}",route)); + TreeMap> queue = new TreeMap<>(); while (!candidates.isEmpty()) { @@ -99,8 +111,10 @@ public class RoutePrepper extends BaseClass implements Runnable{ // However it might be the last route in a long path. // Thus, we need to get the first route in this path: while (predecessors.containsKey(candidate.route)) { - candidate = predecessors.get(candidate.route); - LOG.debug(" - predecessed by {}",candidate); + Candidate predecessor = predecessors.get(candidate.route); + if (isNull(predecessor)) break; + LOG.debug(" - {} is predecessed by {}",candidate,predecessor); + candidate = predecessor; score += candidate.score; } @@ -111,15 +125,18 @@ public class RoutePrepper extends BaseClass implements Runnable{ continue; } - LOG.debug(" - {} not reaching {}, adding ongoing routes to queue:",candidate,destination); + LOG.debug(" - {} not reaching {}, adding ongoing routes to queue:",candidate,destination); TreeMap> successors = routesFrom(c.clone().block(endBlock).direction(endDir)); while (!successors.isEmpty()) { int score = successors.firstKey(); LinkedList best = successors.remove(score); score -= 25; // Nachfolgeroute for (Route route : best) { - LOG.debug(" - queueing {} with score {}",route,score); - if (predecessors.containsKey(route)) continue; // Route wurde bereits besucht + LOG.debug(" - queueing {} with score {}",route,score); + if (predecessors.containsKey(route)) { + LOG.debug("this route already has a predecessor: {}",predecessors.get(route)); + continue; // Route wurde bereits besucht + } predecessors.put(route, candidate); LinkedList list = queue.get(score); @@ -132,11 +149,11 @@ public class RoutePrepper extends BaseClass implements Runnable{ if (!routesToDest.isEmpty()) return routesToDest; LOG.debug("No routes to {} found with distance {}!",destination,level); level ++; - candidates = queue; + candidates = queue; } LOG.debug("No more candidates for routes towards {}!",destination); - return new TreeMap<>(); + return routesToDest; } @@ -244,19 +261,19 @@ public class RoutePrepper extends BaseClass implements Runnable{ Direction startDirection = c.direction(); - LOG.debug("RoutePrepper.routesFrom({},{},{}), dest = {}",startBlock,startDirection,train,destination); + LOG.debug(" RoutePrepper.routesFrom({},{},{}), dest = {}",startBlock,startDirection,train,destination); TreeMap> routes = new TreeMap<>(); for (Route route : startBlock.leavingRoutes()) { - LOG.debug(" - evaluating {}",route); + LOG.debug(" - evaluating {}",route); int score = 0; if (!route.allowed(new Context(train).block(startBlock).direction(startDirection))) { - LOG.debug(" - {} not allowed for {}", route, train); + LOG.debug(" - {} not allowed for {}", route, train); if (route.endBlock() != destination) continue; - LOG.debug(" …overridden by destination of train!", route, train); + LOG.debug(" …overridden by destination of train!", route, train); } if (route.endBlock() == destination) score = 100_000; @@ -269,7 +286,7 @@ public class RoutePrepper extends BaseClass implements Runnable{ LinkedList routesForScore = routes.get(score); if (isNull(routesForScore)) routes.put(score, routesForScore = new LinkedList()); - LOG.debug(" → candidate!"); + LOG.debug(" → candidate!"); routesForScore.add(route); } diff --git a/src/main/java/de/srsoftware/web4rail/tiles/Contact.java b/src/main/java/de/srsoftware/web4rail/tiles/Contact.java index 0a62aa5..cf26039 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/Contact.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/Contact.java @@ -10,8 +10,6 @@ import java.util.TreeMap; import org.json.JSONArray; import org.json.JSONObject; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import de.srsoftware.tools.Tag; import de.srsoftware.web4rail.Application; diff --git a/src/main/java/de/srsoftware/web4rail/tiles/Tile.java b/src/main/java/de/srsoftware/web4rail/tiles/Tile.java index ba2dbaf..954dbd6 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/Tile.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/Tile.java @@ -290,8 +290,7 @@ public abstract class Tile extends BaseClass implements Comparable { } @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(occupyingTrain)) { @@ -312,12 +311,13 @@ public abstract class Tile extends BaseClass implements Comparable { if (isSet(fieldset)) preForm.add(fieldset); if (isTrack) { - formInputs.add(t("Length"), - new Input(LENGTH, length).numeric().addTo(new Tag("span")).content(NBSP + lengthUnit)); + 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); } + + if (isSet(lockingTrain())) formInputs.add(t("Locked by {}",lockingTrain()), button(t("free"), Map.of(REALM,REALM_PLAN,ACTION,ACTION_FREE))); List pd = possibleDirections(); if (!pd.isEmpty()) {