diff --git a/pom.xml b/pom.xml index c457cde..4a85f24 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 de.srsoftware web4rail - 1.2.50 + 1.2.51 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 c8c884e..bd68abb 100644 --- a/src/main/java/de/srsoftware/web4rail/BaseClass.java +++ b/src/main/java/de/srsoftware/web4rail/BaseClass.java @@ -77,6 +77,7 @@ public abstract class BaseClass implements Constants{ } public Context block(Block newBlock) { + LOG.debug("{}.block({})",this,newBlock); block = newBlock; return this; } @@ -130,6 +131,7 @@ public abstract class BaseClass implements Constants{ } public Context direction(Direction newDirection) { + LOG.debug("{}.direction({})",this,newDirection); direction = newDirection; return this; } @@ -140,6 +142,7 @@ public abstract class BaseClass implements Constants{ public Context setMain(BaseClass object) { main = object; + LOG.debug("{}.setMain({})",this,object); if (main instanceof Tile) this.tile = (Tile) main; if (main instanceof Contact) this.contact = (Contact) main; if (main instanceof Block) this.block = (Block) main; @@ -183,6 +186,7 @@ public abstract class BaseClass implements Constants{ } public Context train(Train newTrain) { + LOG.debug("{}.train({})",this,newTrain); train = newTrain; return this; } diff --git a/src/main/java/de/srsoftware/web4rail/PathFinder.java b/src/main/java/de/srsoftware/web4rail/PathFinder.java index b8ec8f7..08f3cb0 100644 --- a/src/main/java/de/srsoftware/web4rail/PathFinder.java +++ b/src/main/java/de/srsoftware/web4rail/PathFinder.java @@ -17,6 +17,7 @@ public class PathFinder extends BaseClass{ private static final Logger LOG = LoggerFactory.getLogger(PathFinder.class); private static TreeMap> availableRoutes(Context context,HashSet visitedRoutes){ + LOG.debug("PathFinder.availableRoutes({})",context); TreeMap> availableRoutes = new TreeMap>(); String inset = ""; @@ -53,7 +54,7 @@ public class PathFinder extends BaseClass{ continue; } if (!routeCandidate.allowed(context)) continue; // Zug darf auf Grund einer nicht erfüllten Bedingung nicht auf die Route - if (!routeCandidate.isFreeFor(train)) continue; // Route ist nicht frei + if (!routeCandidate.isFreeFor(context.route(routeCandidate))) continue; // Route ist nicht frei int priority = 0; if (isSet(direction) && routeCandidate.startDirection != direction) { // Route startet entgegen der aktuellen Fahrtrichtung des Zuges if (!train.pushPull) continue; // Zug kann nicht wenden @@ -99,6 +100,7 @@ public class PathFinder extends BaseClass{ } public static Route chooseRoute(Context context) { + LOG.debug("PathFinder.chooseRoute({})",context); TreeMap> availableRoutes = PathFinder.availableRoutes(context,new HashSet()); if (availableRoutes.isEmpty()) return null; Entry> entry = availableRoutes.lastEntry(); diff --git a/src/main/java/de/srsoftware/web4rail/Plan.java b/src/main/java/de/srsoftware/web4rail/Plan.java index f23ac59..7d024c9 100644 --- a/src/main/java/de/srsoftware/web4rail/Plan.java +++ b/src/main/java/de/srsoftware/web4rail/Plan.java @@ -33,7 +33,6 @@ import de.srsoftware.web4rail.tags.Input; import de.srsoftware.web4rail.tags.Label; import de.srsoftware.web4rail.tags.Table; import de.srsoftware.web4rail.tiles.Block; -import de.srsoftware.web4rail.tiles.BlockContact; import de.srsoftware.web4rail.tiles.BlockH; import de.srsoftware.web4rail.tiles.BlockV; import de.srsoftware.web4rail.tiles.Bridge; diff --git a/src/main/java/de/srsoftware/web4rail/Route.java b/src/main/java/de/srsoftware/web4rail/Route.java index b5dae03..b8ceaeb 100644 --- a/src/main/java/de/srsoftware/web4rail/Route.java +++ b/src/main/java/de/srsoftware/web4rail/Route.java @@ -423,10 +423,10 @@ public class Route extends BaseClass { public void finish() { context.clear(); // prevent delayed actions from firing after route has finished setSignals(Signal.STOP); - for (Tile tile : path) tile.setRoute(null); + for (Tile tile : path) tile.unset(this); Tile lastTile = path.lastElement(); if (lastTile instanceof Contact) { - lastTile.set(null); + lastTile.setTrain(null); if (isSet(train)) train.removeChild(lastTile); } if (isSet(train)) { @@ -445,6 +445,7 @@ public class Route extends BaseClass { } public boolean fireSetupActions() { + LOG.debug("{}.firesSetupActions({})",this); ActionList setupActions = triggeredActions.get(ROUTE_SETUP); if (isSet(setupActions) && !setupActions.fire(context)) return false; state = State.PREPARED; @@ -475,9 +476,13 @@ public class Route extends BaseClass { return disabled; } - public boolean isFreeFor(Train newTrain) { - for (int i=1; i alreadyLocked = new Vector(); HashSet ignoredPath = new HashSet(); if (isSet(ignoredRoute)) ignoredPath.addAll(ignoredRoute.path); boolean success = true; for (Tile tile : path) { if (ignoredPath.contains(tile)) continue; try { - alreadyLocked.add(tile.setRoute(this)); + tile.setRoute(this); } catch (IllegalStateException e) { + LOG.debug("{}.lockIgnoring(...) failed at {}, rolling back",this,tile); success = false; break; } } - if (success) { - state = State.LOCKED; - } else for (Tile tile :alreadyLocked) tile.setRoute(null); + if (success) state = State.LOCKED; return success; } @@ -795,11 +799,16 @@ public class Route extends BaseClass { } public boolean reset() { + LOG.debug("{}.reset()",this); setSignals(Signal.STOP); - for (Tile tile : path) tile.setRoute(null); + for (Tile tile : path) { + try { + tile.unset(this); + } catch (IllegalArgumentException e) {} + } Tile lastTile = path.lastElement(); if (lastTile instanceof Contact) { - lastTile.set(null); + lastTile.setTrain(null); if (isSet(train)) train.removeChild(lastTile); } if (isSet(train)) { @@ -807,7 +816,8 @@ public class Route extends BaseClass { train.heading(startDirection); if (train.route == this) train.route = null; train = null; - } + } + LOG.debug("chlearing triggeredContacts of {}",this); triggeredContacts.clear(); state = State.FREE; return true; @@ -840,6 +850,7 @@ public class Route extends BaseClass { } public boolean setSignals(String state) { + LOG.debug("{}.setSignals({})",this,state); for (Signal signal : signals) { if (!signal.state(isNull(state) ? Signal.GO : state)) return false; } @@ -861,6 +872,7 @@ public class Route extends BaseClass { } public boolean start(Train newTrain) { + LOG.debug("{}.start({})",this,newTrain); 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 diff --git a/src/main/java/de/srsoftware/web4rail/actions/ActionList.java b/src/main/java/de/srsoftware/web4rail/actions/ActionList.java index ae169ec..5502a79 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/ActionList.java +++ b/src/main/java/de/srsoftware/web4rail/actions/ActionList.java @@ -67,6 +67,7 @@ public class ActionList extends Action implements Iterable{ } public boolean fire(Context context) { + LOG.debug("{}.fire({})",this,context); if (context.invalidated()) { LOG.debug("Context has been invalidated, aborting {}",this); return false; diff --git a/src/main/java/de/srsoftware/web4rail/actions/PreserveRoute.java b/src/main/java/de/srsoftware/web4rail/actions/PreserveRoute.java index 0161d50..ecf8135 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/PreserveRoute.java +++ b/src/main/java/de/srsoftware/web4rail/actions/PreserveRoute.java @@ -25,7 +25,10 @@ public class PreserveRoute extends Action { if (train.destination() == route.endBlock()) return true; Range waitTime = route.endBlock().getWaitTime(train,route.endDirection); - if (waitTime.max > 0) return true; // train is expected to wait in next block. + if (waitTime.max > 0) { + LOG.debug("Not preserving route, as train needs to stop in following block!"); + return true; // train is expected to wait in next block. + } train.reserveNext(); return true; diff --git a/src/main/java/de/srsoftware/web4rail/moving/Car.java b/src/main/java/de/srsoftware/web4rail/moving/Car.java index fb97106..8034022 100644 --- a/src/main/java/de/srsoftware/web4rail/moving/Car.java +++ b/src/main/java/de/srsoftware/web4rail/moving/Car.java @@ -309,6 +309,7 @@ public class Car extends BaseClass implements Comparable{ } public Object turn() { + LOG.debug("{}.turn()",this); orientation = !orientation; return t("Reversed {}.",this); } diff --git a/src/main/java/de/srsoftware/web4rail/moving/Train.java b/src/main/java/de/srsoftware/web4rail/moving/Train.java index 8504dc5..6f1ab76 100644 --- a/src/main/java/de/srsoftware/web4rail/moving/Train.java +++ b/src/main/java/de/srsoftware/web4rail/moving/Train.java @@ -100,8 +100,11 @@ public class Train extends BaseClass implements Comparable { if (stop) return; if (isNull(route)) { // may have been set by start action in between Object o = Train.this.start(); - if (o instanceof String) plan.stream((String)o); - if (isSet(destination)) Thread.sleep(1000); // limit load on PathFinder + LOG.debug("Train.start called, route now is {}",route); + if (isSet(route)) { + if (o instanceof String) plan.stream((String)o); + //if (isSet(destination)) Thread.sleep(1000); // limit load on PathFinder + } else Thread.sleep(1000); // limit load on PathFinder } } else Thread.sleep(250); } @@ -306,15 +309,15 @@ public class Train extends BaseClass implements Comparable { public String directedName() { String result = name(); - if (isSet(autopilot)) result="℗"+result; + String mark = isSet(autopilot) ? "ⓐ" : ""; if (isNull(direction)) return result; switch (direction) { case NORTH: case WEST: - return '←'+result; + return '←'+mark+result; case SOUTH: case EAST: - return result+'→'; + return result+mark+'→'; } return result; } @@ -335,7 +338,7 @@ public class Train extends BaseClass implements Comparable { } public void dropTrace() { - while (!trace.isEmpty()) trace.removeFirst().set(null); + while (!trace.isEmpty()) trace.removeFirst().setTrain(null); } private Tag faster(int steps) { @@ -348,6 +351,7 @@ public class Train extends BaseClass implements Comparable { } public Train heading(Direction dir) { + LOG.debug("{}.heading({})",this,dir); direction = dir; if (isSet(currentBlock)) plan.place(currentBlock); return this; @@ -411,8 +415,8 @@ 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).set(this)); }); - if (json.has(BLOCK)) currentBlock = (Block) plan.get(new Id(json.getString(BLOCK)), false).set(this); // do not move this up! during set, other fields will be referenced! + 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(LOCOS)) { // for downward compatibility for (Object id : json.getJSONArray(LOCOS)) add(BaseClass.get(new Id(""+id))); } @@ -568,6 +572,7 @@ public class Train extends BaseClass implements Comparable { @Override public void removeChild(BaseClass child) { + LOG.debug("{}.removeChild({})",this,child); if (child == route) route = null; if (child == nextRoute) nextRoute = null; if (child == currentBlock) currentBlock = null; @@ -578,9 +583,13 @@ public class Train extends BaseClass implements Comparable { } public void reserveNext() { + LOG.debug("{}.reserveNext()",this); Context context = new Context(this).route(route).block(route.endBlock()).direction(route.endDirection); Route nextRoute = PathFinder.chooseRoute(context); - if (isNull(nextRoute)) return; + if (isNull(nextRoute)) { + LOG.debug("{}.reserveNext() found no available route!",this); + return; + } nextRoute.set(context); boolean error = !nextRoute.lockIgnoring(route); error = error || !nextRoute.fireSetupActions(); @@ -644,8 +653,9 @@ public class Train extends BaseClass implements Comparable { } public void set(Block newBlock) { + LOG.debug("{}.set({})",this,newBlock); currentBlock = newBlock; - if (isSet(currentBlock)) currentBlock.set(this); + if (isSet(currentBlock)) currentBlock.setTrain(this); } private String setDestination(HashMap params) { @@ -692,6 +702,7 @@ public class Train extends BaseClass implements Comparable { } public void setSpeed(int newSpeed) { + LOG.debug("{}.setSpeed({})",this,newSpeed); speed = Math.min(newSpeed,maxSpeed()); if (speed < 0) speed = 0; cars.stream().filter(c -> c instanceof Locomotive).forEach(car -> ((Locomotive)car).setSpeed(speed)); @@ -714,9 +725,9 @@ public class Train extends BaseClass implements Comparable { Tile tile = trace.get(i); if (remainingLength>0) { remainingLength-=tile.length(); - tile.set(this); + tile.setTrain(this); } else { - tile.set(null); + tile.setTrain(null); trace.remove(i); i--; // do not move to next index: remove shifted the next index towards us } @@ -729,18 +740,20 @@ public class Train extends BaseClass implements Comparable { } public Object 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 { - Context context = new Context(this).block(currentBlock).direction(direction).train(this); + Context context = new Context(this).block(currentBlock).direction(direction); route = PathFinder.chooseRoute(context); if (isNull(route)) return t("No free routes from {}",currentBlock); if (!route.lock()) error = t("Was not able to lock {}",route); @@ -751,6 +764,7 @@ public class Train extends BaseClass implements Comparable { 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; @@ -766,8 +780,12 @@ public class Train extends BaseClass implements Comparable { } private void startSimulation() { + LOG.debug("{}.startSimulation({})",this); for (Contact contact : route.contacts()) { - if (contact.addr() != 0) return; // simulate train only when all contacts are non-physical + if (contact.addr() != 0) { + LOG.debug("{}.startSimulation aborted!",this); + return; // simulate train only when all contacts are non-physical + } } try { Thread.sleep(1000); @@ -844,7 +862,7 @@ public class Train extends BaseClass implements Comparable { * @return */ public Tag turn() { - LOG.debug("train.turn()"); + LOG.debug("{}.turn()",this); for (Car car : cars) car.turn(); Collections.reverse(cars); return reverse(); diff --git a/src/main/java/de/srsoftware/web4rail/tiles/Bridge.java b/src/main/java/de/srsoftware/web4rail/tiles/Bridge.java index 0abc950..85d8fd8 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/Bridge.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/Bridge.java @@ -66,12 +66,6 @@ public abstract class Bridge extends Tile { return super.load(json); } - public Tile set(Train train) { - super.set(train); - if (isSet(counterpart) && counterpart.train != train) counterpart.set(train); - return this; - } - @Override public Tile setRoute(Route route) { super.setRoute(route); @@ -79,6 +73,12 @@ public abstract class Bridge extends Tile { return this; } + public Tile setTrain(Train train) { + super.setTrain(train); + if (isSet(counterpart) && counterpart.train != train) counterpart.setTrain(train); + return this; + } + @Override protected Window properties(List
preForm, FormInput formInputs, List
postForm) { Fieldset fieldset = new Fieldset(t("Counterpart")); @@ -113,4 +113,11 @@ 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/Signal.java b/src/main/java/de/srsoftware/web4rail/tiles/Signal.java index 12c780d..8cbb795 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/Signal.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/Signal.java @@ -32,6 +32,7 @@ public abstract class Signal extends Tile { public abstract boolean isAffectedFrom(Direction dir); public boolean state(String state) { + LOG.debug("{}.state({})",this,state); this.state = state; plan.place(this); 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 8566011..7f432c9 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/Tile.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/Tile.java @@ -112,10 +112,46 @@ public abstract class Tile extends BaseClass implements Comparable{ plan.place(tile); } - public boolean isFreeFor(Train newTrain) { - if (disabled) return false; - if (isSet(route) && isSet(route.train()) && route.train() != newTrain) return false; - if (isSet(train) && train != newTrain) return false; + public boolean isFreeFor(Context context) { + LOG.debug("{}.isFreeFor({})",this,context); + if (disabled) { + LOG.debug("{} is disabled!",this); + return false; + } + if (isNull(context)) { + if (isSet(train)) { + LOG.debug("{} is occupied by {}",this,train); + return false; + } + if (isSet(route)) { + LOG.debug("{} is occupied by {}",this,route); + return false; + + } + } + if (isSet(train)) { + boolean free = train == context.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! + if (free) { + LOG.debug("already reserved by {} → true",train); + } else { + 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()) { + LOG.debug("reserved by other route: {}",route); + if (isSet(route.train())) { + if (route.train() == context.train()) { + LOG.debug("that route is used by {}, which is also requesting this tile → true",route.train()); + return true; + } + } + LOG.debug("{}.route.train = {} → false",this,route.train()); + return false; + } + LOG.debug("free"); return true; } @@ -179,6 +215,19 @@ public abstract class Tile extends BaseClass implements Comparable{ return this; } + public boolean move(int dx, int 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); + if (isSet(tileAtDestination) && !tileAtDestination.move(dx, dy)) return false; + plan.drop(this); + position(x+dx, y+dy); + plan.place(this); + return true; + } + protected void noTrack() { isTrack = false; } @@ -310,15 +359,20 @@ public abstract class Tile extends BaseClass implements Comparable{ file.close(); } - public Tile set(Train newTrain) { + public Tile setTrain(Train newTrain) { + LOG.debug("{}.set({})",this,newTrain); if (newTrain == train) return this; // nothing to update this.train = newTrain; return plan.place(this); } public Tile setRoute(Route lockingRoute) { - if (route == lockingRoute) return this; // nothing changed - if (isSet(route) && isSet(lockingRoute)) throw new IllegalStateException(this.toString()); // tile already locked by other route + 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); } @@ -426,6 +480,16 @@ public abstract class Tile extends BaseClass implements Comparable{ 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); @@ -449,16 +513,5 @@ public abstract class Tile extends BaseClass implements Comparable{ return 1; } - public boolean move(int dx, int 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); - if (isSet(tileAtDestination) && !tileAtDestination.move(dx, dy)) return false; - plan.drop(this); - position(x+dx, y+dy); - plan.place(this); - return true; - } + }