Browse Source

bugfixes

lookup-tables
Stephan Richter 4 years ago
parent
commit
da2ad054ed
  1. 2
      pom.xml
  2. 2
      resources/logback.xml
  3. 5
      src/main/java/de/srsoftware/web4rail/BaseClass.java
  4. 5
      src/main/java/de/srsoftware/web4rail/Constants.java
  5. 10
      src/main/java/de/srsoftware/web4rail/Plan.java
  6. 9
      src/main/java/de/srsoftware/web4rail/Route.java
  7. 2
      src/main/java/de/srsoftware/web4rail/actions/DelayedAction.java
  8. 2
      src/main/java/de/srsoftware/web4rail/actions/DetermineTrainInBlock.java
  9. 6
      src/main/java/de/srsoftware/web4rail/actions/WaitForContact.java
  10. 149
      src/main/java/de/srsoftware/web4rail/moving/Train.java
  11. 201
      src/main/java/de/srsoftware/web4rail/tiles/Block.java
  12. 17
      src/main/java/de/srsoftware/web4rail/tiles/BlockContact.java
  13. 2
      src/main/java/de/srsoftware/web4rail/tiles/Bridge.java
  14. 18
      src/main/java/de/srsoftware/web4rail/tiles/Contact.java
  15. 239
      src/main/java/de/srsoftware/web4rail/tiles/Tile.java
  16. 4
      src/main/java/de/srsoftware/web4rail/tiles/Turnout.java
  17. 8
      src/main/java/de/srsoftware/web4rail/tiles/TurnoutL.java
  18. 8
      src/main/java/de/srsoftware/web4rail/tiles/TurnoutR.java

2
pom.xml

@ -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>1.4.3</version> <version>1.4.4</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>

2
resources/logback.xml

@ -9,7 +9,7 @@
</encoder> </encoder>
<filter class="de.srsoftware.web4rail.ThreadFilter"> <filter class="de.srsoftware.web4rail.ThreadFilter">
<level>DEBUG</level> <level>DEBUG</level>
<keywords>Brake, Contact, Feed, Route, Train</keywords> <keywords>Brake, Contact, Feed, Route, Train, e</keywords>
</filter> </filter>
</appender> </appender>

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

