Browse Source

Another refactoring step...

lookup-tables
Stephan Richter 5 years ago
parent
commit
540d1be489
  1. 8
      src/main/java/de/srsoftware/web4rail/BaseClass.java
  2. 5
      src/main/java/de/srsoftware/web4rail/EventListener.java
  3. 26
      src/main/java/de/srsoftware/web4rail/Route.java
  4. 5
      src/main/java/de/srsoftware/web4rail/actions/PreserveRoute.java
  5. 2
      src/main/java/de/srsoftware/web4rail/conditions/BlockFree.java
  6. 57
      src/main/java/de/srsoftware/web4rail/moving/Train.java
  7. 121
      src/main/java/de/srsoftware/web4rail/threads/RoutePrepper.java

8
src/main/java/de/srsoftware/web4rail/BaseClass.java

@ -8,6 +8,7 @@ import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Random; import java.util.Random;
@ -69,6 +70,7 @@ public abstract class BaseClass implements Constants{
private Contact contact; private Contact contact;
private Direction direction; private Direction direction;
private Integer waitTime; private Integer waitTime;
List<EventListener> invalidationListeners = new LinkedList<>();
public Context(BaseClass object) { public Context(BaseClass object) {
setMain(object); setMain(object);
@ -147,12 +149,18 @@ public abstract class BaseClass implements Constants{
public void invalidate() { public void invalidate() {
setMain(null); setMain(null);
invalidationListeners.forEach(EventListener::fire);
} }
public boolean invalidated() { public boolean invalidated() {
return isNull(main); return isNull(main);
} }
public void onInvalidate(EventListener listener) {
invalidationListeners.add(listener);
}
public Context setMain(BaseClass object) { public Context setMain(BaseClass object) {
main = object; main = object;
LOG.debug("{}.setMain({})",this,object); LOG.debug("{}.setMain({})",this,object);

5
src/main/java/de/srsoftware/web4rail/EventListener.java

@ -0,0 +1,5 @@
package de.srsoftware.web4rail;
public interface EventListener {
public void fire();
}

26
src/main/java/de/srsoftware/web4rail/Route.java

@ -38,6 +38,7 @@ import de.srsoftware.web4rail.tags.Input;
import de.srsoftware.web4rail.tags.Table; import de.srsoftware.web4rail.tags.Table;
import de.srsoftware.web4rail.tags.Window; import de.srsoftware.web4rail.tags.Window;
import de.srsoftware.web4rail.threads.BrakeProcess; import de.srsoftware.web4rail.threads.BrakeProcess;
import de.srsoftware.web4rail.threads.RoutePrepper;
import de.srsoftware.web4rail.tiles.Block; import de.srsoftware.web4rail.tiles.Block;
import de.srsoftware.web4rail.tiles.BlockContact; import de.srsoftware.web4rail.tiles.BlockContact;
import de.srsoftware.web4rail.tiles.Contact; import de.srsoftware.web4rail.tiles.Contact;
@ -95,6 +96,8 @@ public class Route extends BaseClass {
private HashSet<Contact> triggeredContacts = new HashSet<>(); private HashSet<Contact> triggeredContacts = new HashSet<>();
private Route nextPreparedRoute; private Route nextPreparedRoute;
private RoutePrepper nextRoutePrepper;
public Route() { public Route() {
conditions = new ConditionList(); conditions = new ConditionList();
@ -394,8 +397,6 @@ public class Route extends BaseClass {
} }
private void free() { private void free() {
context.invalidate(); // do not set to null:
// this action may be called from route.contact → finishRoute, which calls train.updateTrace afterwards, which in turn requires context
Train train = context.train(); Train train = context.train();
Vector<Tile> reversedPath = reverse(path()); Vector<Tile> reversedPath = reverse(path());
for (Tile tile : reversedPath) { for (Tile tile : reversedPath) {
@ -684,6 +685,19 @@ public class Route extends BaseClass {
return true; return true;
} }
public boolean prepareNext(Context context) {
if (isSet(nextRoutePrepper)) return false;
nextRoutePrepper = new RoutePrepper(context);
nextRoutePrepper.onRoutePrepared(() -> {
nextPreparedRoute = nextRoutePrepper.route();
nextRoutePrepper = null;
});
nextRoutePrepper.onFail(() -> {
nextRoutePrepper = null;
});
return nextRoutePrepper.prepareRoute();
}
private Tag previewScript() { private Tag previewScript() {
Tag script = new Tag("script").attr("type", "text/javascript"); Tag script = new Tag("script").attr("type", "text/javascript");
for (Tile tile : path) { for (Tile tile : path) {
@ -752,7 +766,7 @@ public class Route extends BaseClass {
public boolean reserveFor(Context newContext) { public boolean reserveFor(Context newContext) {
LOG.debug("{}.reserverFor({})",this,newContext); LOG.debug("{}.reserverFor({})",this,newContext);
if (isSet(context)) return false; // route already has context! // if (isSet(context)) return false; // route already has context!
context = newContext; context = newContext;
for (Tile tile : path) { for (Tile tile : path) {
if (newContext.invalidated() || !tile.reserveFor(newContext)) { if (newContext.invalidated() || !tile.reserveFor(newContext)) {
@ -765,6 +779,7 @@ public class Route extends BaseClass {
public boolean reset() { public boolean reset() {
LOG.debug("{}.reset()",this); LOG.debug("{}.reset()",this);
resetNext();
setSignals(Signal.RED); setSignals(Signal.RED);
Train train = context.train(); Train train = context.train();
free(); free();
@ -773,6 +788,11 @@ public class Route extends BaseClass {
return true; return true;
} }
public void resetNext() {
if (isSet(nextRoutePrepper)) nextRoutePrepper.stop();
if (isSet(nextPreparedRoute)) nextPreparedRoute.reset();
}
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));
file.write("{\""+ROUTES+"\":[\n"); file.write("{\""+ROUTES+"\":[\n");

5
src/main/java/de/srsoftware/web4rail/actions/PreserveRoute.java

@ -30,6 +30,9 @@ public class PreserveRoute extends Action {
LOG.debug("Not preserving route, as train needs to stop for {} ms at {}!",waitTime,endBlock); LOG.debug("Not preserving route, as train needs to stop for {} ms at {}!",waitTime,endBlock);
return false; // train is expected to wait in next block. return false; // train is expected to wait in next block.
} }
return isSet(route.getNextPreparedRoute()) || train.reserveRouteAfter(route); if (isSet(route.getNextPreparedRoute())) return true;
Context nextContext = new Context(train).block(route.endBlock()).direction(route.endDirection);
context.onInvalidate(nextContext::invalidate);
return route.prepareNext(nextContext);
} }
} }

2
src/main/java/de/srsoftware/web4rail/conditions/BlockFree.java

@ -25,7 +25,7 @@ public class BlockFree extends Condition {
@Override @Override
public boolean fulfilledBy(Context context) { public boolean fulfilledBy(Context context) {
return block.isFreeFor(null) != inverted; return block.isFreeFor(context) != inverted;
} }
@Override @Override

57
src/main/java/de/srsoftware/web4rail/moving/Train.java

@ -36,10 +36,9 @@ import de.srsoftware.web4rail.tags.Label;
import de.srsoftware.web4rail.tags.Select; import de.srsoftware.web4rail.tags.Select;
import de.srsoftware.web4rail.tags.Table; import de.srsoftware.web4rail.tags.Table;
import de.srsoftware.web4rail.tags.Window; import de.srsoftware.web4rail.tags.Window;
import de.srsoftware.web4rail.threads.RouteManager.Callback;
import de.srsoftware.web4rail.threads.BrakeProcess; import de.srsoftware.web4rail.threads.BrakeProcess;
import de.srsoftware.web4rail.threads.DelayedExecution; import de.srsoftware.web4rail.threads.DelayedExecution;
import de.srsoftware.web4rail.threads.RouteManager; import de.srsoftware.web4rail.threads.RoutePrepper;
import de.srsoftware.web4rail.tiles.Block; import de.srsoftware.web4rail.tiles.Block;
import de.srsoftware.web4rail.tiles.Contact; import de.srsoftware.web4rail.tiles.Contact;
import de.srsoftware.web4rail.tiles.Tile; import de.srsoftware.web4rail.tiles.Tile;
@ -92,7 +91,7 @@ public class Train extends BaseClass implements Comparable<Train> {
public int speed = 0; public int speed = 0;
private static final String SHUNTING = "shunting"; private static final String SHUNTING = "shunting";
private boolean shunting = false; private boolean shunting = false;
private RouteManager routeManager = null; private RoutePrepper routePrepper = null;
private HashSet<Tile> stuckTrace = null; private HashSet<Tile> stuckTrace = null;
@ -422,7 +421,7 @@ public class Train extends BaseClass implements Comparable<Train> {
if (isSet(brake)) brake.updateTime(); if (isSet(brake)) brake.updateTime();
Integer waitTime = route.waitTime(); Integer waitTime = route.waitTime();
nextPreparedRoute = route.dropNextPreparedRoute(); nextPreparedRoute = route.dropNextPreparedRoute();
if (isNull(nextPreparedRoute) || (isSet(waitTime) && waitTime > 0)) setSpeed(0); if ((!autopilot)|| isNull(nextPreparedRoute) || (isSet(waitTime) && waitTime > 0)) setSpeed(0);
route = null; route = null;
direction = endDirection; direction = endDirection;
endBlock.add(this, direction); endBlock.add(this, direction);
@ -490,7 +489,7 @@ public class Train extends BaseClass implements Comparable<Train> {
public boolean isStoppable() { public boolean isStoppable() {
if (speed > 0) return true; if (speed > 0) return true;
if (isSet(routeManager) && routeManager.isActive()) return true; if (isSet(routePrepper)) return true;
if (isSet(route)) return true; if (isSet(route)) return true;
return false; return false;
} }
@ -721,7 +720,8 @@ public class Train extends BaseClass implements Comparable<Train> {
} }
public String quitAutopilot() { public String quitAutopilot() {
if (isSet(routeManager)) routeManager.quit(); if (isSet(routePrepper)) routePrepper.stop();
// if (isSet(route)) route.resetNext();
autopilot = false; autopilot = false;
return t("Autopilot already was disabled!"); return t("Autopilot already was disabled!");
} }
@ -747,7 +747,7 @@ public class Train extends BaseClass implements Comparable<Train> {
//if (child == nextRoute) nextRoute = null; // TODO //if (child == nextRoute) nextRoute = null; // TODO
if (child == currentBlock) currentBlock = null; if (child == currentBlock) currentBlock = null;
if (child == destination) destination = null; if (child == destination) destination = null;
if (child == routeManager) routeManager = null; if (child == routePrepper) routePrepper.stop();
cars.remove(child); cars.remove(child);
trace.remove(child); trace.remove(child);
super.removeChild(child); super.removeChild(child);
@ -758,21 +758,6 @@ public class Train extends BaseClass implements Comparable<Train> {
return tags().iterator(); return tags().iterator();
} }
public boolean reserveRouteAfter(Route route) {
LOG.debug("reserveRouteAfter({})",route);
if (isNull(routeManager)) routeManager = new RouteManager();
Context newContext = new Context(this).block(route.endBlock()).direction(route.endDirection);
if (routeManager.isSearching()) return false;
routeManager.setContext(newContext);
routeManager.setCallback(new Callback() {
@Override
public void routePrepared(Route nextRoute) {
route.setNextPreparedRoute(nextRoute);
}
});
return routeManager.prepareRoute();
}
/** /**
* This turns the train as if it went through a loop. Example: * This turns the train as if it went through a loop. Example:
* before: CabCar MiddleCar Loco * before: CabCar MiddleCar Loco
@ -921,24 +906,20 @@ public class Train extends BaseClass implements Comparable<Train> {
nextPreparedRoute = null; nextPreparedRoute = null;
return null; return null;
} }
if (isNull(routeManager)) routeManager = new RouteManager(); if (isSet(routePrepper)) return t("Already searching route for {}",this);
routePrepper = new RoutePrepper(new Context(this).block(currentBlock).direction(direction));
Callback callback = new Callback() { routePrepper.onRoutePrepared(() -> {
@Override routePrepper.route().start();
public void routePrepared(Route route) { routePrepper = null;
route.start(); plan.stream(t("Started {}",Train.this));
plan.stream(t("Started {}",Train.this)); });
}
};
if (routeManager.isActive()) { // es wird bereits eine Anschlussroute gesucht routePrepper.onFail(() -> {
// in diesem Fall muss bloß dass Callback des Route-Managers aktualisiert werden routePrepper = null;
routeManager.setCallback(callback); });
} else { // routeManager nicht aktiv →> neuen Context setzen und starten
routeManager.setContext(new Context(this).block(currentBlock).direction(direction));
routeManager.start(callback);
}
routePrepper.start();
return null; return null;
} }
@ -950,7 +931,7 @@ public class Train extends BaseClass implements Comparable<Train> {
public void startBrake() { public void startBrake() {
LOG.debug("{}.startBrake()",this); LOG.debug("{}.startBrake()",this);
if (isSet(nextPreparedRoute)) return; if (autopilot && isSet(nextPreparedRoute)) return;
brake = new BrakeProcess(this); brake = new BrakeProcess(this);
} }

121
src/main/java/de/srsoftware/web4rail/threads/RouteManager.java → src/main/java/de/srsoftware/web4rail/threads/RoutePrepper.java

@ -1,45 +1,42 @@
package de.srsoftware.web4rail.threads; package de.srsoftware.web4rail.threads;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.TreeMap; import java.util.TreeMap;
import java.util.Vector; import java.util.Vector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import de.srsoftware.web4rail.Application; import de.srsoftware.web4rail.Application;
import de.srsoftware.web4rail.BaseClass; import de.srsoftware.web4rail.BaseClass;
import de.srsoftware.web4rail.EventListener;
import de.srsoftware.web4rail.Plan.Direction; import de.srsoftware.web4rail.Plan.Direction;
import de.srsoftware.web4rail.Route; import de.srsoftware.web4rail.Route;
import de.srsoftware.web4rail.moving.Train; import de.srsoftware.web4rail.moving.Train;
import de.srsoftware.web4rail.tiles.Block; import de.srsoftware.web4rail.tiles.Block;
import de.srsoftware.web4rail.tiles.Tile; import de.srsoftware.web4rail.tiles.Tile;
public class RouteManager extends BaseClass { public class RoutePrepper extends BaseClass implements Runnable{
private Context context;
private Route route;
private List<EventListener> failListeners = new LinkedList<>();
private List<EventListener> foundListeners = new LinkedList<>();
private List<EventListener> lockedListeners = new LinkedList<>();
private List<EventListener> preparedListeners= new LinkedList<>();
private enum State{ public RoutePrepper(Context c) {
IDLE,SEARCHING,PREPARING List<String> errors = new LinkedList<>();
} if (isNull(c.train())) errors.add(t("No train in context for {}",getClass().getSimpleName()));
if (isNull(c.block())) errors.add(t("No block in context for {}",getClass().getSimpleName()));
public static abstract class Callback { if (isNull(c.direction())) errors.add(t("No direction in context for {}",getClass().getSimpleName()));
public abstract void routePrepared(Route route); if (!errors.isEmpty()) throw new NullPointerException(String.join(", ", errors));
context = c;
} }
private static final Logger LOG = LoggerFactory.getLogger(RouteManager.class);
private Context ctx;
private State state;
private Callback callback;
public RouteManager() {
state =State.IDLE;
}
private static TreeMap<Integer, List<Route>> availableRoutes(Context context, HashSet<Route> visitedRoutes) { private static TreeMap<Integer, List<Route>> availableRoutes(Context context, HashSet<Route> visitedRoutes) {
String inset = ""; String inset = "";
for (int i = 0; i < visitedRoutes.size(); i++) inset += " "; for (int i = 0; i < visitedRoutes.size(); i++) inset += " ";
LOG.debug("{}{}.availableRoutes({})", inset, RouteManager.class.getSimpleName(), context); LOG.debug("{}{}.availableRoutes({})", inset, RoutePrepper.class.getSimpleName(), context);
Block block = context.block(); Block block = context.block();
Train train = context.train(); Train train = context.train();
@ -135,9 +132,9 @@ public class RouteManager extends BaseClass {
} }
return availableRoutes; return availableRoutes;
} }
public static Route chooseRoute(Context context) { private static Route chooseRoute(Context context) {
LOG.debug("{}.chooseRoute({})", RouteManager.class.getSimpleName(), context); LOG.debug("{}.chooseRoute({})", RoutePrepper.class.getSimpleName(), context);
TreeMap<Integer, List<Route>> availableRoutes = availableRoutes(context, new HashSet<Route>()); TreeMap<Integer, List<Route>> availableRoutes = availableRoutes(context, new HashSet<Route>());
while (!availableRoutes.isEmpty()) { while (!availableRoutes.isEmpty()) {
if (context.invalidated()) break; if (context.invalidated()) break;
@ -158,63 +155,71 @@ public class RouteManager extends BaseClass {
return null; return null;
} }
public boolean isActive() {
return state != State.IDLE; private void notify(List<EventListener> listeners) {
for (EventListener listener: listeners) {
listener.fire();
}
}
public void onFail(EventListener l) {
failListeners.add(l);
} }
public boolean isSearching() { public void onRouteFound(EventListener l) {
return state == State.SEARCHING; foundListeners.add(l);
}
public void onRouteLocked(EventListener l) {
lockedListeners.add(l);
} }
public void onRoutePrepared(EventListener l) {
preparedListeners.add(l);
}
public boolean prepareRoute() { public boolean prepareRoute() {
try { try {
if (isNull(ctx) || ctx.invalidated()) return false; if (isNull(context) || context.invalidated()) return false;
state = State.SEARCHING; route = chooseRoute(context);
Route route = chooseRoute(ctx); if (isNull(route)) return false;
if (isNull(route)) return false; context.route(route);
ctx.route(route); notify(foundListeners);
if (!route.reserveFor(ctx)) { if (!route.reserveFor(context)) {
route.reset(); route.reset();
route = null;
return false; return false;
} }
state = State.PREPARING; notify(lockedListeners);
if (!route.prepareAndLock()) { if (!route.prepareAndLock()) {
route.reset(); route.reset();
route = null;
return false; return false;
} }
if (isNull(route) || isNull(callback)) return false; notify(preparedListeners);
callback.routePrepared(route);
return true; return true;
} finally { } finally {
state = State.IDLE; if (isNull(route)) notify(failListeners);
} }
} }
public void quit() {
LOG.debug("{}.quit", this);
callback = null;
if (isSet(ctx)) ctx.invalidate();
}
public void setContext(Context context) { public Route route() {
ctx = context; return route;
} }
public void setCallback(Callback callback) { @Override
if (!ctx.invalidated()) this.callback = callback; public void run() {
prepareRoute();
} }
public void start(Callback callback) { public void start() {
setCallback(callback); new Thread(this,Application.threadName(this)).start();
new Thread(Application.threadName(this)) {
public void run() {
prepareRoute();
};
}.start();
} }
@Override public void stop() {
public String toString() { context.invalidate();
return getClass().getSimpleName() + "(" + ctx.train() + ")";
} }
} }
Loading…
Cancel
Save