diff --git a/pom.xml b/pom.xml index f9f41c8..480c672 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 de.srsoftware web4rail - 0.9.18 + 0.9.19 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 2c1f599..54abd10 100644 --- a/src/main/java/de/srsoftware/web4rail/Plan.java +++ b/src/main/java/de/srsoftware/web4rail/Plan.java @@ -550,8 +550,12 @@ public class Plan implements Constants{ * @return * @throws IOException */ - public Tile place(Tile tile) throws IOException { - stream("place "+tile.tag(null)); + public Tile place(Tile tile) { + try { + stream("place "+tile.tag(null)); + } catch (IOException e) { + e.printStackTrace(); + } return tile; } diff --git a/src/main/java/de/srsoftware/web4rail/Route.java b/src/main/java/de/srsoftware/web4rail/Route.java index a5c7964..4097933 100644 --- a/src/main/java/de/srsoftware/web4rail/Route.java +++ b/src/main/java/de/srsoftware/web4rail/Route.java @@ -26,7 +26,7 @@ import de.srsoftware.web4rail.actions.Action.Context; import de.srsoftware.web4rail.actions.ActionList; import de.srsoftware.web4rail.actions.ActivateRoute; import de.srsoftware.web4rail.actions.FinishRoute; -import de.srsoftware.web4rail.actions.FreeStartBlock; +import de.srsoftware.web4rail.actions.FreePreviousBlocks; import de.srsoftware.web4rail.actions.SetSignalsToStop; import de.srsoftware.web4rail.actions.SetSpeed; import de.srsoftware.web4rail.conditions.Condition; @@ -43,7 +43,6 @@ import de.srsoftware.web4rail.tiles.Signal; import de.srsoftware.web4rail.tiles.Tile; import de.srsoftware.web4rail.tiles.Turnout; import de.srsoftware.web4rail.tiles.Turnout.State; - /** * A route is a vector of tiles that leads from one block to another. * @@ -131,8 +130,12 @@ public class Route implements Constants{ * @throws IOException */ public void activate() throws IOException { - LOG.debug("{} aktiviert.",this); - for (Tile tile : path) tile.train(train); + for (Tile tile : path) { + if (!(tile instanceof Block)) tile.train(train); + } + train.heading(endDirection.inverse()); + endBlock.train(train); + startBlock.trailingTrain(train); } /** @@ -301,8 +304,8 @@ public class Route implements Constants{ if (!contacts.isEmpty()) { Contact lastContact = contacts.lastElement(); add(lastContact.trigger(), new SetSpeed()); - add(lastContact.trigger(), new FreeStartBlock()); add(lastContact.trigger(), new FinishRoute()); + add(lastContact.trigger(), new FreePreviousBlocks()); } } @@ -331,16 +334,14 @@ public class Route implements Constants{ return endBlock; } - public void finish() throws IOException { - train.route = null; - unlock(); - endBlock.train(train.heading(endDirection.inverse())); - train = null; + public void finish() { + reset(); + train.block(endBlock, false); + train.heading(endDirection.inverse()); } - - public void fireSetupActions(Context context) { - setupActions.fire(context); + public boolean fireSetupActions(Context context) { + return setupActions.fire(context); } public boolean free() { @@ -350,11 +351,6 @@ public class Route implements Constants{ return true; } - public Route freeStartBlock() throws IOException { - startBlock.train(null); - return this; - } - private String generateName() { StringBuilder sb = new StringBuilder(); for (int i=0; i lockedTiles = new ArrayList(); try { for (Tile tile : path) lockedTiles.add(tile.lock(this)); - } catch (IOException e) { - for (Tile tile: lockedTiles) try { - tile.unlock(); - } catch (IOException inner) { - LOG.warn("Was not able to unlock {}!",tile,inner); - } + } catch (IllegalStateException e) { + for (Tile tile: lockedTiles) tile.unlock(); return false; } return true; @@ -541,6 +533,18 @@ public class Route implements Constants{ return win; } + public void reset() { + new SetSignalsToStop().fire(new Context(this)); + for (Tile tile : path) { + if (!(tile instanceof Block)) tile.unlock(); + } + if (endBlock.route() == this) endBlock.lock(null); + if (startBlock.route() == this) startBlock.lock(null); + train.heading(startDirection); + train.block(startBlock, false); + if (train.route == this) train.route = null; + } + public static void saveAll(Collection routes, String filename) throws IOException { BufferedWriter file = new BufferedWriter(new FileWriter(filename)); file.write("{\""+ROUTES+"\":[\n"); @@ -560,7 +564,7 @@ public class Route implements Constants{ if (lastTile instanceof Turnout) addTurnout((Turnout) lastTile,state); } - public boolean setSignals(String state) throws IOException { + public boolean setSignals(String state) { for (Signal signal : signals) { if (!signal.state(state == null ? Signal.GO : state)) return false; } diff --git a/src/main/java/de/srsoftware/web4rail/actions/Action.java b/src/main/java/de/srsoftware/web4rail/actions/Action.java index e147016..668fe47 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/Action.java +++ b/src/main/java/de/srsoftware/web4rail/actions/Action.java @@ -45,6 +45,11 @@ public abstract class Action implements Constants { public Context(Train train) { this.train = train; } + + public Context(Route route) { + this.route = route; + train = route.train; + } } public Action() { @@ -54,6 +59,7 @@ public abstract class Action implements Constants { public static Action create(String type) { try { + if (type.equals("FreeStartBlock")) type = FreePreviousBlocks.class.getSimpleName(); return (Action) Class.forName(PREFIX+"."+type).getDeclaredConstructor().newInstance(); } catch (Exception e) { e.printStackTrace(); @@ -86,7 +92,7 @@ public abstract class Action implements Constants { ConditionalAction.class, SetSpeed.class, SetSignalsToStop.class, - FreeStartBlock.class, + FreePreviousBlocks.class, FinishRoute.class, TurnTrain.class, StopAuto.class, diff --git a/src/main/java/de/srsoftware/web4rail/actions/ActionList.java b/src/main/java/de/srsoftware/web4rail/actions/ActionList.java index 3e8a7f3..bbf0dfc 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/ActionList.java +++ b/src/main/java/de/srsoftware/web4rail/actions/ActionList.java @@ -121,15 +121,16 @@ public class ActionList extends Vector implements Constants{ public boolean fire(Context context) { LOG.debug("Firing {}",this); - + boolean success = true; for (Action action : this) { try { - action.fire(context); + success &= action.fire(context); } catch (IOException e) { LOG.warn("Action did not fire properly: {}",action,e); + success = false; } } - return true; + return success; } public int id() { diff --git a/src/main/java/de/srsoftware/web4rail/actions/FinishRoute.java b/src/main/java/de/srsoftware/web4rail/actions/FinishRoute.java index d63cd9d..f1bca48 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/FinishRoute.java +++ b/src/main/java/de/srsoftware/web4rail/actions/FinishRoute.java @@ -2,11 +2,14 @@ package de.srsoftware.web4rail.actions; import java.io.IOException; +import de.srsoftware.web4rail.Route; + public class FinishRoute extends Action { @Override public boolean fire(Context context) throws IOException { - context.route.finish(); + Route route = context.route; + if (route != null) route.finish(); return true; } } diff --git a/src/main/java/de/srsoftware/web4rail/actions/FreeStartBlock.java b/src/main/java/de/srsoftware/web4rail/actions/FreePreviousBlocks.java similarity index 52% rename from src/main/java/de/srsoftware/web4rail/actions/FreeStartBlock.java rename to src/main/java/de/srsoftware/web4rail/actions/FreePreviousBlocks.java index 25748bd..02edb4e 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/FreeStartBlock.java +++ b/src/main/java/de/srsoftware/web4rail/actions/FreePreviousBlocks.java @@ -2,11 +2,11 @@ package de.srsoftware.web4rail.actions; import java.io.IOException; -public class FreeStartBlock extends Action { +public class FreePreviousBlocks extends Action { @Override public boolean fire(Context context) throws IOException { - context.route.freeStartBlock(); - return true; + if (context.train != null) context.train.resetPreviousBlocks(); + return false; } } diff --git a/src/main/java/de/srsoftware/web4rail/actions/SetSignalsToStop.java b/src/main/java/de/srsoftware/web4rail/actions/SetSignalsToStop.java index 819f003..8663552 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/SetSignalsToStop.java +++ b/src/main/java/de/srsoftware/web4rail/actions/SetSignalsToStop.java @@ -1,13 +1,11 @@ package de.srsoftware.web4rail.actions; -import java.io.IOException; - import de.srsoftware.web4rail.tiles.Signal; public class SetSignalsToStop extends Action { @Override - public boolean fire(Context context) throws IOException { + public boolean fire(Context context) { context.route.setSignals(Signal.STOP); 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 1559bab..e03455d 100644 --- a/src/main/java/de/srsoftware/web4rail/moving/Train.java +++ b/src/main/java/de/srsoftware/web4rail/moving/Train.java @@ -36,7 +36,6 @@ import de.srsoftware.web4rail.tags.Input; import de.srsoftware.web4rail.tags.Label; import de.srsoftware.web4rail.tags.Select; import de.srsoftware.web4rail.tiles.Block; -import de.srsoftware.web4rail.tiles.Signal; public class Train implements Comparable,Constants { private static final Logger LOG = LoggerFactory.getLogger(Train.class); @@ -70,6 +69,7 @@ public class Train implements Comparable,Constants { private Block block = null; + private Vector previousBlocks = new Vector(); private class Autopilot extends Thread{ boolean stop = false; @@ -149,6 +149,47 @@ public class Train implements Comparable,Constants { return t("Unknown action: {}",params.get(ACTION)); } + private Route chooseRoute(Context context) { HashSet routes = block.routes(); + Vector availableRoutes = new Vector(); + for (Route rt : routes) { + if (rt == route) continue; // andere Route als zuvor wählen + if (rt.path().firstElement() != block) continue; // keine Route wählen, die nicht vom aktuellen Block des Zuges startet + if (direction != null && rt.startDirection != direction) { // Route ist entgegen der Startrichtung des Zuges + if (!pushPull || !block.turnAllowed) { // Zug ist kein Wendezug oder Block erlaubt kein Wenden + continue; + } + } + if (!rt.free()) { // keine belegten Routen wählen + LOG.debug("{} is not free!",rt); + continue; + } + if (!rt.allowed(context)) continue; + availableRoutes.add(rt); + } + Random rand = new Random(); + if (availableRoutes.isEmpty()) return null; + return availableRoutes.get(rand.nextInt(availableRoutes.size())); + } + + @Override + public int compareTo(Train o) { + return name().compareTo(o.toString()); + } + + public String directedName() { + String result = name(); + if (direction == null) return result; + switch (direction) { + case NORTH: + case WEST: + return '←'+result; + case SOUTH: + case EAST: + return result+'→'; + } + return result; + } + private Object dropCar(HashMap params) { String carId = params.get(CAR_ID); if (carId != null) cars.remove(Car.get(carId)); @@ -188,10 +229,18 @@ public class Train implements Comparable,Constants { return block; } - public void block(Block block) throws IOException { + public Train block(Block block, boolean resetPreviousBlocks) { + if (this.block == block) return this; // nothing to update + if (this.block != null) { + this.block.trailingTrain(this); + previousBlocks.add(this.block); + } this.block = block; + block.train(this); + if (resetPreviousBlocks) resetPreviousBlocks(); + return this; } - + private Tag carList() { Tag locoProp = new Tag("li").content(t("Cars:")); Tag locoList = new Tag("ul").clazz("carlist"); @@ -235,6 +284,7 @@ public class Train implements Comparable,Constants { public Train heading(Direction dir) { direction = dir; + if (block != null) plan.place(block); return this; } @@ -353,17 +403,7 @@ public class Train implements Comparable,Constants { } public String name() { - String result = (name != null ? name : locos.firstElement().name()); - if (direction == null) return result; - switch (direction) { - case NORTH: - case WEST: - return '←'+result; - case SOUTH: - case EAST: - return result+'→'; - } - return result; + return (name != null ? name : locos.firstElement().name()); } private Train name(String newName) { @@ -438,7 +478,20 @@ public class Train implements Comparable,Constants { return t("{} stopping at next block.",this); } else return t("autopilot not active."); } + + public void removeFromBlock(Block block) { + if (block.train() == this) block.train(null); + if (this.block == block) this.block = null; + previousBlocks.remove(block); + } + public void resetPreviousBlocks() { + for (Block block : previousBlocks) { + if (block.train() == this || block.trailingTrain() == this) block.unlock(); + } + previousBlocks.clear(); + } + public static void saveAll(String filename) throws IOException { BufferedWriter file = new BufferedWriter(new FileWriter(filename)); for (Entry entry:trains.entrySet()) { @@ -468,44 +521,25 @@ public class Train implements Comparable,Constants { public String start() throws IOException { if (block == null) return t("{} not in a block",this); - if (route != null) route.unlock().setSignals(Signal.STOP); - HashSet routes = block.routes(); - Vector availableRoutes = new Vector(); + if (route != null) route.reset(); // reset route previously chosen Context context = new Context(this); - for (Route rt : routes) { - if (rt == route) continue; // andere Route als zuvor wählen - if (rt.path().firstElement() != block) continue; // keine Route wählen, die nicht vom aktuellen Block des Zuges startet - if (direction != null && rt.startDirection != direction) { // Route ist entgegen der Startrichtung des Zuges - if (!pushPull || !block.turnAllowed) { // Zug ist kein Wendezug oder Block erlaubt kein Wenden - continue; - } - } - if (!rt.free()) { // keine belegten Routen wählen - LOG.debug("{} is not free!",rt); - continue; - } - if (!rt.allowed(context)) continue; - availableRoutes.add(rt); - } - Random rand = new Random(); - if (availableRoutes.isEmpty()) return t("No free routes from {}",block); - route = availableRoutes.get(rand.nextInt(availableRoutes.size())); + route = chooseRoute(context); + if (route == null) return t("No free routes from {}",block); if (!route.lock()) return t("Was not able to lock {}",route); - String error = null; if (direction != route.startDirection) turn(); + + String error = null; if (!route.setTurnouts()) error = t("Was not able to set all turnouts!"); - route.fireSetupActions(context); + if (error == null && !route.fireSetupActions(context)) error = t("Was not able to fire all setup actions of route!"); if (error == null && !route.setSignals(null)) error = t("Was not able to set all signals!"); if (error == null && !route.train(this)) error = t("Was not able to assign {} to {}!",this,route); - if (error == null) { - setSpeed(128); - return t("Started {}",this); + if (error != null) { + route.reset(); + return error; } - route.unlock(); - this.block.train(this); // re-set train on previous block - this.route = null; - return error; + setSpeed(128); + return t("Started {}",this); } private Object stopNow() { @@ -537,9 +571,7 @@ public class Train implements Comparable,Constants { direction = direction.inverse(); for (Locomotive loco : locos) loco.turn(); } - if (block != null) try { - plan.place(block.train(this)); - } catch (IOException e) {} + if (block != null) plan.place(block.train(this)); return t("{} turned.",this); } @@ -559,9 +591,4 @@ public class Train implements Comparable,Constants { return this; } - - @Override - public int compareTo(Train o) { - return name().compareTo(o.toString()); - } } diff --git a/src/main/java/de/srsoftware/web4rail/tiles/Block.java b/src/main/java/de/srsoftware/web4rail/tiles/Block.java index eaa8551..8734cd3 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/Block.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/Block.java @@ -22,6 +22,7 @@ public abstract class Block extends StretchableTile{ private static final String ALLOW_TURN = "allowTurn"; public boolean turnAllowed = false; + private Train trailingTrain = null; private static final String TRAIN = Train.class.getSimpleName(); @@ -34,9 +35,9 @@ public abstract class Block extends StretchableTile{ @Override public boolean free() { - return train == null && super.free(); + return super.free() && trailingTrain == null; } - + @Override public JSONObject json() { JSONObject json = super.json(); @@ -53,7 +54,7 @@ public abstract class Block extends StretchableTile{ turnAllowed = json.has(ALLOW_TURN) && json.getBoolean(ALLOW_TURN); if (json.has(TRAIN)) { Train tr = Train.get(json.getInt(TRAIN)); - train(tr); + train(tr.block(this, false)); } return this; } @@ -91,9 +92,11 @@ public abstract class Block extends StretchableTile{ @Override public Tag tag(Map replacements) throws IOException { if (replacements == null) replacements = new HashMap(); - replacements.put("%text%",train == null ? name : train.name()); + replacements.put("%text%",name); + if (trailingTrain != null) replacements.put("%text%","("+trailingTrain.name()+")"); + if (train != null) replacements.put("%text%",train.directedName()); Tag tag = super.tag(replacements); - if (train != null) tag.clazz(tag.get("class")+" occupied"); + if (train != null || trailingTrain != null) tag.clazz(tag.get("class")+" occupied"); return tag; } @@ -107,11 +110,20 @@ public abstract class Block extends StretchableTile{ return getClass().getSimpleName()+"("+name+") @ ("+x+","+y+")"; } - public Tile train(Train newTrain) throws IOException { - if (train == newTrain) return this; - if (train != null) train.block(null); // vorherigen Zug rauswerfen - if (newTrain != null) newTrain.block(this); - return super.train(newTrain); + public void trailingTrain(Train train) { + trailingTrain = train; + this.train = null; + plan.place(this); + } + + public Train trailingTrain() { + return trailingTrain; + } + + @Override + public void unlock() { + trailingTrain = null; + super.unlock(); } @Override @@ -119,14 +131,14 @@ public abstract class Block extends StretchableTile{ if (params.containsKey(NAME)) name=params.get(NAME); if (params.containsKey(TRAIN)) { int trainId = Integer.parseInt(params.get(TRAIN)); - Train t = Train.get(trainId); - if (t != null) { - Block oldBlock = t.block(); - if (oldBlock != null) oldBlock.train(null); - train(t); - } + if (trainId == 0) { + train(null); + } else { + Train t = Train.get(trainId); + if (t != null) train = t.block(this,true); + } } turnAllowed = params.containsKey(ALLOW_TURN) && params.get(ALLOW_TURN).equals("on"); return super.update(params); - } + } } diff --git a/src/main/java/de/srsoftware/web4rail/tiles/Relay.java b/src/main/java/de/srsoftware/web4rail/tiles/Relay.java index d71025f..df5a92a 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/Relay.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/Relay.java @@ -181,10 +181,8 @@ public class Relay extends Tile implements Device{ @Override public void onSuccess() { super.onSuccess(); - try { - Relay.this.state = newState; - plan.place(Relay.this); - } catch (IOException e) {} + Relay.this.state = newState; + plan.place(Relay.this); } @Override diff --git a/src/main/java/de/srsoftware/web4rail/tiles/Signal.java b/src/main/java/de/srsoftware/web4rail/tiles/Signal.java index 15b326e..5020f80 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/Signal.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/Signal.java @@ -26,9 +26,13 @@ public abstract class Signal extends Tile{ public abstract boolean isAffectedFrom(Direction dir); - public boolean state(String state) throws IOException { + public boolean state(String state) { this.state = state; - plan.stream("place "+tag(null)); + try { + plan.stream("place "+tag(null)); + } catch (IOException e) { + e.printStackTrace(); + } 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 41cda65..ac5022c 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/Tile.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/Tile.java @@ -95,7 +95,10 @@ public abstract class Tile implements Constants{ } public boolean free() { - return (!disabled) && route == null; + if (disabled) return false; + if (route != null) return false; + if (train != null) return false; + return true; } public int height() { @@ -160,8 +163,9 @@ public abstract class Tile implements Constants{ return this; } - public Tile lock(Route lockingRoute) throws IOException { - if (route != null && route != lockingRoute) throw new IllegalStateException(this.toString()); + public Tile lock(Route lockingRoute) { + if (route == lockingRoute) return this; + if (route != null && lockingRoute != null) throw new IllegalStateException(this.toString()); route = lockingRoute; return plan.place(this); } @@ -353,12 +357,13 @@ public abstract class Tile implements Constants{ return train; } - public Tile train(Train train) throws IOException { + public Tile train(Train train) { + if (this.train == train) return this; // nothing to update this.train = train; return plan.place(this); } - public void unlock() throws IOException { + public void unlock() { route = null; train = null; plan.place(this); diff --git a/src/main/java/de/srsoftware/web4rail/tiles/Turnout.java b/src/main/java/de/srsoftware/web4rail/tiles/Turnout.java index fc7e979..a4b33e3 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/Turnout.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/Turnout.java @@ -149,10 +149,8 @@ public abstract class Turnout extends Tile implements Device{ @Override public void onSuccess() { super.onSuccess(); - try { - Turnout.this.state = newState; - plan.place(Turnout.this); - } catch (IOException e) {} + Turnout.this.state = newState; + plan.place(Turnout.this); } @Override