diff --git a/pom.xml b/pom.xml
index b337397..019ecca 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
4.0.0
de.srsoftware
web4rail
- 1.3.52
+ 1.3.53
Web4Rail
jar
Java Model Railway Control
diff --git a/resources/css/style.css b/resources/css/style.css
index 62fc459..60088db 100644
--- a/resources/css/style.css
+++ b/resources/css/style.css
@@ -77,14 +77,20 @@ svg.Relay rect{
fill: white;
}
+svg.allocated polygon,
+svg.allocated rect:not(.sig_a):not(.sig_b){
+ fill: yellow;
+}
+
svg.locked polygon,
svg.locked rect:not(.sig_a):not(.sig_b){
- fill:lime;
+ fill: lime;
}
+.occupied .block,
svg.occupied polygon,
svg.occupied rect:not(.sig_a):not(.sig_b){
- fill:yellow;
+ fill: orange;
}
svg text{
@@ -232,10 +238,6 @@ svg.straight .right{
fill: #ddd !important;
}
-.occupied .block{
- fill: yellow;
-}
-
.active circle{
fill: #f57900;
}
diff --git a/resources/logback.xml b/resources/logback.xml
index 3013d46..bf19776 100644
--- a/resources/logback.xml
+++ b/resources/logback.xml
@@ -29,7 +29,7 @@
-
+
diff --git a/src/main/java/de/srsoftware/web4rail/Route.java b/src/main/java/de/srsoftware/web4rail/Route.java
index 293a355..18a62f1 100644
--- a/src/main/java/de/srsoftware/web4rail/Route.java
+++ b/src/main/java/de/srsoftware/web4rail/Route.java
@@ -10,6 +10,7 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.Stack;
import java.util.Vector;
import org.json.JSONArray;
@@ -44,6 +45,7 @@ import de.srsoftware.web4rail.tiles.Contact;
import de.srsoftware.web4rail.tiles.Shadow;
import de.srsoftware.web4rail.tiles.Signal;
import de.srsoftware.web4rail.tiles.Tile;
+import de.srsoftware.web4rail.tiles.Tile.Status;
import de.srsoftware.web4rail.tiles.Turnout;
/**
* A route is a vector of tiles that leads from one block to another.
@@ -53,9 +55,9 @@ import de.srsoftware.web4rail.tiles.Turnout;
*/
public class Route extends BaseClass {
- public enum State {
+/* public enum State {
FREE, LOCKED, PREPARED, STARTED;
- }
+ }*/
public static final Logger LOG = LoggerFactory.getLogger(Route.class);
private static final String ACTIONS = "actions";
@@ -70,7 +72,7 @@ public class Route extends BaseClass {
static final String PATH = "path";
static final String SIGNALS = "signals";
static final String TURNOUTS = "turnouts";
- private State state = State.FREE;
+ //private State state = State.FREE;
public static boolean freeBehindTrain = true;
private static final String ROUTE_START = "route_start";
@@ -79,11 +81,9 @@ public class Route extends BaseClass {
private static HashMap names = new HashMap(); // maps id to name. needed to keep names during plan.analyze()
-// private BrakeProcessor brakeProcessor = null;
private HashMap brakeTimes = new HashMap();
private ConditionList conditions;
private Vector contacts;
- private Context context; // this context is passed to actions
private boolean disabled = false;
private Block endBlock = null;
public Direction endDirection;
@@ -100,7 +100,7 @@ public class Route extends BaseClass {
conditions = new ConditionList();
conditions.parent(this);
}
-
+
/**
* process commands from the client
* @param params
@@ -121,12 +121,6 @@ public class Route extends BaseClass {
plan.stream(t("Removed {}.",route));
return plan.properties(new HashMap());
case ACTION_PROPS:
- return route.properties();
- case ACTION_START:
- route.set(new Context(route));
- route.prepare();
- route.context.clear();
-
return route.properties();
case ACTION_UPDATE:
return route.update(params,plan);
@@ -204,6 +198,10 @@ public class Route extends BaseClass {
turnouts.put(t, s);
}
+ public boolean allocateFor(Train newTrain) {
+ return pathState(newTrain,Tile.Status.ALLOCATED);
+ }
+
/**
* checks, whether the route may be used in a given context
* @param context
@@ -333,6 +331,7 @@ public class Route extends BaseClass {
ActionList actions = triggeredActions.get(contact.trigger());
LOG.debug("Contact has id {} / trigger {} and is assigned with {}",contact.id(),contact.trigger(),isNull(actions)?t("nothing"):actions);
if (isNull(actions)) return;
+ Context context = new Context(this).train(train);
actions.fire(context,"Route.Contact("+contact.addr()+")");
}
@@ -373,10 +372,6 @@ public class Route extends BaseClass {
return win;
}
- public Context context() {
- return context.clone();
- }
-
public void dropBraketimes(String...brakeIds) {
for (String brakeId : brakeIds) brakeTimes.remove(brakeId);
}
@@ -416,10 +411,10 @@ public class Route extends BaseClass {
return disabled;
}
- public boolean isFreeFor(Context context) {
- PathFinder.LOG.debug("{}.isFreeFor({})",this,context);
+ public boolean isFreeFor(Train newTrain) {
+ PathFinder.LOG.debug("{}.isFreeFor({})",this,newTrain);
for (int i=1; i ignoredPath = new HashSet();
- if (isSet(ignoredRoute)) ignoredPath.addAll(ignoredRoute.path);
- for (Tile tile : path) {
- if (ignoredPath.contains(tile)) continue;
- try {
- tile.setRoute(this);
- } catch (IllegalStateException e) {
- LOG.debug("{}.lockIgnoring(...) failed at {}, rolling back",this,tile);
- for (Tile lockedTile : path) { // unlock the same tiles that have been locked before, until we encounter the unlockable tile
- if (lockedTile == tile) return false;
- lockedTile.unset(this);
- }
- return false;
- }
- }
- state = State.LOCKED;
- return true;
- }
-
+
public List multiply(int size) {
Vector routes = new Vector();
for (int i=0; i visited = new Stack<>();
+ for (Tile t : path) {
+ if (t.setState(newState,newTrain)) {
+ visited.push(t);
+ } else {
+ while (!visited.isEmpty()) visited.pop().free();
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public boolean prepareFor(Train newTrain) {
+// if (state == State.PREPARED || state == State.STARTED) return true;
LOG.debug("{}.prepare()",this);
ActionList setupActions = triggeredActions.get(ROUTE_SETUP);
- if (isSet(setupActions) && !setupActions.fire(context,this+".prepare()")) return false;
- state = State.PREPARED;
+ if (isSet(setupActions) && !setupActions.fire(new Context(newTrain).route(this),this+".prepare()")) return false;
+// state = State.PREPARED;
+ pathState(newTrain,Tile.Status.LOCKED);
return true;
}
@@ -760,7 +743,6 @@ public class Route extends BaseClass {
LOG.debug("{}.reset()",this);
// TODO
- state = State.FREE;
return true;
}
@@ -778,13 +760,6 @@ public class Route extends BaseClass {
file.close();
}
- public Context set(Context newContext) {
- LOG.debug("{}.set({})",this,newContext);
- context = newContext;
- context.route(this);
- return context;
- }
-
public void setLast(Turnout.State state) {
if (isNull(state) || state == Turnout.State.UNDEF) return;
Tile lastTile = path.lastElement();
@@ -810,20 +785,21 @@ public class Route extends BaseClass {
return this;
}
- public Route.State state(){
- return state;
- }
-
public boolean start(Train newTrain) {
- if (state == State.STARTED) return true;
+// if (state == State.STARTED) return true;
LOG.debug("{}.start()",this);
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
- } else train = newTrain; // set new train
+ } else train = newTrain.setRoute(this); // set new train
+
ActionList startActions = triggeredActions.get(ROUTE_START);
- if (isSet(startActions) && !startActions.fire(context,this+".start("+train.name()+")")) return false; // start actions failed
- state = State.STARTED;
+
+ if (isSet(startActions)) {
+ Context context = new Context(train).route(this);
+ if (!startActions.fire(context,this+".start("+train.name()+")")) return false; // start actions failed
+ }
+// state = State.STARTED;
triggeredContacts.clear();
return true;
}
@@ -865,9 +841,8 @@ public class Route extends BaseClass {
newTrace.add(tile);
remainingLength -= tile.length();
} else if (Route.freeBehindTrain) {
- try {
- tile.unset(this);
- } catch (IllegalArgumentException e) {}
+
+ // TODO
} else break;
}
}
diff --git a/src/main/java/de/srsoftware/web4rail/actions/ActionList.java b/src/main/java/de/srsoftware/web4rail/actions/ActionList.java
index dc7b17e..27ecfeb 100644
--- a/src/main/java/de/srsoftware/web4rail/actions/ActionList.java
+++ b/src/main/java/de/srsoftware/web4rail/actions/ActionList.java
@@ -88,7 +88,10 @@ public class ActionList extends Action implements Iterable{
for (Action action : actions) {
LOG.debug("firing \"{}\"",action);
- if (!action.fire(context,cause)) return false;
+ if (!action.fire(context,cause)) {
+ LOG.warn("{} failed",action);
+ return false;
+ }
}
return true;
}
diff --git a/src/main/java/de/srsoftware/web4rail/conditions/BlockFree.java b/src/main/java/de/srsoftware/web4rail/conditions/BlockFree.java
index f37b042..d46794b 100644
--- a/src/main/java/de/srsoftware/web4rail/conditions/BlockFree.java
+++ b/src/main/java/de/srsoftware/web4rail/conditions/BlockFree.java
@@ -25,7 +25,7 @@ public class BlockFree extends Condition {
@Override
public boolean fulfilledBy(Context context) {
- return block.isFreeFor(null) != inverted;
+ return block.canNeEnteredBy(null) != inverted;
}
@Override
diff --git a/src/main/java/de/srsoftware/web4rail/moving/Train.java b/src/main/java/de/srsoftware/web4rail/moving/Train.java
index d957fd8..ece7fde 100644
--- a/src/main/java/de/srsoftware/web4rail/moving/Train.java
+++ b/src/main/java/de/srsoftware/web4rail/moving/Train.java
@@ -38,12 +38,23 @@ import de.srsoftware.web4rail.tags.Table;
import de.srsoftware.web4rail.tags.Window;
import de.srsoftware.web4rail.threads.PathFinder;
import de.srsoftware.web4rail.tiles.Block;
+import de.srsoftware.web4rail.tiles.Contact;
import de.srsoftware.web4rail.tiles.Tile;
+import de.srsoftware.web4rail.tiles.Tile.Status;
/**
* @author Stephan Richter, SRSoftware 2020-2021 *
*/
public class Train extends BaseClass implements Comparable {
+
+ public interface Listener {
+ enum Signal {
+ STOP
+ }
+
+ public void on(Signal signal);
+ }
+
private static final Logger LOG = LoggerFactory.getLogger(Train.class);
private static final String CAR_ID = "carId";
@@ -88,6 +99,8 @@ public class Train extends BaseClass implements Comparable {
private static final String SHUNTING = "shunting";
private boolean shunting = false;
+ private HashSet listeners = new HashSet();
+
public static Object action(HashMap params, Plan plan) throws IOException {
String action = params.get(ACTION);
if (isNull(action)) return t("No action passed to Train.action!");
@@ -147,10 +160,18 @@ public class Train extends BaseClass implements Comparable {
return t("Unknown action: {}",params.get(ACTION));
}
+ public Train add(Car car) {
+ if (isSet(car)) {
+ cars.add(car);
+ car.train(this);
+ }
+ return this;
+ }
+
public void addTag(String tag) {
tags.add(tag);
}
-
+
private Object addCar(HashMap params) {
LOG.debug("addCar({})",params);
String carId = params.get(CAR_ID);
@@ -161,12 +182,8 @@ public class Train extends BaseClass implements Comparable {
return properties();
}
- public Train add(Car car) {
- if (isSet(car)) {
- cars.add(car);
- car.train(this);
- }
- return this;
+ public void addListener(Listener listener) {
+ listeners.add(listener);
}
public boolean automatic() {
@@ -299,6 +316,11 @@ public class Train extends BaseClass implements Comparable {
return properties();
}
+ public void contact(Contact contact) {
+ if (isSet(route)) route.contact(contact);
+ }
+
+
public void coupleWith(Train parkingTrain,boolean swap) {
if (isSet(direction) && isSet(parkingTrain.direction) && parkingTrain.direction != direction) parkingTrain.turn();
if (swap) {
@@ -384,7 +406,7 @@ public class Train extends BaseClass implements Comparable {
}
public void dropTrace() {
- while (!trace.isEmpty()) trace.removeFirst().setTrain(null);
+ while (!trace.isEmpty()) trace.removeFirst().free();
}
private Tag faster(int steps) {
@@ -485,8 +507,14 @@ 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).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(TRACE)) json.getJSONArray(TRACE).forEach(elem -> {
+ Tile tile = plan.get(new Id(elem.toString()), false);
+ if (tile.setState(Status.OCCUPIED,this)) trace.add(tile);
+ });
+ if (json.has(BLOCK)) {// do not move this up! during set, other fields will be referenced!
+ currentBlock = (Block) plan.get(new Id(json.getString(BLOCK)), false);
+ currentBlock.setState(Status.OCCUPIED,this);
+ }
if (json.has(LOCOS)) { // for downward compatibility
for (Object id : json.getJSONArray(LOCOS)) add(BaseClass.get(new Id(""+id)));
}
@@ -724,10 +752,10 @@ public class Train extends BaseClass implements Comparable {
public void set(Block newBlock) {
LOG.debug("{}.set({})",this,newBlock);
- if (isSet(currentBlock)) currentBlock.setTrain(null);
+ if (isSet(currentBlock)) currentBlock.free();
currentBlock = newBlock;
if (isSet(currentBlock)) {
- currentBlock.setTrain(this);
+ currentBlock.setState(Status.OCCUPIED,this);
lastBlocks.add(newBlock);
if (lastBlocks.size()>32) lastBlocks.remove(0);
}
@@ -776,9 +804,9 @@ public class Train extends BaseClass implements Comparable {
return properties();
}
- protected Route setRoute(Route newRoute) {
+ public Train setRoute(Route newRoute) {
route = newRoute;
- return route;
+ return this;
}
public void setSpeed(int newSpeed) {
@@ -794,9 +822,9 @@ public class Train extends BaseClass implements Comparable {
LOG.debug("old trace: {}",trace);
trace.removeAll(newTrace);
- for (Tile tile : trace) tile.setTrain(null);
+ for (Tile tile : trace) tile.free();
trace = newTrace;
- for (Tile tile : trace) tile.setTrain(this);
+ for (Tile tile : trace) tile.setState(Status.OCCUPIED,this);
LOG.debug("new trace of {}: {}",this,trace);
}
@@ -835,27 +863,33 @@ public class Train extends BaseClass implements Comparable {
public void start() {
- Context context = new Context(this).block(currentBlock).direction(direction);
- new PathFinder(context) {
+ new PathFinder(this,currentBlock,direction) {
@Override
- public void found(Route r) {
- // TODO Auto-generated method stub
- LOG.debug("Route {} prepared for {}",r,Train.this);
+ public void aborted() {
+ LOG.debug("Aborted");
}
@Override
- public void locked(Route r) {
+ public void found(Route newRoute) {
// TODO Auto-generated method stub
- LOG.debug("Route {} locked for {}",r,Train.this);
+ LOG.debug("Found route {} for {}",newRoute,Train.this);
+ }
+
+ @Override
+ public void locked(Route newRoute) {
+ // TODO Auto-generated method stub
+ LOG.debug("Locked route {} for {}",newRoute,Train.this);
}
@Override
- public void prepared(Route r) {
- LOG.debug("Route {} prepared for {}",r,Train.this);
- setRoute(r).start(Train.this);
+ public void prepared(Route newRoute) {
+ LOG.debug("Prepared route {} for {}",newRoute,Train.this);
+ newRoute.start(Train.this);
}
- };
+
+
+ }.start();
}
public static void startAll() {
@@ -869,7 +903,8 @@ public class Train extends BaseClass implements Comparable {
}
public Object stopNow() {
-
+ setSpeed(0);
+ listeners.forEach(listener -> listener.on(Listener.Signal.STOP));
return properties();
}
@@ -911,6 +946,7 @@ public class Train extends BaseClass implements Comparable {
*/
public Train turn() {
LOG.debug("{}.turn()",this);
+ setSpeed(0);
for (Car car : cars) car.turn();
Collections.reverse(cars);
return reverse();
diff --git a/src/main/java/de/srsoftware/web4rail/threads/ControlUnit.java b/src/main/java/de/srsoftware/web4rail/threads/ControlUnit.java
index c93cb19..97e5054 100644
--- a/src/main/java/de/srsoftware/web4rail/threads/ControlUnit.java
+++ b/src/main/java/de/srsoftware/web4rail/threads/ControlUnit.java
@@ -321,7 +321,9 @@ public class ControlUnit extends Thread implements Constants{
Thread thread = new Thread() {
@Override
public void run() {
- ControlUnit.this.plan.sensor(addr,active);
+ set(false);
+ plan.sensor(addr,active);
+ set(true);
}
};
thread.setName(Application.threadName("CU.FeedBack("+addr+")"));
diff --git a/src/main/java/de/srsoftware/web4rail/threads/PathFinder.java b/src/main/java/de/srsoftware/web4rail/threads/PathFinder.java
index 5c27806..aef3ded 100644
--- a/src/main/java/de/srsoftware/web4rail/threads/PathFinder.java
+++ b/src/main/java/de/srsoftware/web4rail/threads/PathFinder.java
@@ -18,71 +18,76 @@ import de.srsoftware.web4rail.tiles.Block;
/**
* @author Stephan Richter, SRSoftware 2020-2021
*/
-public abstract class PathFinder extends BaseClass implements Runnable{
+public abstract class PathFinder extends BaseClass implements Runnable, Train.Listener{
public static final Logger LOG = LoggerFactory.getLogger(PathFinder.class);
- private Context context;
+// private Context context;
private boolean aborted = false;
+ private Direction direction;
+ private Block startBlock;
+ private Train train;
- public PathFinder(Context context) {
- this.context = context;
+ public PathFinder(Train train, Block start, Direction direction) {
+ this.train = train;
+ this.startBlock = start;
+ this.direction = direction;
}
public void abort() {
aborted = true;
+ aborted();
+ LOG.debug("aborted {}",this);
}
- private static TreeMap> availableRoutes(Context context,HashSet visitedRoutes){
+ private static TreeMap> availableRoutes(Train train, Block start, Direction startDir, HashSet visitedRoutes){
String inset = "";
for (int i=0; i> availableRoutes = new TreeMap>();
+ LOG.debug(inset+"PathFinder.availableRoutes({})",visitedRoutes);
boolean error = false;
- Block block = context.block();
- if (isNull(block)) {
- LOG.warn("{} → {}.availableRoutes called without context.block!",inset,Train.class.getSimpleName());
+ if (isNull(start)) {
+ LOG.warn("{} → {}.availableRoutes called without start block!",inset,Train.class.getSimpleName());
error = true;
}
- Train train = context.train();
if (isNull(train)) {
- LOG.warn("{}→ {}.availableRoutes called without context.train!",inset,Train.class.getSimpleName());
+ LOG.warn("{}→ {}.availableRoutes called without train!",inset,Train.class.getSimpleName());
error = true;
}
- if (error) return availableRoutes;
+ if (error) return new TreeMap>();
- Block destination = train.destination();
- Direction direction = context.direction();
- if (isSet(direction)) {
- LOG.debug("{}Looking for {}-bound routes from {}",inset,direction,block);
+ if (isSet(startDir)) {
+ LOG.debug("{}Looking for {}-bound routes from {}",inset,startDir,start);
} else {
- LOG.debug("{}Looking for all routes from {}",inset,block);
+ LOG.debug("{}Looking for all routes from {}",inset,start);
}//*/
+ Block destination = train.destination();
if (isSet(destination) && visitedRoutes.isEmpty()) LOG.debug("{}- Destination: {}",inset,destination);
- Route currentRoute = context.route();
-
- for (Route routeCandidate : block.routes()) {
- if (routeCandidate.path().firstElement() != block) continue; // Routen, die nicht vom aktuellen Block starten sind bubu
+ //Route currentRoute = context.route();
+ TreeMap> availableRoutes = new TreeMap>();
+
+ for (Route routeCandidate : start.routes()) {
+ if (routeCandidate.path().firstElement() != start) continue; // Routen, die nicht vom aktuellen Block starten sind bubu
if (visitedRoutes.contains(routeCandidate)) {
LOG.debug("{}→ Candidate {} would create loop, skipping",inset,routeCandidate.shortName());
continue;
}
- if (!routeCandidate.allowed(context)) {
+ Context c = new Context(train).block(start).direction(startDir);
+ if (!routeCandidate.allowed(c)) {
if (routeCandidate.endBlock() != destination) { // allowance may be overridden by destination
- LOG.debug("{} not allowed for {}",routeCandidate,context);
+ LOG.debug("{} not allowed for {}",routeCandidate,c);
continue; // Zug darf auf Grund einer nicht erfüllten Bedingung nicht auf die Route
}
- LOG.debug("{} not allowed for {} – overridden by selected destination",routeCandidate,context);
+ LOG.debug("{} not allowed for {} – overridden by selected destination",routeCandidate,c);
}
int priority = 0;
- if (isSet(direction) && routeCandidate.startDirection != direction) { // Route startet entgegen der aktuellen Fahrtrichtung des Zuges
+ if (isSet(startDir) && routeCandidate.startDirection != startDir) { // Route startet entgegen der aktuellen Fahrtrichtung des Zuges
if (!train.pushPull) continue; // Zug kann nicht wenden
- if (!block.turnAllowed) continue; // Wenden im Block nicht gestattet
+ if (!start.turnAllowed) continue; // Wenden im Block nicht gestattet
priority -= 5;
}
- if (routeCandidate == currentRoute) priority-=10; // möglichst andere Route als zuvor wählen // TODO: den Routen einen "last-used" Zeitstempel hinzufügen, und diesen mit in die Priorisierung einbeziehen
+ //if (routeCandidate == currentRoute) priority-=10; // möglichst andere Route als zuvor wählen // TODO: den Routen einen "last-used" Zeitstempel hinzufügen, und diesen mit in die Priorisierung einbeziehen
if (isSet(destination)) {
if (routeCandidate.endBlock() == destination) { // route goes directly to destination
@@ -90,9 +95,8 @@ public abstract class PathFinder extends BaseClass implements Runnable{
priority = 1_000_000;
} else {
LOG.debug("{}- Candidate: {}",inset,routeCandidate.shortName());
- Context forwardContext = new Context(train).block(routeCandidate.endBlock()).route(null).direction(routeCandidate.endDirection);
visitedRoutes.add(routeCandidate);
- TreeMap> forwardRoutes = availableRoutes(forwardContext,visitedRoutes);
+ TreeMap> forwardRoutes = availableRoutes(train, routeCandidate.endBlock(), routeCandidate.endDirection, visitedRoutes);
visitedRoutes.remove(routeCandidate);
if (forwardRoutes.isEmpty()) continue; // the candidate does not lead to a block, from which routes to the destination exist
Entry> entry = forwardRoutes.lastEntry();
@@ -110,7 +114,7 @@ public abstract class PathFinder extends BaseClass implements Runnable{
routeSet.add(routeCandidate);
if (routeCandidate.endBlock() == destination) break; // direct connection to destination discovered, quit search
}
- if (!availableRoutes.isEmpty()) LOG.debug("{}→ Routes from {}: {}",inset,block,availableRoutes.isEmpty()?"none":"");
+ if (!availableRoutes.isEmpty()) LOG.debug("{}→ Routes from {}: {}",inset,start,availableRoutes.isEmpty()?"none":"");
for (Entry> entry : availableRoutes.entrySet()) {
LOG.debug("{} - Priority {}:",inset,entry.getKey());
for (Route r : entry.getValue()) {
@@ -122,19 +126,20 @@ public abstract class PathFinder extends BaseClass implements Runnable{
public Route chooseRoute() {
LOG.debug("PathFinder.chooseRoute()");
- TreeMap> availableRoutes = availableRoutes(context,new HashSet());
+ HashSet visitedRoutes = new HashSet();
+ TreeMap> availableRoutes = availableRoutes(train, startBlock, direction,visitedRoutes);
while (!availableRoutes.isEmpty()) {
LOG.debug("availableRoutes: {}",availableRoutes);
Entry> entry = availableRoutes.lastEntry();
List preferredRoutes = entry.getValue();
LOG.debug("preferredRoutes: {}",preferredRoutes);
Route selectedRoute = preferredRoutes.get(random.nextInt(preferredRoutes.size()));
- if (selectedRoute.isFreeFor(context.route(selectedRoute))) {
+ if (selectedRoute.isFreeFor(train)) {
LOG.debug("Chose \"{}\" with priority {}.",selectedRoute,entry.getKey());
return selectedRoute;
}
- LOG.debug("Selected route \"{}\" is not free for {}",selectedRoute,context);
+ LOG.debug("Selected route \"{}\" is not free for {}",selectedRoute,train);
preferredRoutes.remove(selectedRoute);
if (preferredRoutes.isEmpty()) availableRoutes.remove(availableRoutes.lastKey());
}
@@ -142,17 +147,17 @@ public abstract class PathFinder extends BaseClass implements Runnable{
}
@Override
- public void run() {
+ public void run() {
while (true) {
- Route route = chooseRoute();
if (aborted) return;
+ Route route = chooseRoute();
if (isSet(route)) {
found(route);
if (aborted) return;
- if (route.lock()) {
+ if (route.allocateFor(train)) {
locked(route);
if (aborted) return;
- if (route.prepare()) {
+ if (route.prepareFor(train)) {
prepared(route);
return;
}
@@ -162,7 +167,24 @@ public abstract class PathFinder extends BaseClass implements Runnable{
}
}
+ public abstract void aborted();
public abstract void locked(Route r);
public abstract void found(Route r);
public abstract void prepared(Route r);
+
+ @Override
+ public void on(Signal signal) {
+ switch (signal) {
+ case STOP:
+ abort();
+ break;
+ }
+ }
+
+ public void start() {
+ train.addListener(this);
+ Thread thread = new Thread(this);
+ thread.setName("Pathfinder("+train+")");
+ thread.start();
+ }
}
diff --git a/src/main/java/de/srsoftware/web4rail/tiles/Block.java b/src/main/java/de/srsoftware/web4rail/tiles/Block.java
index 5bf4d4e..166c03e 100644
--- a/src/main/java/de/srsoftware/web4rail/tiles/Block.java
+++ b/src/main/java/de/srsoftware/web4rail/tiles/Block.java
@@ -146,10 +146,17 @@ public abstract class Block extends StretchableTile{
return t("Trigger contact to learn new contact");
}
+ @Override
+ public boolean canNeEnteredBy(Train newTrain) {
+ if (!super.canNeEnteredBy(newTrain)) return false;
+ if (parkedTrains.isEmpty()) return true;
+ return isNull(newTrain) ? false : newTrain.isShunting(); // block contains train(s), thus it is only free for shunting train
+ }
+
@Override
protected HashSet classes() {
HashSet classes = super.classes();
- if (!parkedTrains.isEmpty()) classes.add(OCCUPIED);
+ if (!parkedTrains.isEmpty()) classes.add(Status.OCCUPIED.toString());
return classes;
}
@@ -229,14 +236,6 @@ public abstract class Block extends StretchableTile{
return 1+internalContacts.indexOf(contact);
}
- @Override
- public boolean isFreeFor(Context context) {
- if (!super.isFreeFor(context)) return false;
- if (parkedTrains.isEmpty()) return true;
- Train t = isSet(context) ? context.train() : null;
- return isSet(t) ? t.isShunting() : false; // block contains train(s), thus it is olny free for shunting train
- }
-
@Override
public JSONObject json() {
JSONObject json = super.json();
@@ -358,7 +357,6 @@ public abstract class Block extends StretchableTile{
super.removeChild(child);
internalContacts.remove(child);
if (parkedTrains.remove(child)) plan.place(this);
- if (train == child) setTrain(null);
}
public void removeContact(BlockContact blockContact) {
diff --git a/src/main/java/de/srsoftware/web4rail/tiles/BlockContact.java b/src/main/java/de/srsoftware/web4rail/tiles/BlockContact.java
index fcd8031..8963560 100644
--- a/src/main/java/de/srsoftware/web4rail/tiles/BlockContact.java
+++ b/src/main/java/de/srsoftware/web4rail/tiles/BlockContact.java
@@ -27,11 +27,6 @@ public class BlockContact extends Contact {
return new Id(block.name+":"+block.indexOf(this));
}
- @Override
- public Route route() {
- return ((Block)parent()).route();
- }
-
@Override
public Tag tag(Map replacements) throws IOException {
return ((Block)parent()).tag(replacements);
diff --git a/src/main/java/de/srsoftware/web4rail/tiles/Bridge.java b/src/main/java/de/srsoftware/web4rail/tiles/Bridge.java
index b5e3c8b..260f92f 100644
--- a/src/main/java/de/srsoftware/web4rail/tiles/Bridge.java
+++ b/src/main/java/de/srsoftware/web4rail/tiles/Bridge.java
@@ -9,7 +9,6 @@ import org.json.JSONObject;
import de.srsoftware.tools.Tag;
import de.srsoftware.web4rail.BaseClass;
import de.srsoftware.web4rail.Connector;
-import de.srsoftware.web4rail.Route;
import de.srsoftware.web4rail.moving.Train;
import de.srsoftware.web4rail.tags.Fieldset;
import de.srsoftware.web4rail.tags.Window;
@@ -42,6 +41,12 @@ public abstract class Bridge extends Tile {
protected abstract Connector connector();
+ @Override
+ public void free() {
+ if (isSet(counterpart) && counterpart.train != null) counterpart.free();
+ super.free();
+ }
+
@Override
public JSONObject json() {
JSONObject json = super.json();
@@ -63,16 +68,10 @@ public abstract class Bridge extends Tile {
}
@Override
- public Tile setRoute(Route route) {
- super.setRoute(route);
- if (isSet(counterpart) && counterpart.route != route) counterpart.setRoute(route);
- return this;
- }
-
- public Tile setTrain(Train train) {
- super.setTrain(train);
- if (isSet(counterpart) && counterpart.train != train) counterpart.setTrain(train);
- return this;
+ public boolean setState(Status newState, Train newTrain) {
+ if (train == newTrain && is(newState)) return true;
+ if (!super.setState(newState,newTrain)) return false;
+ return isNull(counterpart) ? true : counterpart.setState(newState,newTrain);
}
@Override
@@ -109,11 +108,4 @@ 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/Contact.java b/src/main/java/de/srsoftware/web4rail/tiles/Contact.java
index 889302e..2e60e05 100644
--- a/src/main/java/de/srsoftware/web4rail/tiles/Contact.java
+++ b/src/main/java/de/srsoftware/web4rail/tiles/Contact.java
@@ -16,7 +16,6 @@ import org.slf4j.LoggerFactory;
import de.srsoftware.tools.Tag;
import de.srsoftware.web4rail.Application;
import de.srsoftware.web4rail.BaseClass;
-import de.srsoftware.web4rail.Route;
import de.srsoftware.web4rail.actions.Action;
import de.srsoftware.web4rail.actions.ActionList;
import de.srsoftware.web4rail.tags.Fieldset;
@@ -85,12 +84,12 @@ public class Contact extends Tile{
LOG.debug("{} activated.",this);
state = true;
if (isSet(timer)) timer.abort();
- Route route = route();
- Context context = isSet(route) ? route.context().contact(this) : new Context(this);
-
- if (isSet(route)) route.traceTrainFrom(this);
+ Context context = new Context(this);
+ if (isSet(train)) {
+ train.contact(this);
+ context.train(train);
+ }
actions.fire(context,"Contact("+addr+")");
- if (isSet(route)) route.contact(this);
for (Listener listener : listeners) listener.fired("Contact("+addr+")");
diff --git a/src/main/java/de/srsoftware/web4rail/tiles/Tile.java b/src/main/java/de/srsoftware/web4rail/tiles/Tile.java
index def0924..9df37f4 100644
--- a/src/main/java/de/srsoftware/web4rail/tiles/Tile.java
+++ b/src/main/java/de/srsoftware/web4rail/tiles/Tile.java
@@ -40,12 +40,27 @@ import de.srsoftware.web4rail.threads.PathFinder;
*
*/
public abstract class Tile extends BaseClass implements Comparable{
+ public enum Status{
+ FREE("free"),
+ ALLOCATED("allocated"),
+ LOCKED("locked"),
+ OCCUPIED("occupied");
+
+ private String tx;
+
+ Status(String s) {
+ tx = s;
+ }
+
+ @Override
+ public String toString() {
+ return tx;
+ }
+ }
protected static Logger LOG = LoggerFactory.getLogger(Tile.class);
private static int DEFAUT_LENGTH = 100; // 10cm
private static final String LENGTH = "length";
- private static final String LOCKED = "locked";
- protected static final String OCCUPIED = "occupied";
private static final String ONEW_WAY = "one_way";
private static final String POS = "pos";
private static final String TYPE = "type";
@@ -56,8 +71,8 @@ public abstract class Tile extends BaseClass implements Comparable{
private boolean isTrack = true;
private int length = DEFAUT_LENGTH;
protected Direction oneWay = null;
- protected Route route = null;
private TreeSet routes = new TreeSet<>((r1,r2)->r1.toString().compareTo(r2.toString()));
+ private Status status = Status.FREE;
protected Train train = null;
public Integer x = null;
public Integer y = null;
@@ -65,13 +80,38 @@ public abstract class Tile extends BaseClass implements Comparable{
public void add(Route route) {
this.routes.add(route);
}
+
+ public boolean canNeEnteredBy(Train newTrain) {
+ PathFinder.LOG.debug("{}.canNeEnteredBy({})",this,newTrain);
+ if (disabled) {
+ PathFinder.LOG.debug("{} is disabled!",this);
+ return false;
+ }
+
+ if (isNull(train)) {
+ PathFinder.LOG.debug("→ free");
+ return true;
+ }
+
+ if (newTrain == 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!
+ PathFinder.LOG.debug("already reserved by {} → true",train);
+ return true;
+ }
+
+ if (isSet(newTrain) && newTrain.isShunting()) {
+ PathFinder.LOG.debug("occupied by {}. Allowed for shunting {}",train,newTrain);
+ return true;
+ }
+
+ PathFinder.LOG.debug("occupied by {} → false",train);
+ return false;
+ }
protected HashSet classes(){
HashSet classes = new HashSet();
classes.add("tile");
- classes.add(getClass().getSimpleName());
- if (isSet(route)) classes.add(LOCKED);
- if (isSet(train)) classes.add(OCCUPIED);
+ classes.add(getClass().getSimpleName());
+ if (!is(Status.FREE)) classes.add(status.toString());
if (disabled) classes.add(DISABLED);
return classes;
}
@@ -96,6 +136,11 @@ public abstract class Tile extends BaseClass implements Comparable{
return new HashMap<>();
}
+ public void free() {
+ train = null;
+ status = Status.FREE;
+ }
+
public int height() {
return 1;
}
@@ -115,58 +160,18 @@ public abstract class Tile extends BaseClass implements Comparable{
if (tile instanceof TileWithShadow) ((TileWithShadow)tile).placeShadows();
plan.place(tile);
}
-
- public boolean isFreeFor(Context context) {
- PathFinder.LOG.debug("{}.isFreeFor({})",this,context);
- if (disabled) {
- PathFinder.LOG.debug("{} is disabled!",this);
- return false;
- }
- if (isNull(context)) {
- if (isSet(train)) {
- PathFinder.LOG.debug("{} is occupied by {}",this,train);
- return false;
- }
- if (isSet(route)) {
- PathFinder.LOG.debug("{} is occupied by {}",this,route);
- return false;
- }
- }
- if (isSet(train)) {
- Train contextTrain = context.train();
- boolean free = train == contextTrain; // 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) {
- PathFinder.LOG.debug("already reserved by {} → true",train);
- } else {
- if (isSet(contextTrain) && contextTrain.isShunting()) {
- PathFinder.LOG.debug("occupied by {}. Allowed for shunting {}",train,contextTrain);
- free = true;
- } else PathFinder.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()) {
- PathFinder.LOG.debug("reserved by other route: {}",route);
- if (isSet(route.train())) {
- if (route.train() == context.train()) {
- PathFinder.LOG.debug("that route is used by {}, which is also requesting this tile → true",route.train());
- return true;
- }
- }
- PathFinder.LOG.debug("{}.route.train = {} → false",this,route.train());
- return false;
+
+ public boolean is(Status...states) {
+ for (Status s: states) {
+ if (status == s) return true;
}
- PathFinder.LOG.debug("free");
- return true;
+ return false;
}
-
+
public JSONObject json() {
JSONObject json = super.json();
json.put(TYPE, getClass().getSimpleName());
if (isSet(x) && isSet(y)) json.put(POS, new JSONObject(Map.of(X,x,Y,y)));
- if (isSet(route)) json.put(ROUTE, route.id());
if (isSet(oneWay)) json.put(ONEW_WAY, oneWay);
if (disabled) json.put(DISABLED, true);
if (isSet(train)) json.put(REALM_TRAIN, train.id());
@@ -242,7 +247,7 @@ public abstract class Tile extends BaseClass implements Comparable{
protected void noTrack() {
isTrack = false;
}
-
+
public Tile position(int x, int y) {
this.x = x;
this.y = y;
@@ -256,16 +261,9 @@ public abstract class Tile extends BaseClass implements Comparable{
@Override
protected Window properties(List