@ -315,6 +315,11 @@ public abstract class BaseClass implements Constants{
public Button button(String text) { public Button button(String text) {
return button(text,null); return button(text,null);
} }
public boolean debug(String tx, Object... fills) {
LOG.debug(tx, fills);
return false;
}
public Form form(String id,List<Map.Entry<String, Tag>> elements) { public Form form(String id,List<Map.Entry<String, Tag>> elements) {
Form form = new Form(id); Form form = new Form(id);

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

@ -58,15 +58,18 @@ public interface Constants {
public static final String DEFAULT_SPEED_UNIT = "km/h"; public static final String DEFAULT_SPEED_UNIT = "km/h";
public static final String DEFAULT_LENGTH_UNIT = "mm"; public static final String DEFAULT_LENGTH_UNIT = "mm";
public static final String DISABLED = "disabled"; public static final String DISABLED = "disabled";
public static final String DIRECTION = "direction"; public static final String DIRECTION = "direction";
public static final String GITHUB_URL = "https://github.com/srsoftware-de/Web4Rail"; public static final String GITHUB_URL = "https://github.com/srsoftware-de/Web4Rail";
public static final String ID = "id"; public static final String ID = "id";
public static final String LOCKED = "locked";
public static final String NAME = "name"; public static final String NAME = "name";
public static final String NBSP = "&nbsp;"; public static final String NBSP = "&nbsp;";
public static final String NOTES = "notes"; public static final String NOTES = "notes";
public static final String OCCUPIED = "occupied";
public static final String PARENT = "parent"; public static final String PARENT = "parent";
public static final String PORT = "port"; public static final String PORT = "port";
public static final String RELAY = "relay"; public static final String RELAY = "relay";
public static final String RESERVED = "reserved";
public static final String ROUTE = "route"; public static final String ROUTE = "route";
public static final String STATE = "state"; public static final String STATE = "state";
public static final String TURNOUT = "turnout"; public static final String TURNOUT = "turnout";

10
src/main/java/de/srsoftware/web4rail/Plan.java

@ -12,6 +12,7 @@ import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
@ -160,6 +161,7 @@ public class Plan extends BaseClass{
private ControlUnit controlUnit = new ControlUnit(this); // the control unit, to which the plan is connected private ControlUnit controlUnit = new ControlUnit(this); // the control unit, to which the plan is connected
private Contact learningContact; private Contact learningContact;
private Configuration appConfig; private Configuration appConfig;
private LinkedList<EventListener> listeners = new LinkedList<>();
/** /**
* creates a new plan, starts to send heart beats * creates a new plan, starts to send heart beats
@ -283,6 +285,10 @@ public class Plan extends BaseClass{
return t("Added {}",tile.getClass().getSimpleName()); return t("Added {}",tile.getClass().getSimpleName());
} }
public void alter() {
while (!listeners.isEmpty()) listeners.removeFirst().fire();
}
/** /**
* search all possible routes in the plan * search all possible routes in the plan
* @param params * @param params
@ -641,6 +647,10 @@ public class Plan extends BaseClass{
} }
return t(moved ? "Tile(s) moved.":"No tile moved."); return t(moved ? "Tile(s) moved.":"No tile moved.");
} }
public void onChange(EventListener listener) {
listeners.add(listener);
}
/** /**
* adds a new tile to the plan on the client side * adds a new tile to the plan on the client side

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

@ -4,7 +4,6 @@ import java.io.BufferedWriter;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileWriter; import java.io.FileWriter;
import java.io.IOException; import java.io.IOException;
import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
@ -37,7 +36,6 @@ import de.srsoftware.web4rail.tags.Fieldset;
import de.srsoftware.web4rail.tags.Input; 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.RoutePrepper; 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;
@ -389,7 +387,8 @@ public class Route extends BaseClass {
public void finish(Train train) { public void finish(Train train) {
LOG.debug("{}.finish()",this); LOG.debug("{}.finish()",this);
train.endRoute(endBlock,endDirection); context.invalidate();
train.endRoute(this);
setSignals(Signal.RED); setSignals(Signal.RED);
freeIgnoring(null); freeIgnoring(null);
train = null; train = null;
@ -767,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);
context = newContext; context = newContext;
for (Tile tile : path) { for (Tile tile : path) {
@ -859,7 +858,7 @@ public class Route extends BaseClass {
String cause = this+".start("+train.name()+")"; String cause = this+".start("+train.name()+")";
if (!startActions.fire(context,cause)) return false; // start actions failed if (!startActions.fire(context,cause)) return false; // start actions failed
} }
context.waitTime(endBlock.getWaitTime(train, endDirection).random()); context.waitTime(endBlock.getWaitTime(train, endDirection).random());
return true; return true;
} }

2
src/main/java/de/srsoftware/web4rail/actions/DelayedAction.java

@ -38,7 +38,9 @@ public class DelayedAction extends ActionList {
@Override @Override
public void execute() { public void execute() {
LOG.debug("{} ms passed by, firing actions:",delay); LOG.debug("{} ms passed by, firing actions:",delay);
if (context.invalidated()) return;
DelayedAction.super.fire(context,cause); DelayedAction.super.fire(context,cause);
plan.alter();
} }
}; };
return true; return true;

2
src/main/java/de/srsoftware/web4rail/actions/DetermineTrainInBlock.java

@ -24,7 +24,7 @@ public class DetermineTrainInBlock extends Action {
@Override @Override
public boolean fire(Context context,Object cause) { public boolean fire(Context context,Object cause) {
context.block(block); context.block(block);
context.train(block.train()); context.train(block.occupyingTrain());
return true; return true;
} }

6
src/main/java/de/srsoftware/web4rail/actions/WaitForContact.java

@ -8,13 +8,13 @@ import org.json.JSONObject;
import de.srsoftware.tools.Tag; import de.srsoftware.tools.Tag;
import de.srsoftware.web4rail.BaseClass; import de.srsoftware.web4rail.BaseClass;
import de.srsoftware.web4rail.EventListener;
import de.srsoftware.web4rail.LoadCallback; import de.srsoftware.web4rail.LoadCallback;
import de.srsoftware.web4rail.tags.Fieldset; import de.srsoftware.web4rail.tags.Fieldset;
import de.srsoftware.web4rail.tags.Input; import de.srsoftware.web4rail.tags.Input;
import de.srsoftware.web4rail.tags.Window; import de.srsoftware.web4rail.tags.Window;
import de.srsoftware.web4rail.threads.DelayedExecution; import de.srsoftware.web4rail.threads.DelayedExecution;
import de.srsoftware.web4rail.tiles.Contact; import de.srsoftware.web4rail.tiles.Contact;
import de.srsoftware.web4rail.tiles.Contact.Listener;
import de.srsoftware.web4rail.tiles.Tile; import de.srsoftware.web4rail.tiles.Tile;
public class WaitForContact extends ActionList { public class WaitForContact extends ActionList {
@ -35,9 +35,9 @@ public class WaitForContact extends ActionList {
LOG.debug("{}.fire(...) called, waiting for {}.",this,contact); LOG.debug("{}.fire(...) called, waiting for {}.",this,contact);
if (isNull(contact)) return false; if (isNull(contact)) return false;
fired = false; fired = false;
Listener listener = new Listener() { EventListener listener = new EventListener() {
@Override @Override
public void fired(Object cause) { public void fire() {
LOG.debug("{} triggered, firing {}",contact,actions); LOG.debug("{} triggered, firing {}",contact,actions);
fired = true; fired = true;
contact.removeListener(this); contact.removeListener(this);

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

@ -226,14 +226,14 @@ public class Train extends BaseClass implements Comparable<Train> {
carList.addHead(t("Car"),t("Actions")); carList.addHead(t("Car"),t("Actions"));
boolean first = true; boolean first = true;
for (Car car : this.cars) { for (Car car : cars) {
Tag link = car.link(car.name()+(car.stockId.isEmpty() ? "" : " ("+car.stockId+")")); Tag link = car.link(car.name()+(car.stockId.isEmpty() ? "" : " ("+car.stockId+")"));
Tag buttons = new Tag("span"); Tag buttons = new Tag("span");
car.button(t("turn within train"),Map.of(ACTION,ACTION_TURN)).addTo(buttons); car.button(t("turn within train"),Map.of(ACTION,ACTION_TURN)).addTo(buttons);
if (!first) { if (!first) {
car.button("↑",Map.of(ACTION,ACTION_MOVE)).addTo(buttons); car.button("↑",Map.of(ACTION,ACTION_MOVE)).addTo(buttons);
car.button(t("decouple"),Map.of(ACTION,ACTION_DECOUPLE)).addTo(buttons); car.button(t("decouple"),Map.of(ACTION,ACTION_DECOUPLE,REALM,REALM_CAR)).addTo(buttons);
} }
button(t("delete"),Map.of(ACTION,ACTION_DROP,CAR_ID,car.id().toString())).addTo(buttons); button(t("delete"),Map.of(ACTION,ACTION_DROP,CAR_ID,car.id().toString())).addTo(buttons);
carList.addRow(link,buttons); carList.addRow(link,buttons);
@ -275,7 +275,8 @@ public class Train extends BaseClass implements Comparable<Train> {
} }
if (isSet(currentBlock)) { if (isSet(currentBlock)) {
Tag ul = new Tag("ul"); Tag ul = new Tag("ul");
if (isSet(currentBlock.train()) && currentBlock.train() != this) currentBlock.train().link().addTo(new Tag("li")).addTo(ul); Train trainInBlock = isSet(currentBlock) ? currentBlock.occupyingTrain() : null;
if (isSet(trainInBlock) && trainInBlock != this) trainInBlock.link().addTo(new Tag("li")).addTo(ul);
for (Train tr : currentBlock.trains()) { for (Train tr : currentBlock.trains()) {
if (tr == this) continue; if (tr == this) continue;
Tag li = new Tag("li").addTo(ul); Tag li = new Tag("li").addTo(ul);
@ -319,10 +320,13 @@ public class Train extends BaseClass implements Comparable<Train> {
dummy.addAll(cars); dummy.addAll(cars);
cars = dummy; cars = dummy;
} else { } else {
for (Car car : parkingTrain.cars) cars.add(car.train(this)); for (Car car : parkingTrain.cars) {
cars.add(car.train(this));
}
} }
parkingTrain.remove(); parkingTrain.remove();
if (isSet(currentBlock)) currentBlock.setTrain(this);
} }
private static Object create(HashMap<String, String> params, Plan plan) { private static Object create(HashMap<String, String> params, Plan plan) {
@ -350,6 +354,28 @@ public class Train extends BaseClass implements Comparable<Train> {
} }
public Block destination(){ public Block destination(){
LOG.debug("{}.destination()",this);
if (isNull(destination)) {
String destTag = destinationTag();
LOG.debug("→ processing \"{}\"...",destTag);
if (isSet(destTag)) {
destTag = destTag.split(DESTINATION_PREFIX)[1];
LOG.debug("....processing \"{}\"…",destTag);
for (int i=destTag.length()-1; i>0; i--) {
switch (destTag.charAt(i)) {
case FLAG_SEPARATOR:
destTag = destTag.substring(0,i);
i=0;
break;
case SHUNTING_FLAG:
LOG.debug("....enabled shunting option");
shunting = true;
break;
}
}
destination = BaseClass.get(new Id(destTag));
}
}// else LOG.debug("→ heading towards {}",destination);
return destination; return destination;
} }
@ -360,11 +386,11 @@ public class Train extends BaseClass implements Comparable<Train> {
return null; return null;
} }
public String directedName(Direction dir) { public String directedName() {
String result = name(); String result = name();
String mark = autopilot ? "ⓐ" : ""; String mark = autopilot ? "ⓐ" : "";
if (isNull(dir)) return result; if (isNull(direction)) return result;
switch (dir) { switch (direction) {
case NORTH: case NORTH:
case WEST: case WEST:
return '←'+mark+result; return '←'+mark+result;
@ -416,35 +442,75 @@ public class Train extends BaseClass implements Comparable<Train> {
} }
} }
public void endRoute(Block endBlock, Direction endDirection) { public void endRoute(Route endedRoute) {
BrakeProcess brake = endBrake(); BrakeProcess brake = endBrake();
direction = endedRoute.endDirection; // muss vor der Auswertung des Destination-Tags stehen!
Block endBlock = endedRoute.endBlock();
Block startBlock = endedRoute.startBlock();
shunting = false;
if (endBlock == destination) { if (endBlock == destination) {
quitAutopilot();
destination = null; destination = null;
String destTag = destinationTag();
if (isSet(destTag)) {
LOG.debug("destination list: {}",destTag);
String[] parts = destTag.split(Train.DESTINATION_PREFIX);
for (int i=0; i<parts.length;i++) LOG.debug(" part {}: {}",i+1,parts[i]);
String destId = parts[1];
LOG.debug("destination tag: {}",destId);
boolean turn = false;
for (int i=destId.length()-1; i>0; i--) {
switch (destId.charAt(i)) {
case Train.FLAG_SEPARATOR:
destId = destId.substring(0,i);
i=0;
break;
case Train.TURN_FLAG:
turn = true;
LOG.debug("Turn flag is set!");
break;
}
}
if (destId.equals(endBlock.id().toString())) {
if (turn) turn();
// update destination tag: remove and add altered tag:
removeTag(destTag);
destTag = destTag.substring(parts[1].length()+1);
if (destTag.isEmpty()) { // no further destinations
destTag = null;
} else addTag(destTag);
}
}
if (isNull(destTag)) {
quitAutopilot();
plan.stream(t("{} reached it`s destination!",this));
}
} }
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 ((!autopilot)|| isNull(nextPreparedRoute) || (isSet(waitTime) && waitTime > 0)) setSpeed(0); if ((!autopilot)|| isNull(nextPreparedRoute) || (isSet(waitTime) && waitTime > 0)) setSpeed(0);
route = null; route = null;
direction = endDirection; endBlock.setTrain(this);
endBlock.add(this, direction);
currentBlock = endBlock; currentBlock = endBlock;
trace.add(endBlock); trace.add(endBlock);
if (!trace.contains(startBlock)) startBlock.dropTrain(this);
stuckTrace = null; stuckTrace = null;
if (autopilot) { if (autopilot) {
if (isNull(waitTime) || waitTime == 0) { if (isNull(waitTime)) waitTime = 0;
start(false); if (waitTime>0) plan.stream(t("{} waiting {} secs",this,(int)(waitTime/1000)));
} else if (isSet(waitTime) && waitTime > 0) { new DelayedExecution(waitTime,this) {
new DelayedExecution(waitTime,this) {
@Override
@Override public void execute() {
public void execute() { if (autopilot) Train.this.start(false);
if (autopilot) Train.this.start(false); }
} };
};
plan.stream(t("{} waiting {} secs",this,(int)(waitTime/1000)));
}
} }
} }
@ -566,16 +632,14 @@ public class Train extends BaseClass implements Comparable<Train> {
public void afterLoad() { public void afterLoad() {
if (json.has(TRACE)) json.getJSONArray(TRACE).forEach(elem -> { if (json.has(TRACE)) json.getJSONArray(TRACE).forEach(elem -> {
Tile tile = plan.get(new Id(elem.toString()), false); Tile tile = plan.get(new Id(elem.toString()), false);
if (tile instanceof Block) { tile.setTrain(Train.this);
((Block)tile).add(Train.this, direction);
} else if (tile.setTrain(Train.this));
trace.add(tile); trace.add(tile);
}); });
if (json.has(BLOCK)) {// do not move this up! during set, other fields will be referenced! if (json.has(BLOCK)) {// do not move this up! during set, other fields will be referenced!
currentBlock = (Block) plan.get(Id.from(json, BLOCK), false); currentBlock = (Block) plan.get(Id.from(json, BLOCK), false);
if (isSet(currentBlock)) { if (isSet(currentBlock)) {
currentBlock.setTrain(Train.this); currentBlock.setTrain(Train.this);
currentBlock.add(Train.this, direction); // currentBlock.add(Train.this, direction);
} }
} }
} }
@ -773,10 +837,7 @@ public class Train extends BaseClass implements Comparable<Train> {
LOG.debug("train.reverse();"); LOG.debug("train.reverse();");
if (isSet(direction)) direction = direction.inverse(); if (isSet(direction)) direction = direction.inverse();
if (isSet(currentBlock)) { if (isSet(currentBlock)) plan.place(currentBlock);
currentBlock.set(this,direction);
plan.place(currentBlock);
}
return this; return this;
} }
@ -805,10 +866,10 @@ public class Train extends BaseClass implements Comparable<Train> {
return select; return select;
} }
public void set(Block newBlock) { public Train set(Block newBlock) {
LOG.debug("{}.set({})",this,newBlock); LOG.debug("{}.set({})",this,newBlock);
if (isSet(currentBlock)) { if (isSet(currentBlock)) {
if (newBlock == currentBlock) return; if (newBlock == currentBlock) return this;
currentBlock.free(this); currentBlock.free(this);
} }
currentBlock = newBlock; currentBlock = newBlock;
@ -817,6 +878,7 @@ public class Train extends BaseClass implements Comparable<Train> {
lastBlocks.add(newBlock); lastBlocks.add(newBlock);
if (lastBlocks.size()>32) lastBlocks.remove(0); if (lastBlocks.size()>32) lastBlocks.remove(0);
} }
return this;
} }
private Object setDestination(HashMap<String, String> params) { private Object setDestination(HashMap<String, String> params) {
@ -899,7 +961,7 @@ public class Train extends BaseClass implements Comparable<Train> {
if (remaining.cars.isEmpty()) return false; if (remaining.cars.isEmpty()) return false;
remaining.direction = this.direction; remaining.direction = this.direction;
this.name = null; this.name = null;
currentBlock.add(remaining,direction); currentBlock.addParkedTrain(remaining);
remaining.currentBlock = currentBlock; remaining.currentBlock = currentBlock;
plan.place(currentBlock); plan.place(currentBlock);
return true; return true;
@ -926,15 +988,9 @@ public class Train extends BaseClass implements Comparable<Train> {
routePrepper = null; routePrepper = null;
if (isSet(failedRoute)) failedRoute.reset(); if (isSet(failedRoute)) failedRoute.reset();
LOG.debug("Starting {} failed due to unavailable route!",this); LOG.debug("Starting {} failed due to unavailable route!",this);
if (autopilot) new DelayedExecution(250,this) { plan.onChange(()->{ // wait for state change of plan
if (autopilot) Train.this.start(false);
@Override });
public void execute() {
if (autopilot) {
Train.this.start(false);
}
}
};
}); });
routePrepper.start(); routePrepper.start();
@ -1017,7 +1073,10 @@ public class Train extends BaseClass implements Comparable<Train> {
LOG.debug("update({})",params); LOG.debug("update({})",params);
pushPull = params.containsKey(PUSH_PULL) && params.get(PUSH_PULL).equals("on"); pushPull = params.containsKey(PUSH_PULL) && params.get(PUSH_PULL).equals("on");
shunting = params.containsKey(SHUNTING) && params.get(SHUNTING).equals("on"); shunting = params.containsKey(SHUNTING) && params.get(SHUNTING).equals("on");
if (params.containsKey(NAME)) name = params.get(NAME); if (params.containsKey(NAME)) {
name = params.get(NAME);
if (isSet(currentBlock)) plan.place(currentBlock);
}
if (params.containsKey(TAGS)) { if (params.containsKey(TAGS)) {
String[] parts = params.get(TAGS).replace(",", " ").split(" "); String[] parts = params.get(TAGS).replace(",", " ").split(" ");
tags.clear(); tags.clear();

201
src/main/java/de/srsoftware/web4rail/tiles/Block.java

@ -3,6 +3,7 @@ package de.srsoftware.web4rail.tiles;
import java.io.IOException; import java.io.IOException;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@ -43,16 +44,14 @@ public abstract class Block extends StretchableTile{
private class TrainList implements Iterable<Train>{ private class TrainList implements Iterable<Train>{
private LinkedList<Train> trains = new LinkedList<Train>(); private LinkedList<Train> trains = new LinkedList<Train>();
private HashMap<Train, Direction> dirs = new HashMap<Train, Direction>();
public void add(Train train, Direction direction) { public void add(Train train) {
trains.remove(train); trains.remove(train);
trains.addFirst(train); trains.addFirst(train);
if (isSet(direction)) dirs.put(train, direction);
} }
public Direction directionOf(Train train) { public boolean contains(Train t) {
return dirs.get(train); return trains.contains(t);
} }
public Train first() { public Train first() {
@ -77,18 +76,12 @@ public abstract class Block extends StretchableTile{
} }
public boolean remove(BaseClass b) { public boolean remove(BaseClass b) {
dirs.remove(b);
return trains.remove(b); return trains.remove(b);
} }
public void set(Train train, Direction newDirection) {
dirs.put(train, newDirection);
}
@Override @Override
public String toString() { public String toString() {
return trains.stream().map(t -> t+"→"+directionOf(t)).reduce((s1,s2) -> s1+", "+s2).orElse(t("empty")); return trains.stream().map(t -> "["+t+"]→"+t.direction()).reduce((s1,s2) -> s1+", "+s2).orElse(t("empty"));
} }
} }
private static final String ALLOW_TURN = "allowTurn"; private static final String ALLOW_TURN = "allowTurn";
@ -100,12 +93,11 @@ public abstract class Block extends StretchableTile{
private static final String RAISE = "raise"; private static final String RAISE = "raise";
public static final String ACTION_ADD_CONTACT = "add_contact"; public static final String ACTION_ADD_CONTACT = "add_contact";
private static final String PARKED_TRAINS = "parked_trains"; private static final String PARKED_TRAINS = "parked_trains";
private static final String TRAINS = "trains";
public String name = "Block"; public String name = "Block";
public boolean turnAllowed = false; public boolean turnAllowed = false;
private Vector<BlockContact> internalContacts = new Vector<BlockContact>(); private Vector<BlockContact> internalContacts = new Vector<BlockContact>();
private TrainList trains = new TrainList(); private TrainList parkedTrains = new TrainList();
public Block() { public Block() {
super(); super();
@ -191,10 +183,11 @@ public abstract class Block extends StretchableTile{
private Vector<WaitTime> waitTimes = new Vector<WaitTime>(); private Vector<WaitTime> waitTimes = new Vector<WaitTime>();
public void add(Train train,Direction direction) { public void addParkedTrain(Train train) {
if (parkedTrains.contains(train)) return;
if (isNull(train)) return; if (isNull(train)) return;
train.register(); train.register();
trains.add(train,direction); parkedTrains.add(train);
} }
@ -204,10 +197,17 @@ public abstract class Block extends StretchableTile{
return t("Trigger contact to learn new contact"); return t("Trigger contact to learn new contact");
} }
@Override
protected HashSet<String> classes() {
HashSet<String> classes = super.classes();
if (!parkedTrains.isEmpty()) classes.add(OCCUPIED);
return classes;
}
@Override @Override
public Object click(boolean shift) throws IOException { public Object click(boolean shift) throws IOException {
if (!trains.isEmpty() && !shift) return trains.first().properties(); Train train = occupyingTrain();
return super.click(false); return !shift && isSet(train) ? train.properties() : properties();
} }
public int compareTo(Block other) { public int compareTo(Block other) {
@ -255,25 +255,15 @@ public abstract class Block extends StretchableTile{
return this; return this;
} }
private void dropFirstTrain() { public void dropTrain(Train t) {
Train train = trains.first(); parkedTrains.remove(t);
if (isSet(train)) { plan.place(this);
train.dropTrace();
train.set(null);
trains.remove(train);
}
} }
@Override @Override
public boolean free(Train train) { public boolean free(Train oldTrain) {
LOG.debug("{}.free({})",this,train); parkedTrains.remove(oldTrain);
Train firstTrain = trains.first(); if (!super.free(oldTrain)) return false;
if (isNull(firstTrain)) return true;
if (firstTrain != train) return false;
trains.remove(train);
if (isSet(firstTrain)) {
super.free(train);
} else super.setTrain(firstTrain);
return true; return true;
} }
@ -304,11 +294,10 @@ public abstract class Block extends StretchableTile{
@Override @Override
public boolean isFreeFor(Context context) { public boolean isFreeFor(Context context) {
Train train = context.train(); if (!super.isFreeFor(context)) return false;
if (is(Status.DISABLED)) return false; Train train = context.train();
if (trains.isEmpty()) return true; if (isSet(occupyingTrain()) && occupyingTrain() == train) return true;
if (trains.first() == train) return true; return parkedTrains.isEmpty() || parkedTrains.contains(train) || train.isShunting();
return train.isShunting(); // block contains train(s), thus it is only free for shunting train
} }
@Override @Override
@ -317,7 +306,7 @@ public abstract class Block extends StretchableTile{
json.put(NAME, name); json.put(NAME, name);
json.put(ALLOW_TURN, turnAllowed); json.put(ALLOW_TURN, turnAllowed);
JSONArray jWaitTimes = new JSONArray(); JSONArray jWaitTimes = new JSONArray();
for (WaitTime wt : waitTimes) jWaitTimes.put(wt.json()); waitTimes.forEach(wt -> jWaitTimes.put(wt.json()));
json.put(WAIT_TIMES, jWaitTimes); json.put(WAIT_TIMES, jWaitTimes);
JSONObject jContacts = null; JSONObject jContacts = null;
for (BlockContact contact : internalContacts) { for (BlockContact contact : internalContacts) {
@ -328,22 +317,22 @@ public abstract class Block extends StretchableTile{
} }
if (isSet(jContacts)) json.put(CONTACT, jContacts); if (isSet(jContacts)) json.put(CONTACT, jContacts);
json.remove(REALM_TRAIN); // is set by TRAINS field for blocks json.remove(REALM_TRAIN); // is set by TRAINS field for blocks
if (!trains.isEmpty()) { if (!parkedTrains.isEmpty()) {
JSONArray jTrains = new JSONArray(); JSONArray jTrains = new JSONArray();
for (Train train : trains) { for (Train train : parkedTrains) {
JSONObject to = new JSONObject(); JSONObject to = new JSONObject();
to.put(ID, train.id()); to.put(ID, train.id());
Direction dir = trains.directionOf(train); // Direction dir = parkedTrains.directionOf(train);
if (isSet(dir)) to.put(DIRECTION, dir.toString()); // if (isSet(dir)) to.put(DIRECTION, dir.toString());
jTrains.put(to); jTrains.put(to);
} }
json.put(TRAINS, jTrains); json.put(PARKED_TRAINS, jTrains);
} }
return json; return json;
} }
public Train lastTrain() { public Train lastTrain() {
return trains.last(); return parkedTrains.last();
} }
public List<Route> leavingRoutes() { public List<Route> leavingRoutes() {
@ -387,26 +376,15 @@ public abstract class Block extends StretchableTile{
new LoadCallback() { new LoadCallback() {
@Override @Override
public void afterLoad() { public void afterLoad() {
if (json.has(TRAINS)) { if (json.has(PARKED_TRAINS)) {
JSONArray jTrains = json.getJSONArray(TRAINS); JSONArray jParkedTrains = json.getJSONArray(PARKED_TRAINS);
for (Object o : jTrains) { for (Object o : jParkedTrains) {
if (o instanceof JSONObject) { if (o instanceof JSONObject) {
JSONObject to = (JSONObject) o; JSONObject trainData = (JSONObject) o;
Id tID = new Id(to.getString(ID)); Train train = BaseClass.get(Id.from(trainData));
Train train = BaseClass.get(tID); if (isSet(train)) parkedTrains.add(train.set(Block.this));
Direction direction = to.has(DIRECTION) ? Direction.valueOf(to.getString(DIRECTION)) : null;
if (isSet(train)) {
train.set(Block.this);
trains.add(train, direction);
status = Status.OCCUPIED;
}
} }
} }
} else if (json.has(PARKED_TRAINS)) { // legacy
for (Object id : json.getJSONArray(PARKED_TRAINS)) {
Train train = BaseClass.get(new Id(id.toString()));
if (isSet(train)) trains.add(train,null);
}
} }
} }
}; };
@ -417,38 +395,20 @@ public abstract class Block extends StretchableTile{
@Override @Override
public boolean lockFor(Context context, boolean downgrade) { public boolean lockFor(Context context, boolean downgrade) {
Train newTrain = context.train(); Train newTrain = context.train();
Route route = context.route(); LOG.debug("{}.lockFor({})",this,newTrain);
LOG.debug("{}.lock({})",this,newTrain); Train train = lockingTrain();
if (isNull(newTrain)) return false; if (newTrain == train || parkedTrains.isEmpty() || parkedTrains.contains(newTrain) || newTrain.isShunting()) return super.lockFor(context, downgrade);
Train train = trains.first(); return false;
if (isSet(train) && train != newTrain) return false;
switch (status) {
case DISABLED:
return false;
case OCCUPIED:
if (!downgrade) break;
case FREE:
case RESERVED:
status = Status.LOCKED;
Direction dir = trains.directionOf(train);
if (isSet(route) && this == route.endBlock()) dir = route.endDirection;
add(newTrain,dir);
plan.place(this);
break;
case LOCKED:
break; // do not downgrade
}
return true;
} }
@Override @Override
protected Window properties(List<Fieldset> preForm, FormInput formInputs, List<Fieldset> postForm,String...errors) { protected Window properties(List<Fieldset> preForm, FormInput formInputs, List<Fieldset> postForm,String...errors) {
formInputs.add(t("Name"),new Input(NAME, name)); formInputs.add(t("Name"),new Input(NAME, name));
formInputs.add("",new Checkbox(ALLOW_TURN,t("Turn allowed"),turnAllowed)); formInputs.add("",new Checkbox(ALLOW_TURN,t("Turn allowed"),turnAllowed));
formInputs.add(t("Train"),Train.selector(trains.first(), null)); formInputs.add(t("Train"),Train.selector(parkedTrains.first(), null));
postForm.add(contactForm()); postForm.add(contactForm());
postForm.add(waitTimeForm()); postForm.add(waitTimeForm());
if (!trains.isEmpty()) postForm.add(trainList()); if (!parkedTrains.isEmpty()) postForm.add(trainList());
return super.properties(preForm, formInputs, postForm,errors); return super.properties(preForm, formInputs, postForm,errors);
} }
@ -467,27 +427,10 @@ public abstract class Block extends StretchableTile{
@Override @Override
public boolean reserveFor(Context context) { public boolean reserveFor(Context context) {
Train newTrain = context.train(); Train newTrain = context.train();
Route route = context.route(); LOG.debug("{}.lockFor({})",this,newTrain);
LOG.debug("{}.reserverFor({})",this,newTrain); Train train = lockingTrain();
if (isNull(newTrain)) return false; if (newTrain == train || parkedTrains.isEmpty() || parkedTrains.contains(newTrain) || newTrain.isShunting()) return super.reserveFor(context);
Train train = trains.first(); return false;
if (isSet(train) && train != newTrain) return false;
switch (status) {
case DISABLED:
return false;
case FREE:
status = Status.RESERVED;
Direction dir = trains.directionOf(train);
if (isSet(route) && this == route.endBlock()) dir = route.endDirection;
add(newTrain,dir);
plan.place(this);
break;
case OCCUPIED:
case LOCKED:
case RESERVED:
break; // do not downgrade
}
return true;
} }
public BlockContact register(BlockContact contact) { public BlockContact register(BlockContact contact) {
@ -499,18 +442,13 @@ public abstract class Block extends StretchableTile{
public void removeChild(BaseClass child) { public void removeChild(BaseClass child) {
super.removeChild(child); super.removeChild(child);
internalContacts.remove(child); internalContacts.remove(child);
if (trains.remove(child)) plan.place(this); if (parkedTrains.remove(child)) plan.place(this);
} }
public void removeContact(BlockContact blockContact) { public void removeContact(BlockContact blockContact) {
internalContacts.remove(blockContact); internalContacts.remove(blockContact);
} }
public void set(Train train, Direction direction) {
trains.set(train,direction);
}
public abstract List<Connector> startPoints(); public abstract List<Connector> startPoints();
@Override @Override
@ -518,7 +456,9 @@ public abstract class Block extends StretchableTile{
if (isNull(replacements)) replacements = new HashMap<String, Object>(); if (isNull(replacements)) replacements = new HashMap<String, Object>();
replacements.put("%text%",name); replacements.put("%text%",name);
Vector<String> trainNames = new Vector<String>(); Vector<String> trainNames = new Vector<String>();
for (Train train: trains) trainNames.add(train.directedName(trains.directionOf(train))); Train lockingTrain = lockingTrain();
if (isSet(lockingTrain) /*&& !parkedTrains.contains(lockingTrain)*/) trainNames.add(lockingTrain.directedName());
for (Train train: parkedTrains) trainNames.add(train.directedName());
if (!trainNames.isEmpty())replacements.put("%text%",String.join(" | ", trainNames)); if (!trainNames.isEmpty())replacements.put("%text%",String.join(" | ", trainNames));
Tag tag = super.tag(replacements); Tag tag = super.tag(replacements);
tag.clazz(tag.get("class")+" Block"); tag.clazz(tag.get("class")+" Block");
@ -535,20 +475,10 @@ public abstract class Block extends StretchableTile{
return name + " @ ("+x+","+y+")"; return name + " @ ("+x+","+y+")";
} }
@Override
public Train train() {
return train(false);
}
public Train train(boolean last) {
return last ? trains.last() : trains.first();
}
private Fieldset trainList() { private Fieldset trainList() {
Fieldset fieldset = new Fieldset(t("Trains")); Fieldset fieldset = new Fieldset(t("Trains"));
Tag list = new Tag("ul"); Tag list = new Tag("ul");
for (Train t : trains) { for (Train t : parkedTrains) {
if (isSet(t)) t.link("li", t).addTo(list); if (isSet(t)) t.link("li", t).addTo(list);
} }
list.addTo(fieldset); list.addTo(fieldset);
@ -556,7 +486,7 @@ public abstract class Block extends StretchableTile{
} }
public List<Train> trains(){ public List<Train> trains(){
return trains.list(); return parkedTrains.list();
} }
@Override @Override
@ -565,15 +495,14 @@ public abstract class Block extends StretchableTile{
if (params.containsKey(Train.class.getSimpleName())) { if (params.containsKey(Train.class.getSimpleName())) {
Id trainId = Id.from(params,Train.class.getSimpleName()); Id trainId = Id.from(params,Train.class.getSimpleName());
if (trainId.equals(0)) { // remove first train if (trainId.equals(0)) { // remove first train
dropFirstTrain(); free(occupyingTrain());
} else { } else {
Train train = Train.get(trainId); Train newTrain = Train.get(trainId);
if (isSet(train) && train != trains.first()) { if (isSet(newTrain) && newTrain != occupyingTrain()) {
dropFirstTrain(); free(occupyingTrain());
train.dropTrace(); newTrain.dropTrace();
if (connections(train.direction()).isEmpty()) train.heading(null); if (connections(newTrain.direction()).isEmpty()) newTrain.heading(null);
train.set(this); newTrain.set(this);
trains.add(train,train.direction());
} }
} }
} }

17
src/main/java/de/srsoftware/web4rail/tiles/BlockContact.java

@ -4,7 +4,6 @@ import java.io.IOException;
import java.util.Map; import java.util.Map;
import de.srsoftware.tools.Tag; import de.srsoftware.tools.Tag;
import de.srsoftware.web4rail.Route;
import de.srsoftware.web4rail.moving.Train; import de.srsoftware.web4rail.moving.Train;
public class BlockContact extends Contact { public class BlockContact extends Contact {
@ -27,15 +26,25 @@ public class BlockContact extends Contact {
Block block = ((Block)parent()); Block block = ((Block)parent());
return new Id(block.name+":"+block.indexOf(this)); return new Id(block.name+":"+block.indexOf(this));
} }
@Override
public Train lockingTrain() {
return parent().lockingTrain();
}
@Override @Override
public Tag tag(Map<String, Object> replacements) throws IOException { public Tag tag(Map<String, Object> replacements) throws IOException {
return ((Block)parent()).tag(replacements); return parent().tag(replacements);
}
@Override
public Train occupyingTrain() {
return parent().occupyingTrain();
} }
@Override @Override
public Train train() { public Block parent() {
return ((Block)parent()).train(); return (Block) super.parent();
} }
@Override @Override

2
src/main/java/de/srsoftware/web4rail/tiles/Bridge.java

@ -67,7 +67,7 @@ public abstract class Bridge extends Tile {
@Override @Override
public boolean setTrain(Train newTrain) { public boolean setTrain(Train newTrain) {
if (train() == newTrain) return true; if (occupyingTrain() == newTrain) return true;
if (!super.setTrain(newTrain)) return false; if (!super.setTrain(newTrain)) return false;
return isNull(counterpart) ? true : counterpart.setTrain(newTrain); return isNull(counterpart) ? true : counterpart.setTrain(newTrain);
} }

18
src/main/java/de/srsoftware/web4rail/tiles/Contact.java

@ -16,6 +16,7 @@ import org.slf4j.LoggerFactory;
import de.srsoftware.tools.Tag; import de.srsoftware.tools.Tag;
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.actions.Action; import de.srsoftware.web4rail.actions.Action;
import de.srsoftware.web4rail.actions.ActionList; import de.srsoftware.web4rail.actions.ActionList;
import de.srsoftware.web4rail.moving.Train; import de.srsoftware.web4rail.moving.Train;
@ -34,12 +35,8 @@ public class Contact extends Tile{
protected int addr = 0; protected int addr = 0;
private ActionList actions; private ActionList actions;
private OffTimer timer = null; private OffTimer timer = null;
private HashSet<Listener> listeners = new HashSet<Listener>(); private HashSet<EventListener> listeners = new HashSet<EventListener>();
public interface Listener{
public void fired(Object cause);
}
public Contact() { public Contact() {
actions = new ActionList(this); actions = new ActionList(this);
} }
@ -82,15 +79,16 @@ public class Contact extends Tile{
if (isSet(timer)) return; if (isSet(timer)) return;
timer = new OffTimer(); timer = new OffTimer();
} else { } else {
LOG.debug("{} activated.",this);
state = true; state = true;
stream(); stream();
if (isSet(timer)) timer.abort(); if (isSet(timer)) timer.abort();
Train train = train(); Train train = lockingTrain();
Context context = isSet(train) ? train.contact(this) : new Context(this); Context context = isSet(train) ? train.contact(this) : new Context(this);
actions.fire(context,"Contact("+addr+")"); actions.fire(context,"Contact("+addr+")");
for (Listener listener : listeners) listener.fired("Contact("+addr+")"); for (EventListener listener : listeners) listener.fire();
plan.alter();
} }
} }
@ -105,7 +103,7 @@ public class Contact extends Tile{
return this; return this;
} }
public void addListener(Listener l) { public void addListener(EventListener l) {
listeners.add(l); listeners.add(l);
} }
@ -205,7 +203,7 @@ public class Contact extends Tile{
super.removeChild(child); super.removeChild(child);
} }
public void removeListener(Listener listener) { public void removeListener(EventListener listener) {
listeners.remove(listener); listeners.remove(listener);
} }

239
src/main/java/de/srsoftware/web4rail/tiles/Tile.java

@ -22,6 +22,7 @@ import org.slf4j.LoggerFactory;
import de.srsoftware.tools.Tag; import de.srsoftware.tools.Tag;
import de.srsoftware.web4rail.BaseClass; import de.srsoftware.web4rail.BaseClass;
import de.srsoftware.web4rail.Connector; import de.srsoftware.web4rail.Connector;
import de.srsoftware.web4rail.LoadCallback;
import de.srsoftware.web4rail.Plan; import de.srsoftware.web4rail.Plan;
import de.srsoftware.web4rail.Plan.Direction; import de.srsoftware.web4rail.Plan.Direction;
import de.srsoftware.web4rail.Route; import de.srsoftware.web4rail.Route;
@ -39,20 +40,6 @@ import de.srsoftware.web4rail.tags.Window;
* *
*/ */
public abstract class Tile extends BaseClass implements Comparable<Tile> { public abstract class Tile extends BaseClass implements Comparable<Tile> {
public enum Status {
DISABLED("disabled"), FREE("free"), RESERVED("reserved"), 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); protected static Logger LOG = LoggerFactory.getLogger(Tile.class);
private static int DEFAUT_LENGTH = 100; // 10cm private static int DEFAUT_LENGTH = 100; // 10cm
@ -65,11 +52,11 @@ public abstract class Tile extends BaseClass implements Comparable<Tile> {
private static final String Y = "y"; private static final String Y = "y";
private boolean isTrack = true; private boolean isTrack = true;
private boolean disabled;
private int length = DEFAUT_LENGTH; private int length = DEFAUT_LENGTH;
protected Direction oneWay = null; protected Direction oneWay = null;
private TreeSet<Route> routes = new TreeSet<>((r1, r2) -> r1.toString().compareTo(r2.toString())); private TreeSet<Route> routes = new TreeSet<>((r1, r2) -> r1.toString().compareTo(r2.toString()));
private Train train = null; private Train reservingTrain,lockingTrain,occupyingTrain;
protected Status status = Status.FREE;
public Integer x = null; public Integer x = null;
public Integer y = null; public Integer y = null;
@ -81,13 +68,19 @@ public abstract class Tile extends BaseClass implements Comparable<Tile> {
HashSet<String> classes = new HashSet<String>(); HashSet<String> classes = new HashSet<String>();
classes.add("tile"); classes.add("tile");
classes.add(getClass().getSimpleName()); classes.add(getClass().getSimpleName());
if (!is(Status.FREE)) classes.add(status.toString()); if (isSet(reservingTrain)) classes.add(RESERVED);
if (isSet(lockingTrain)) classes.add(LOCKED);
if (isSet(occupyingTrain)) classes.add(OCCUPIED);
return classes; return classes;
} }
public Object click(boolean shift) throws IOException { public Object click(boolean shift) throws IOException {
LOG.debug("{}.click()", getClass().getSimpleName()); LOG.debug("{}.click()", getClass().getSimpleName());
if (isSet(train) && shift) return train.properties(); if (!shift) {
if (isSet(occupyingTrain)) return occupyingTrain.properties();
if (isSet(lockingTrain)) return lockingTrain.properties();
}
return properties(); return properties();
} }
@ -105,10 +98,12 @@ public abstract class Tile extends BaseClass implements Comparable<Tile> {
return new HashMap<>(); return new HashMap<>();
} }
public boolean free(Train t) { public boolean free(Train oldTrain) {
if (isSet(train) && t != train) return false; if (isNull(oldTrain)) return false;
train = null; if (isSet(reservingTrain) && reservingTrain != oldTrain) return false;
status = Status.FREE; if (isSet(lockingTrain) && lockingTrain != oldTrain) return false;
if (isSet(occupyingTrain) && occupyingTrain != oldTrain) return false;
reservingTrain = lockingTrain = occupyingTrain = null;
plan.place(this); plan.place(this);
return true; return true;
} }
@ -134,48 +129,44 @@ public abstract class Tile extends BaseClass implements Comparable<Tile> {
plan.place(tile); plan.place(tile);
} }
public boolean is(Status... states) { public boolean isDisabled() {
for (Status s : states) { return disabled;
if (status == s) return true;
}
return false;
} }
public boolean isFreeFor(Context newTrain) { public boolean isFreeFor(Context newTrain) {
LOG.debug("{}.isFreeFor({})", this, newTrain); LOG.debug("{}.isFreeFor({})", this, newTrain);
if (is(Status.DISABLED)) { if (isDisabled()) {
LOG.debug("{} is disabled!", this); LOG.debug("{} is disabled!", this);
return false; return false;
} }
if (isNull(train)) { Train train = newTrain.train();
LOG.debug("→ free");
return true; if (isSet(reservingTrain) && reservingTrain != train) {
LOG.debug("{} is reserved for {}",reservingTrain);
return false; // nicht in reservierten Block einfahren!
} }
if (newTrain.train() == train) { // during train.reserveNext, we may encounter, parts, that are already reserved if (isSet(lockingTrain) && lockingTrain != train) {
// by the respective train, but having another route. do not compare routes LOG.debug("{} is locked for {}",lockingTrain);
// in that case! return false; // nicht in reservierten Block einfahren!
LOG.debug("already reserved by {} → true", train);
return true;
} }
if (isSet(newTrain.train()) && newTrain.train().isShunting()) { if (isSet(occupyingTrain) && occupyingTrain != train) {
LOG.debug("occupied by {}. Allowed for shunting {}", train, newTrain.train()); LOG.debug("{} is occupied by {}",occupyingTrain);
return true; return train.isShunting(); // nur in belegte Blöcke einfahren, wenn Rangiermodus aktiv!
} }
LOG.debug("occupied by {} → false", train); return true;
return false;
} }
public JSONObject json() { public JSONObject json() {
JSONObject json = super.json(); JSONObject json = super.json();
json.put(TYPE, getClass().getSimpleName()); json.put(TYPE, getClass().getSimpleName());
if (isSet(x) && isSet(y)) json.put(POS, new JSONObject(Map.of(X, x, Y, y))); if (isSet(x) && isSet(y)) json.put(POS, new JSONObject(Map.of(X, x, Y, y)));
if (isSet(oneWay)) json.put(ONEW_WAY, oneWay); if (isSet(oneWay)) json.put(ONEW_WAY, oneWay);
if (is(Status.DISABLED)) json.put(DISABLED, true); if (disabled) json.put(DISABLED, true);
if (isSet(train)) json.put(REALM_TRAIN, train.id()); if (isSet(occupyingTrain)) json.put(REALM_TRAIN, occupyingTrain.id());
json.put(LENGTH, length); json.put(LENGTH, length);
return json; return json;
} }
@ -221,19 +212,53 @@ public abstract class Tile extends BaseClass implements Comparable<Tile> {
} }
public Tile load(JSONObject json) { public Tile load(JSONObject json) {
if (json.has(ID)) json.remove(ID); // id should be created from cordinates if (json.has(ID)) json.remove(ID); // id should be created from coordinates
super.load(json); super.load(json);
if (json.has(POS)) { if (json.has(POS)) {
JSONObject pos = json.getJSONObject(POS); JSONObject pos = json.getJSONObject(POS);
x = pos.getInt(X); x = pos.getInt(X);
y = pos.getInt(Y); y = pos.getInt(Y);
} }
if (json.has(DISABLED) && json.getBoolean(DISABLED)) status = Status.DISABLED; if (json.has(DISABLED)) disabled = json.getBoolean(DISABLED);
if (json.has(LENGTH)) length = json.getInt(LENGTH); if (json.has(LENGTH)) length = json.getInt(LENGTH);
if (json.has(ONEW_WAY)) oneWay = Direction.valueOf(json.getString(ONEW_WAY)); if (json.has(ONEW_WAY)) oneWay = Direction.valueOf(json.getString(ONEW_WAY));
if (json.has(REALM_TRAIN))
new LoadCallback() {
@Override
public void afterLoad() {
occupyingTrain = Train.get(new Id(json.getString(REALM_TRAIN)));
}
};
return this; return this;
} }
public boolean lockFor(Context context,boolean downgrade) {
Train newTrain = context.train();
LOG.debug("{}.lockFor({})",this,newTrain);
if (isNull(newTrain)) return false;
if (isSet(reservingTrain) && reservingTrain != newTrain) return debug("{} already reserved for {}",this,reservingTrain);
if (isSet(lockingTrain)) {
if (lockingTrain != newTrain) return debug("{} already locked by {}",this,lockingTrain);
return true; // already locked!
}
if (isSet(occupyingTrain)) {
if (occupyingTrain != newTrain && !newTrain.isShunting()) return debug("{} already occupied by {}",this,occupyingTrain);
lockingTrain = newTrain;
if (!downgrade) return true;
}
lockingTrain = newTrain;
reservingTrain = occupyingTrain = null;
plan.place(this);
return true;
}
public Train lockingTrain() {
if (isSet(lockingTrain)) return lockingTrain;
if (isSet(occupyingTrain)) return occupyingTrain;
return null;
}
public boolean move(int dx, int dy) { public boolean move(int dx, int dy) {
int destX = x + (dx > 0 ? width() : dx); int destX = x + (dx > 0 ? width() : dx);
int destY = y + (dy > 0 ? height() : dy); int destY = y + (dy > 0 ? height() : dy);
@ -250,6 +275,10 @@ public abstract class Tile extends BaseClass implements Comparable<Tile> {
protected void noTrack() { protected void noTrack() {
isTrack = false; isTrack = false;
} }
public Train occupyingTrain() {
return occupyingTrain;
}
public Tile position(int x, int y) { public Tile position(int x, int y) {
this.x = x; this.x = x;
@ -266,18 +295,18 @@ public abstract class Tile extends BaseClass implements Comparable<Tile> {
String... errors) { String... errors) {
Fieldset fieldset = null; Fieldset fieldset = null;
if (isSet(train)) { if (isSet(occupyingTrain)) {
fieldset = new Fieldset(t("Train")); fieldset = new Fieldset(t("Train"));
train.link("span", t("Train") + ":" + NBSP + train + NBSP).addTo(fieldset); occupyingTrain.link("span", t("Train") + ":" + NBSP + occupyingTrain + NBSP).addTo(fieldset);
if (isSet(train.route())) { if (isSet(occupyingTrain.route())) {
train.button(t("stop"), Map.of(ACTION, ACTION_STOP)).addTo(fieldset); occupyingTrain.button(t("stop"), Map.of(ACTION, ACTION_STOP)).addTo(fieldset);
} else { } else {
train.button(t("depart"), Map.of(ACTION, ACTION_START)).addTo(fieldset); occupyingTrain.button(t("depart"), Map.of(ACTION, ACTION_START)).addTo(fieldset);
} }
if (train.usesAutopilot()) { if (occupyingTrain.usesAutopilot()) {
train.button(t("quit autopilot"), Map.of(ACTION, ACTION_QUIT)).addTo(fieldset); occupyingTrain.button(t("quit autopilot"), Map.of(ACTION, ACTION_QUIT)).addTo(fieldset);
} else { } else {
train.button(t("auto"), Map.of(ACTION, ACTION_AUTO)).addTo(fieldset); occupyingTrain.button(t("auto"), Map.of(ACTION, ACTION_AUTO)).addTo(fieldset);
} }
} }
@ -286,8 +315,8 @@ public abstract class Tile extends BaseClass implements Comparable<Tile> {
if (isTrack) { if (isTrack) {
formInputs.add(t("Length"), formInputs.add(t("Length"),
new Input(LENGTH, length).numeric().addTo(new Tag("span")).content(NBSP + lengthUnit)); new Input(LENGTH, length).numeric().addTo(new Tag("span")).content(NBSP + lengthUnit));
Checkbox checkbox = new Checkbox(DISABLED, t("disabled"), is(Status.DISABLED)); Checkbox checkbox = new Checkbox(DISABLED, t("disabled"), disabled);
if (is(Status.DISABLED)) checkbox.clazz("disabled"); if (disabled) checkbox.clazz("disabled");
formInputs.add(t("State"), checkbox); formInputs.add(t("State"), checkbox);
} }
@ -373,21 +402,15 @@ public abstract class Tile extends BaseClass implements Comparable<Tile> {
} }
public boolean setTrain(Train newTrain) { public boolean setTrain(Train newTrain) {
if (isNull(newTrain)) return false; if (disabled) return false;
if (isSet(train) && newTrain != train) return false; if (isNull(newTrain)) return false;
switch (status) { // bisheriger Status if (isSet(reservingTrain) && newTrain != reservingTrain) return false;
case DISABLED: if (isSet(lockingTrain) && newTrain != lockingTrain) return false;
return false; if (isSet(occupyingTrain) && newTrain != occupyingTrain) return false;
case FREE: if (occupyingTrain == newTrain) return true;
case RESERVED: occupyingTrain = newTrain;
case LOCKED: reservingTrain = lockingTrain = null;
train = newTrain; plan.place(this);
status = Status.OCCUPIED;
plan.place(this);
break;
case OCCUPIED:
break;
}
return true; return true;
} }
@ -456,10 +479,6 @@ public abstract class Tile extends BaseClass implements Comparable<Tile> {
return t("{}({},{})", getClass().getSimpleName(), x, y); return t("{}({},{})", getClass().getSimpleName(), x, y);
} }
public Train train() {
return train;
}
@Override @Override
public BaseClass remove() { public BaseClass remove() {
while (!routes.isEmpty()) routes.first().remove(); while (!routes.isEmpty()) routes.first().remove();
@ -473,61 +492,40 @@ public abstract class Tile extends BaseClass implements Comparable<Tile> {
LOG.debug("Removing {} from {}", childAsString, this); LOG.debug("Removing {} from {}", childAsString, this);
if (child instanceof Route) routes.remove(child); if (child instanceof Route) routes.remove(child);
if (child == train) train = null; if (child == reservingTrain) reservingTrain = null;
if (child == lockingTrain) lockingTrain = null;
if (child == occupyingTrain) occupyingTrain = null;
super.removeChild(child); super.removeChild(child);
plan.place(this); plan.place(this);
} }
public boolean lockFor(Context context,boolean downgrade) {
Train newTrain = context.train();
LOG.debug("{}.lockFor({})",this,newTrain);
if (isNull(newTrain)) return false;
if (isSet(train) && train != newTrain) return false;
switch (status) {
case DISABLED:
return false;
case OCCUPIED:
if (!downgrade) break;
case FREE:
case RESERVED:
status = Status.LOCKED;
plan.place(this);
break;
case LOCKED:
break; // already locked
}
return true;
}
public boolean reserveFor(Context context) { public boolean reserveFor(Context context) {
Train newTrain = context.train(); Train newTrain = context.train();
LOG.debug("{}.reserverFor({})",this,newTrain); LOG.debug("{}.reserverFor({})",this,newTrain);
if (isNull(newTrain)) return false; if (isNull(newTrain)) return false;
if (isSet(train) && train != newTrain) return false; if (isSet(reservingTrain)) {
switch (status) { if (reservingTrain != newTrain) return debug("{} already reserved for {}",this,reservingTrain);
case DISABLED: return true; // already reserved for newTrain
return false; }
case FREE: if (isSet(lockingTrain)) {
status = Status.RESERVED; if (lockingTrain != newTrain) return debug("{} already locked by {}",this,lockingTrain);
train = newTrain; return true; // do not downgrade!
plan.place(this);
break;
case OCCUPIED:
case LOCKED:
case RESERVED:
break; // do not downgrade
} }
if (isSet(occupyingTrain)) {
if (occupyingTrain != newTrain && !newTrain.isShunting()) return debug("{} already occupied by {}",this,occupyingTrain);
return true; // do not downgrade!
}
reservingTrain = newTrain;
plan.place(this);
return true; return true;
} }
public void setEnabled(boolean enabled) { public void setEnabled(boolean enabled) {
if (!enabled) { boolean show = (disabled == enabled);
status = Status.DISABLED; disabled = !enabled;
} else if (is(Status.DISABLED)) { // Status nur ändern, wenn er bisher DISABLED war if (show) plan.place(this);
status = isNull(train) ? Status.FREE : Status.OCCUPIED;
}
plan.place(this);
} }
public Tile update(HashMap<String, String> params) { public Tile update(HashMap<String, String> params) {
@ -540,11 +538,8 @@ public abstract class Tile extends BaseClass implements Comparable<Tile> {
oneWay = null; oneWay = null;
} }
} }
if ("on".equals(params.get(DISABLED))) { disabled = "on".equals(params.get(DISABLED));
status = Status.DISABLED;
} else {
status = isSet(train) ? Status.OCCUPIED : Status.FREE;
}
String len = params.get(LENGTH); String len = params.get(LENGTH);
if (isSet(len)) length(Integer.parseInt(len)); if (isSet(len)) length(Integer.parseInt(len));
super.update(params); super.update(params);

4
src/main/java/de/srsoftware/web4rail/tiles/Turnout.java

@ -15,6 +15,7 @@ import de.srsoftware.web4rail.Command;
import de.srsoftware.web4rail.Command.Reply; import de.srsoftware.web4rail.Command.Reply;
import de.srsoftware.web4rail.Device; import de.srsoftware.web4rail.Device;
import de.srsoftware.web4rail.Protocol; import de.srsoftware.web4rail.Protocol;
import de.srsoftware.web4rail.moving.Train;
import de.srsoftware.web4rail.tags.Fieldset; import de.srsoftware.web4rail.tags.Fieldset;
import de.srsoftware.web4rail.tags.Input; import de.srsoftware.web4rail.tags.Input;
import de.srsoftware.web4rail.tags.Radio; import de.srsoftware.web4rail.tags.Radio;
@ -159,7 +160,8 @@ public abstract class Turnout extends Tile implements Device{
} }
public Reply state(State newState) { public Reply state(State newState) {
if (is(Status.LOCKED,Status.OCCUPIED) && newState != state) return new Reply(415, t("{} locked by {}!",this,train())); Train lockingTrain = lockingTrain();
if (isSet(lockingTrain) && newState != state) return new Reply(415, t("{} locked by {}!",this,lockingTrain));
if (address == 0) { if (address == 0) {
sleep(300); sleep(300);
state = newState; state = newState;

8
src/main/java/de/srsoftware/web4rail/tiles/TurnoutL.java

@ -4,7 +4,6 @@ import java.io.IOException;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import de.srsoftware.web4rail.moving.Train;
import de.srsoftware.web4rail.tags.Fieldset; import de.srsoftware.web4rail.tags.Fieldset;
import de.srsoftware.web4rail.tags.Input; import de.srsoftware.web4rail.tags.Input;
import de.srsoftware.web4rail.tags.Window; import de.srsoftware.web4rail.tags.Window;
@ -16,12 +15,7 @@ public abstract class TurnoutL extends Turnout {
@Override @Override
public Object click(boolean shift) throws IOException { public Object click(boolean shift) throws IOException {
Object o = super.click(shift); Object o = super.click(shift);
if (!shift) { if (!shift) state(state == State.STRAIGHT ? State.LEFT : State.STRAIGHT);
Train train = train();
if (isSet(train)) {
plan.stream(t("{} is locked by {}!",this,train));
} else state(state == State.STRAIGHT ? State.LEFT : State.STRAIGHT);
}
return o; return o;
} }

8
src/main/java/de/srsoftware/web4rail/tiles/TurnoutR.java

@ -4,7 +4,6 @@ import java.io.IOException;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import de.srsoftware.web4rail.moving.Train;
import de.srsoftware.web4rail.tags.Fieldset; import de.srsoftware.web4rail.tags.Fieldset;
import de.srsoftware.web4rail.tags.Input; import de.srsoftware.web4rail.tags.Input;
import de.srsoftware.web4rail.tags.Window; import de.srsoftware.web4rail.tags.Window;
@ -16,12 +15,7 @@ public abstract class TurnoutR extends Turnout {
@Override @Override
public Object click(boolean shift) throws IOException { public Object click(boolean shift) throws IOException {
Object o = super.click(shift); Object o = super.click(shift);
if (!shift) { if (!shift) state(state == State.STRAIGHT ? State.RIGHT : State.STRAIGHT);
Train train = train();
if (isSet(train)) {
plan.stream(t("{} is locked by {}!",this,train));
} else state(state == State.STRAIGHT ? State.RIGHT : State.STRAIGHT);
}
return o; return o;
} }

Loading…
Cancel
Save