working on route actions:

start block of route should only be freed, if corresponding action is fired.
  however, train should not be available from previous start block after route has finished.
This commit is contained in:
Stephan Richter
2020-11-03 23:28:23 +01:00
parent a2cb3d813e
commit 596317049c
14 changed files with 183 additions and 123 deletions

View File

@@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>de.srsoftware</groupId> <groupId>de.srsoftware</groupId>
<artifactId>web4rail</artifactId> <artifactId>web4rail</artifactId>
<version>0.9.18</version> <version>0.9.19</version>
<name>Web4Rail</name> <name>Web4Rail</name>
<packaging>jar</packaging> <packaging>jar</packaging>
<description>Java Model Railway Control</description> <description>Java Model Railway Control</description>

View File

@@ -550,8 +550,12 @@ public class Plan implements Constants{
* @return * @return
* @throws IOException * @throws IOException
*/ */
public Tile place(Tile tile) throws IOException { public Tile place(Tile tile) {
stream("place "+tile.tag(null)); try {
stream("place "+tile.tag(null));
} catch (IOException e) {
e.printStackTrace();
}
return tile; return tile;
} }

View File

@@ -26,7 +26,7 @@ import de.srsoftware.web4rail.actions.Action.Context;
import de.srsoftware.web4rail.actions.ActionList; import de.srsoftware.web4rail.actions.ActionList;
import de.srsoftware.web4rail.actions.ActivateRoute; import de.srsoftware.web4rail.actions.ActivateRoute;
import de.srsoftware.web4rail.actions.FinishRoute; 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.SetSignalsToStop;
import de.srsoftware.web4rail.actions.SetSpeed; import de.srsoftware.web4rail.actions.SetSpeed;
import de.srsoftware.web4rail.conditions.Condition; 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.Tile;
import de.srsoftware.web4rail.tiles.Turnout; import de.srsoftware.web4rail.tiles.Turnout;
import de.srsoftware.web4rail.tiles.Turnout.State; import de.srsoftware.web4rail.tiles.Turnout.State;
/** /**
* A route is a vector of tiles that leads from one block to another. * 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 * @throws IOException
*/ */
public void activate() throws IOException { public void activate() throws IOException {
LOG.debug("{} aktiviert.",this); for (Tile tile : path) {
for (Tile tile : path) tile.train(train); 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()) { if (!contacts.isEmpty()) {
Contact lastContact = contacts.lastElement(); Contact lastContact = contacts.lastElement();
add(lastContact.trigger(), new SetSpeed()); add(lastContact.trigger(), new SetSpeed());
add(lastContact.trigger(), new FreeStartBlock());
add(lastContact.trigger(), new FinishRoute()); add(lastContact.trigger(), new FinishRoute());
add(lastContact.trigger(), new FreePreviousBlocks());
} }
} }
@@ -331,16 +334,14 @@ public class Route implements Constants{
return endBlock; return endBlock;
} }
public void finish() throws IOException { public void finish() {
train.route = null; reset();
unlock(); train.block(endBlock, false);
endBlock.train(train.heading(endDirection.inverse())); train.heading(endDirection.inverse());
train = null;
} }
public boolean fireSetupActions(Context context) {
public void fireSetupActions(Context context) { return setupActions.fire(context);
setupActions.fire(context);
} }
public boolean free() { public boolean free() {
@@ -350,11 +351,6 @@ public class Route implements Constants{
return true; return true;
} }
public Route freeStartBlock() throws IOException {
startBlock.train(null);
return this;
}
private String generateName() { private String generateName() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
for (int i=0; i<path.size();i++) { for (int i=0; i<path.size();i++) {
@@ -494,12 +490,8 @@ public class Route implements Constants{
ArrayList<Tile> lockedTiles = new ArrayList<Tile>(); ArrayList<Tile> lockedTiles = new ArrayList<Tile>();
try { try {
for (Tile tile : path) lockedTiles.add(tile.lock(this)); for (Tile tile : path) lockedTiles.add(tile.lock(this));
} catch (IOException e) { } catch (IllegalStateException e) {
for (Tile tile: lockedTiles) try { for (Tile tile: lockedTiles) tile.unlock();
tile.unlock();
} catch (IOException inner) {
LOG.warn("Was not able to unlock {}!",tile,inner);
}
return false; return false;
} }
return true; return true;
@@ -541,6 +533,18 @@ public class Route implements Constants{
return win; 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<Route> routes, String filename) throws IOException { public static void saveAll(Collection<Route> routes, String filename) throws IOException {
BufferedWriter file = new BufferedWriter(new FileWriter(filename)); BufferedWriter file = new BufferedWriter(new FileWriter(filename));
file.write("{\""+ROUTES+"\":[\n"); file.write("{\""+ROUTES+"\":[\n");
@@ -560,7 +564,7 @@ public class Route implements Constants{
if (lastTile instanceof Turnout) addTurnout((Turnout) lastTile,state); if (lastTile instanceof Turnout) addTurnout((Turnout) lastTile,state);
} }
public boolean setSignals(String state) throws IOException { public boolean setSignals(String state) {
for (Signal signal : signals) { for (Signal signal : signals) {
if (!signal.state(state == null ? Signal.GO : state)) return false; if (!signal.state(state == null ? Signal.GO : state)) return false;
} }

View File

@@ -45,6 +45,11 @@ public abstract class Action implements Constants {
public Context(Train train) { public Context(Train train) {
this.train = train; this.train = train;
} }
public Context(Route route) {
this.route = route;
train = route.train;
}
} }
public Action() { public Action() {
@@ -54,6 +59,7 @@ public abstract class Action implements Constants {
public static Action create(String type) { public static Action create(String type) {
try { try {
if (type.equals("FreeStartBlock")) type = FreePreviousBlocks.class.getSimpleName();
return (Action) Class.forName(PREFIX+"."+type).getDeclaredConstructor().newInstance(); return (Action) Class.forName(PREFIX+"."+type).getDeclaredConstructor().newInstance();
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
@@ -86,7 +92,7 @@ public abstract class Action implements Constants {
ConditionalAction.class, ConditionalAction.class,
SetSpeed.class, SetSpeed.class,
SetSignalsToStop.class, SetSignalsToStop.class,
FreeStartBlock.class, FreePreviousBlocks.class,
FinishRoute.class, FinishRoute.class,
TurnTrain.class, TurnTrain.class,
StopAuto.class, StopAuto.class,

View File

@@ -121,15 +121,16 @@ public class ActionList extends Vector<Action> implements Constants{
public boolean fire(Context context) { public boolean fire(Context context) {
LOG.debug("Firing {}",this); LOG.debug("Firing {}",this);
boolean success = true;
for (Action action : this) { for (Action action : this) {
try { try {
action.fire(context); success &= action.fire(context);
} catch (IOException e) { } catch (IOException e) {
LOG.warn("Action did not fire properly: {}",action,e); LOG.warn("Action did not fire properly: {}",action,e);
success = false;
} }
} }
return true; return success;
} }
public int id() { public int id() {

View File

@@ -2,11 +2,14 @@ package de.srsoftware.web4rail.actions;
import java.io.IOException; import java.io.IOException;
import de.srsoftware.web4rail.Route;
public class FinishRoute extends Action { public class FinishRoute extends Action {
@Override @Override
public boolean fire(Context context) throws IOException { public boolean fire(Context context) throws IOException {
context.route.finish(); Route route = context.route;
if (route != null) route.finish();
return true; return true;
} }
} }

View File

@@ -2,11 +2,11 @@ package de.srsoftware.web4rail.actions;
import java.io.IOException; import java.io.IOException;
public class FreeStartBlock extends Action { public class FreePreviousBlocks extends Action {
@Override @Override
public boolean fire(Context context) throws IOException { public boolean fire(Context context) throws IOException {
context.route.freeStartBlock(); if (context.train != null) context.train.resetPreviousBlocks();
return true; return false;
} }
} }

View File

@@ -1,13 +1,11 @@
package de.srsoftware.web4rail.actions; package de.srsoftware.web4rail.actions;
import java.io.IOException;
import de.srsoftware.web4rail.tiles.Signal; import de.srsoftware.web4rail.tiles.Signal;
public class SetSignalsToStop extends Action { public class SetSignalsToStop extends Action {
@Override @Override
public boolean fire(Context context) throws IOException { public boolean fire(Context context) {
context.route.setSignals(Signal.STOP); context.route.setSignals(Signal.STOP);
return true; return true;
} }

View File

@@ -36,7 +36,6 @@ import de.srsoftware.web4rail.tags.Input;
import de.srsoftware.web4rail.tags.Label; import de.srsoftware.web4rail.tags.Label;
import de.srsoftware.web4rail.tags.Select; import de.srsoftware.web4rail.tags.Select;
import de.srsoftware.web4rail.tiles.Block; import de.srsoftware.web4rail.tiles.Block;
import de.srsoftware.web4rail.tiles.Signal;
public class Train implements Comparable<Train>,Constants { public class Train implements Comparable<Train>,Constants {
private static final Logger LOG = LoggerFactory.getLogger(Train.class); private static final Logger LOG = LoggerFactory.getLogger(Train.class);
@@ -70,6 +69,7 @@ public class Train implements Comparable<Train>,Constants {
private Block block = null; private Block block = null;
private Vector<Block> previousBlocks = new Vector<Block>();
private class Autopilot extends Thread{ private class Autopilot extends Thread{
boolean stop = false; boolean stop = false;
@@ -149,6 +149,47 @@ public class Train implements Comparable<Train>,Constants {
return t("Unknown action: {}",params.get(ACTION)); return t("Unknown action: {}",params.get(ACTION));
} }
private Route chooseRoute(Context context) { HashSet<Route> routes = block.routes();
Vector<Route> availableRoutes = new Vector<Route>();
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<String, String> params) { private Object dropCar(HashMap<String, String> params) {
String carId = params.get(CAR_ID); String carId = params.get(CAR_ID);
if (carId != null) cars.remove(Car.get(carId)); if (carId != null) cars.remove(Car.get(carId));
@@ -188,10 +229,18 @@ public class Train implements Comparable<Train>,Constants {
return block; 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; this.block = block;
block.train(this);
if (resetPreviousBlocks) resetPreviousBlocks();
return this;
} }
private Tag carList() { private Tag carList() {
Tag locoProp = new Tag("li").content(t("Cars:")); Tag locoProp = new Tag("li").content(t("Cars:"));
Tag locoList = new Tag("ul").clazz("carlist"); Tag locoList = new Tag("ul").clazz("carlist");
@@ -235,6 +284,7 @@ public class Train implements Comparable<Train>,Constants {
public Train heading(Direction dir) { public Train heading(Direction dir) {
direction = dir; direction = dir;
if (block != null) plan.place(block);
return this; return this;
} }
@@ -353,17 +403,7 @@ public class Train implements Comparable<Train>,Constants {
} }
public String name() { public String name() {
String result = (name != null ? name : locos.firstElement().name()); return (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;
} }
private Train name(String newName) { private Train name(String newName) {
@@ -438,7 +478,20 @@ public class Train implements Comparable<Train>,Constants {
return t("{} stopping at next block.",this); return t("{} stopping at next block.",this);
} else return t("autopilot not active."); } 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 { public static void saveAll(String filename) throws IOException {
BufferedWriter file = new BufferedWriter(new FileWriter(filename)); BufferedWriter file = new BufferedWriter(new FileWriter(filename));
for (Entry<Integer, Train> entry:trains.entrySet()) { for (Entry<Integer, Train> entry:trains.entrySet()) {
@@ -468,44 +521,25 @@ public class Train implements Comparable<Train>,Constants {
public String start() throws IOException { public String start() throws IOException {
if (block == null) return t("{} not in a block",this); if (block == null) return t("{} not in a block",this);
if (route != null) route.unlock().setSignals(Signal.STOP); if (route != null) route.reset(); // reset route previously chosen
HashSet<Route> routes = block.routes();
Vector<Route> availableRoutes = new Vector<Route>();
Context context = new Context(this); Context context = new Context(this);
for (Route rt : routes) { route = chooseRoute(context);
if (rt == route) continue; // andere Route als zuvor wählen if (route == null) return t("No free routes from {}",block);
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()));
if (!route.lock()) return t("Was not able to lock {}",route); if (!route.lock()) return t("Was not able to lock {}",route);
String error = null;
if (direction != route.startDirection) turn(); if (direction != route.startDirection) turn();
String error = null;
if (!route.setTurnouts()) error = t("Was not able to set all turnouts!"); 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.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 && !route.train(this)) error = t("Was not able to assign {} to {}!",this,route);
if (error == null) { if (error != null) {
setSpeed(128); route.reset();
return t("Started {}",this); return error;
} }
route.unlock(); setSpeed(128);
this.block.train(this); // re-set train on previous block return t("Started {}",this);
this.route = null;
return error;
} }
private Object stopNow() { private Object stopNow() {
@@ -537,9 +571,7 @@ public class Train implements Comparable<Train>,Constants {
direction = direction.inverse(); direction = direction.inverse();
for (Locomotive loco : locos) loco.turn(); for (Locomotive loco : locos) loco.turn();
} }
if (block != null) try { if (block != null) plan.place(block.train(this));
plan.place(block.train(this));
} catch (IOException e) {}
return t("{} turned.",this); return t("{} turned.",this);
} }
@@ -559,9 +591,4 @@ public class Train implements Comparable<Train>,Constants {
return this; return this;
} }
@Override
public int compareTo(Train o) {
return name().compareTo(o.toString());
}
} }

View File

@@ -22,6 +22,7 @@ public abstract class Block extends StretchableTile{
private static final String ALLOW_TURN = "allowTurn"; private static final String ALLOW_TURN = "allowTurn";
public boolean turnAllowed = false; public boolean turnAllowed = false;
private Train trailingTrain = null;
private static final String TRAIN = Train.class.getSimpleName(); private static final String TRAIN = Train.class.getSimpleName();
@@ -34,9 +35,9 @@ public abstract class Block extends StretchableTile{
@Override @Override
public boolean free() { public boolean free() {
return train == null && super.free(); return super.free() && trailingTrain == null;
} }
@Override @Override
public JSONObject json() { public JSONObject json() {
JSONObject json = super.json(); JSONObject json = super.json();
@@ -53,7 +54,7 @@ public abstract class Block extends StretchableTile{
turnAllowed = json.has(ALLOW_TURN) && json.getBoolean(ALLOW_TURN); turnAllowed = json.has(ALLOW_TURN) && json.getBoolean(ALLOW_TURN);
if (json.has(TRAIN)) { if (json.has(TRAIN)) {
Train tr = Train.get(json.getInt(TRAIN)); Train tr = Train.get(json.getInt(TRAIN));
train(tr); train(tr.block(this, false));
} }
return this; return this;
} }
@@ -91,9 +92,11 @@ public abstract class Block extends StretchableTile{
@Override @Override
public Tag tag(Map<String, Object> replacements) throws IOException { public Tag tag(Map<String, Object> replacements) throws IOException {
if (replacements == null) replacements = new HashMap<String, Object>(); if (replacements == null) replacements = new HashMap<String, Object>();
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); 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; return tag;
} }
@@ -107,11 +110,20 @@ public abstract class Block extends StretchableTile{
return getClass().getSimpleName()+"("+name+") @ ("+x+","+y+")"; return getClass().getSimpleName()+"("+name+") @ ("+x+","+y+")";
} }
public Tile train(Train newTrain) throws IOException { public void trailingTrain(Train train) {
if (train == newTrain) return this; trailingTrain = train;
if (train != null) train.block(null); // vorherigen Zug rauswerfen this.train = null;
if (newTrain != null) newTrain.block(this); plan.place(this);
return super.train(newTrain); }
public Train trailingTrain() {
return trailingTrain;
}
@Override
public void unlock() {
trailingTrain = null;
super.unlock();
} }
@Override @Override
@@ -119,14 +131,14 @@ public abstract class Block extends StretchableTile{
if (params.containsKey(NAME)) name=params.get(NAME); if (params.containsKey(NAME)) name=params.get(NAME);
if (params.containsKey(TRAIN)) { if (params.containsKey(TRAIN)) {
int trainId = Integer.parseInt(params.get(TRAIN)); int trainId = Integer.parseInt(params.get(TRAIN));
Train t = Train.get(trainId); if (trainId == 0) {
if (t != null) { train(null);
Block oldBlock = t.block(); } else {
if (oldBlock != null) oldBlock.train(null); Train t = Train.get(trainId);
train(t); if (t != null) train = t.block(this,true);
} }
} }
turnAllowed = params.containsKey(ALLOW_TURN) && params.get(ALLOW_TURN).equals("on"); turnAllowed = params.containsKey(ALLOW_TURN) && params.get(ALLOW_TURN).equals("on");
return super.update(params); return super.update(params);
} }
} }

View File

@@ -181,10 +181,8 @@ public class Relay extends Tile implements Device{
@Override @Override
public void onSuccess() { public void onSuccess() {
super.onSuccess(); super.onSuccess();
try { Relay.this.state = newState;
Relay.this.state = newState; plan.place(Relay.this);
plan.place(Relay.this);
} catch (IOException e) {}
} }
@Override @Override

View File

@@ -26,9 +26,13 @@ public abstract class Signal extends Tile{
public abstract boolean isAffectedFrom(Direction dir); public abstract boolean isAffectedFrom(Direction dir);
public boolean state(String state) throws IOException { public boolean state(String state) {
this.state = state; this.state = state;
plan.stream("place "+tag(null)); try {
plan.stream("place "+tag(null));
} catch (IOException e) {
e.printStackTrace();
}
return true; return true;
} }

View File

@@ -95,7 +95,10 @@ public abstract class Tile implements Constants{
} }
public boolean free() { 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() { public int height() {
@@ -160,8 +163,9 @@ public abstract class Tile implements Constants{
return this; return this;
} }
public Tile lock(Route lockingRoute) throws IOException { public Tile lock(Route lockingRoute) {
if (route != null && route != lockingRoute) throw new IllegalStateException(this.toString()); if (route == lockingRoute) return this;
if (route != null && lockingRoute != null) throw new IllegalStateException(this.toString());
route = lockingRoute; route = lockingRoute;
return plan.place(this); return plan.place(this);
} }
@@ -353,12 +357,13 @@ public abstract class Tile implements Constants{
return train; 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; this.train = train;
return plan.place(this); return plan.place(this);
} }
public void unlock() throws IOException { public void unlock() {
route = null; route = null;
train = null; train = null;
plan.place(this); plan.place(this);

View File

@@ -149,10 +149,8 @@ public abstract class Turnout extends Tile implements Device{
@Override @Override
public void onSuccess() { public void onSuccess() {
super.onSuccess(); super.onSuccess();
try { Turnout.this.state = newState;
Turnout.this.state = newState; plan.place(Turnout.this);
plan.place(Turnout.this);
} catch (IOException e) {}
} }
@Override @Override