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