started re-implementing route reservation

This commit is contained in:
Stephan Richter
2021-03-10 01:21:10 +01:00
parent 966c5340ba
commit 830c1863ad
17 changed files with 284 additions and 291 deletions

View File

@@ -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<String> classes() {
HashSet<String> 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) {

View File

@@ -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<String, Object> replacements) throws IOException {
return ((Block)parent()).tag(replacements);

View File

@@ -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;
}
}

View File

@@ -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+")");

View File

@@ -40,12 +40,27 @@ import de.srsoftware.web4rail.threads.PathFinder;
*
*/
public abstract class Tile extends BaseClass implements Comparable<Tile>{
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<Tile>{
private boolean isTrack = true;
private int length = DEFAUT_LENGTH;
protected Direction oneWay = null;
protected Route route = null;
private TreeSet<Route> 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<Tile>{
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<String> classes(){
HashSet<String> classes = new HashSet<String>();
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<Tile>{
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<Tile>{
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;
public boolean is(Status...states) {
for (Status s: states) {
if (status == s) return true;
}
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;
}
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<Tile>{
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<Tile>{
@Override
protected Window properties(List<Fieldset> preForm, FormInput formInputs, List<Fieldset> postForm) {
Fieldset fieldset = null;
if (isSet(route)) {
fieldset = new Fieldset(t("Route"));
route.link("p",t("Locked by {}",route)).addTo(fieldset);
}
if (isSet(train)) {
if (isSet(fieldset)) {
fieldset.children().firstElement().content(" / "+t("Train"));
} else fieldset = new Fieldset(t("Train"));
fieldset = new Fieldset(t("Train"));
train.link("span", t("Train")+":"+NBSP+train+NBSP).addTo(fieldset);
if (isSet(train.route())) {
train.button(t("stop"), Map.of(ACTION,ACTION_STOP)).addTo(fieldset);
@@ -352,10 +350,6 @@ public abstract class Tile extends BaseClass implements Comparable<Tile>{
}
return line;
}
public Route route() {
return route;
}
public TreeSet<Route> routes() {
return routes;
@@ -370,24 +364,16 @@ public abstract class Tile extends BaseClass implements Comparable<Tile>{
file.close();
}
public Tile setTrain(Train newTrain) {
LOG.debug("{}.setTrain({})",this,newTrain);
if (newTrain == train) return this; // nothing to update
this.train = newTrain;
return plan.place(this);
}
public Tile setRoute(Route lockingRoute) {
LOG.debug("{}.setRoute({})",this,lockingRoute);
if (isNull(lockingRoute)) throw new NullPointerException();
if (isSet(route)) {
if (route == lockingRoute) return this; // nothing changed
throw new IllegalStateException(this.toString()); // tile already locked by other route
}
route = lockingRoute;
return plan.place(this);
public boolean setState(Status newState,Train newTrain) {
if (isNull(newTrain)) return false;
if (isSet(train) && newTrain != train) return false; // already locked by other train
if (is(Status.OCCUPIED,newState)) return true; // do not downgrade occupied tiles, accept current state
train = newTrain;
status = newState;
plan.place(this);
return true;
}
public Tag tag(Map<String,Object> replacements) throws IOException {
int width = 100*width();
int height = 100*height();
@@ -477,7 +463,6 @@ public abstract class Tile extends BaseClass implements Comparable<Tile>{
if (child instanceof Route) routes.remove(child);
if (child == train) train = null;
if (child == route) route = null;
super.removeChild(child);
plan.place(this);
}
@@ -487,22 +472,6 @@ public abstract class Tile extends BaseClass implements Comparable<Tile>{
plan.place(this);
}
public void unlock() {
route = null;
train = null;
plan.place(this);
}
public Tile unset(Route oldRoute) {
LOG.debug("{}.unset({})",this,oldRoute);
if (route == null) return this;
if (route == oldRoute) {
route = null;
return plan.place(this);
}
throw new IllegalArgumentException(t("{} not occupied by {}!",this,oldRoute));
}
public Tile update(HashMap<String, String> params) {
LOG.debug("{}.update({})",getClass().getSimpleName(),params);
String oneWayDir = params.get("oneway");

View File

@@ -159,7 +159,7 @@ public abstract class Turnout extends Tile implements Device{
}
public Reply state(State newState) {
if (train != null && newState != state) return new Reply(415, t("{} locked by {}!",this,train));
if (is(Status.LOCKED,Status.OCCUPIED) && newState != state) return new Reply(415, t("{} locked by {}!",this,train));
if (address == 0) {
state = newState;
plan.place(this);

View File

@@ -16,8 +16,8 @@ public abstract class TurnoutL extends Turnout {
public Object click(boolean shift) throws IOException {
Object o = super.click(shift);
if (!shift) {
if (route != null) {
plan.stream(t("{} is locked by {}!",this,route));
if (isSet(train)) {
plan.stream(t("{} is locked by {}!",this,train));
} else state(state == State.STRAIGHT ? State.LEFT : State.STRAIGHT);
}
return o;

View File

@@ -16,8 +16,8 @@ public abstract class TurnoutR extends Turnout {
public Object click(boolean shift) throws IOException {
Object o = super.click(shift);
if (!shift) {
if (route != null) {
plan.stream(t("{} is locked by {}!",this,route));
if (isSet(train)) {
plan.stream(t("{} is locked by {}!",this,train));
} else state(state == State.STRAIGHT ? State.RIGHT : State.STRAIGHT);
}
return o;