diff --git a/pom.xml b/pom.xml index 90bb238..e4a95b4 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 de.srsoftware web4rail - 1.2.26 + 1.2.27 Web4Rail jar Java Model Railway Control diff --git a/src/main/java/de/srsoftware/web4rail/BaseClass.java b/src/main/java/de/srsoftware/web4rail/BaseClass.java index 6a1dc71..46fc742 100644 --- a/src/main/java/de/srsoftware/web4rail/BaseClass.java +++ b/src/main/java/de/srsoftware/web4rail/BaseClass.java @@ -87,17 +87,30 @@ public abstract class BaseClass implements Constants{ return car; } + public void clear() { + action = null; + block = null; + car = null; + condition = null; + contact = null; + direction = null; + main = null; + route = null; + tile = null; + train = null; + } + public Context clone() { Context clone = new Context(main); - clone.tile = tile; - clone.block = block; - clone.train = train; - clone.route = route; clone.action = action; - clone.condition = condition; + clone.block = block; clone.car = car; + clone.condition = condition; clone.contact = contact; clone.direction = direction; + clone.route = route; + clone.tile = tile; + clone.train = train; return clone; } @@ -109,6 +122,11 @@ public abstract class BaseClass implements Constants{ return contact; } + public void contact(Contact newContact) { + contact = newContact; + } + + public Direction direction() { return direction; } @@ -117,6 +135,11 @@ public abstract class BaseClass implements Constants{ direction = newDirection; return this; } + + public boolean invalidated() { + return isNull(main); + } + public Route route() { return route; @@ -376,11 +399,11 @@ public abstract class BaseClass implements Constants{ } public String realm() { - if (this instanceof Tile) return REALM_PLAN; if (this instanceof Contact) return REALM_CONTACT; + if (this instanceof Tile) return REALM_PLAN; - if (this instanceof Car) return REALM_CAR; if (this instanceof Locomotive) return REALM_LOCO; + if (this instanceof Car) return REALM_CAR; if (this instanceof Action) return REALM_ACTIONS; if (this instanceof Condition) return REALM_CONDITION; diff --git a/src/main/java/de/srsoftware/web4rail/Plan.java b/src/main/java/de/srsoftware/web4rail/Plan.java index 357bb51..7eea4ff 100644 --- a/src/main/java/de/srsoftware/web4rail/Plan.java +++ b/src/main/java/de/srsoftware/web4rail/Plan.java @@ -251,9 +251,6 @@ public class Plan extends BaseClass{ List oldRoutes = BaseClass.listElements(Route.class); Vector newRoutes = new Vector(); for (Block block : BaseClass.listElements(Block.class)) { - if (block.name.equals("Huhu")) { - System.err.println("Here we go!"); - } for (Connector con : block.startPoints()) { newRoutes.addAll(follow(new Route().begin(block,con.from.inverse()),con)); } diff --git a/src/main/java/de/srsoftware/web4rail/Route.java b/src/main/java/de/srsoftware/web4rail/Route.java index c942196..0839373 100644 --- a/src/main/java/de/srsoftware/web4rail/Route.java +++ b/src/main/java/de/srsoftware/web4rail/Route.java @@ -48,7 +48,7 @@ import de.srsoftware.web4rail.tiles.Turnout; * @author Stephan Richter, SRSoftware * */ -public class Route extends BaseClass implements Comparable{ +public class Route extends BaseClass { public enum State { FREE, LOCKED, PREPARED, STARTED; @@ -106,7 +106,6 @@ public class Route extends BaseClass implements Comparable{ public void finish() { long timestamp2 = new Date().getTime(); //int remainingSpeed = train.speed; - train.setSpeed(0); if (aborted) return; long runtime = timestamp2 - timestamp; int quotient = startSpeed - ENDSPEED; @@ -144,6 +143,7 @@ public class Route extends BaseClass implements Comparable{ private HashMap brakeTimes = new HashMap(); private ConditionList conditions; private Vector contacts; + private Context context; // this context is passed to actions private boolean disabled = false; private Block endBlock = null; public Direction endDirection; @@ -222,71 +222,6 @@ public class Route extends BaseClass implements Comparable{ conditions.add(condition); } - private Fieldset basicProperties() { - Fieldset fieldset = new Fieldset(t("Route properties")); - - if (isSet(train)) train.link("span",t("Train: {}",train)).addTo(fieldset); - Tag list = new Tag("ul"); - Plan.addLink(startBlock, t("Origin: {} to {}",startBlock.name,startDirection), list); - Plan.addLink(endBlock, t("Destination: {} from {}",endBlock.name,endDirection.inverse()), list); - list.addTo(fieldset); - - if (!signals.isEmpty()) { - new Tag("h4").content(t("Signals")).addTo(fieldset); - list = new Tag("ul"); - for (Signal s : signals) Plan.addLink(s,s.toString(),list); - list.addTo(fieldset); - } - return fieldset; - } - - private Fieldset brakeTimes() { - Fieldset fieldset = new Fieldset(t("Brake time table")); - Table table = new Table(); - table.addHead(t("Train"),t("Brake time¹, forward"),t("Brake time¹, reverse")); - for (Train t : Train.list()) { - Integer fTime = brakeTimes.get(t.brakeId()); - Integer rTime = brakeTimes.get(t.brakeId(true)); - table.addRow(t,isSet(fTime)? fTime+NBSP+"ms" : "–",isSet(rTime)? fTime+NBSP+"ms" : "–"); - } - table.clazz("brake-times").addTo(fieldset); - new Tag("p").content(t("1) Duration between 5 {} steps during brake process.",speedUnit)).addTo(fieldset); - return fieldset; - } - - private Fieldset contactsAndActions() { - Fieldset win = new Fieldset(t("Actions and contacts")); - Tag list = new Tag("ol"); - - Tag setup = new Tag("li").content(t("Setup actions")+NBSP); - ActionList setupActions = triggeredActions.get(ROUTE_SETUP); - if (isNull(setupActions)) { - setupActions = new ActionList(this); - triggeredActions.put(ROUTE_SETUP, setupActions); - } - setupActions.list().addTo(setup).addTo(list); - - Tag start = new Tag("li").content(t("Start actions")+NBSP); - ActionList startActions = triggeredActions.get(ROUTE_START); - if (isNull(startActions)) { - startActions = new ActionList(this); - triggeredActions.put(ROUTE_START, startActions); - } - startActions.list().addTo(start).addTo(list); - - for (Contact c : contacts) { - Tag item = c.link("span", c).addTo(new Tag("li")).content(NBSP); - ActionList actions = triggeredActions.get(c.trigger()); - if (isNull(actions)) { - actions = new ActionList(this); - triggeredActions.put(c.trigger(), actions); - } - actions.list().addTo(item).addTo(list); - } - list.addTo(win); - return win; - } - public void addPropertiesFrom(Route existingRoute) { LOG.debug("addPropertiesFrom({})",existingRoute); disabled = existingRoute.disabled; @@ -302,8 +237,8 @@ public class Route extends BaseClass implements Comparable{ ActionList existingActionList = existingRoute.triggeredActions.get(trigger); if (isSet(existingActionList)) { LOG.debug("found action list for {} on existing route {}: {}",trigger,existingRoute,existingActionList); - ActionList newActionList = entry.getValue(); - newActionList.addActionsFrom(existingActionList); + existingActionList.forEach(action -> LOG.debug("OLD Action: {}",action)); + entry.getValue().merge(existingActionList); } } brakeTimes = new HashMap(existingRoute.brakeTimes); @@ -316,17 +251,6 @@ public class Route extends BaseClass implements Comparable{ void addTurnout(Turnout t, Turnout.State s) { turnouts.put(t, s); } - - private Fieldset turnouts() { - Fieldset win = new Fieldset(t("Turnouts")); - Tag list = new Tag("ul"); - for (Entry entry : turnouts.entrySet()) { - Turnout turnout = entry.getKey(); - Plan.addLink(turnout, turnout+": "+t(entry.getValue().toString()), list); - } - list.addTo(win); - return win; - } /** * checks, whether the route may be used in a given context @@ -338,6 +262,38 @@ public class Route extends BaseClass implements Comparable{ return conditions.fulfilledBy(context); } + private Fieldset basicProperties() { + Fieldset fieldset = new Fieldset(t("Route properties")); + + if (isSet(train)) train.link("span",t("Train: {}",train)).addTo(fieldset); + Tag list = new Tag("ul"); + Plan.addLink(startBlock, t("Origin: {} to {}",startBlock.name,startDirection), list); + Plan.addLink(endBlock, t("Destination: {} from {}",endBlock.name,endDirection.inverse()), list); + list.addTo(fieldset); + + if (!signals.isEmpty()) { + new Tag("h4").content(t("Signals")).addTo(fieldset); + list = new Tag("ul"); + for (Signal s : signals) Plan.addLink(s,s.toString(),list); + list.addTo(fieldset); + } + return fieldset; + } + + private Fieldset brakeTimes() { + Fieldset fieldset = new Fieldset(t("Brake time table")); + Table table = new Table(); + table.addHead(t("Train"),t("Brake time¹, forward"),t("Brake time¹, reverse")); + for (Train t : Train.list()) { + Integer fTime = brakeTimes.get(t.brakeId()); + Integer rTime = brakeTimes.get(t.brakeId(true)); + table.addRow(t,isSet(fTime)? fTime+NBSP+"ms" : "–",isSet(rTime)? fTime+NBSP+"ms" : "–"); + } + table.clazz("brake-times").addTo(fieldset); + new Tag("p").content(t("1) Duration between 5 {} steps during brake process.",speedUnit)).addTo(fieldset); + return fieldset; + } + public Route begin(Block block,Direction to) { // add those fields to clone, too! contacts = new Vector(); @@ -360,6 +316,7 @@ public class Route extends BaseClass implements Comparable{ } public void brakeStop() { + train.setSpeed(0); if (isSet(brakeProcessor)) brakeProcessor.finish(); } @@ -376,11 +333,6 @@ public class Route extends BaseClass implements Comparable{ clone.brakeTimes = new HashMap(brakeTimes); return clone; } - - @Override - public int compareTo(Route other) { - return name().compareTo(other.name()); - } public Route complete() { if (contacts.size()>1) { // mindestens 2 Kontakte: erster Kontakt aktiviert Block, vorletzter Kontakt leitet Bremsung ein @@ -411,8 +363,9 @@ public class Route extends BaseClass implements Comparable{ LOG.debug("{} on {} activated {}.",train,this,contact); traceTrainFrom(contact); ActionList actions = triggeredActions.get(contact.trigger()); + LOG.debug("Contact has id {} / trigger {} and is assigned with {}",contact.id(),contact.trigger(),actions); if (isNull(actions)) return; - Context context = new Context(contact).route(this).train(train); + context.contact(contact); actions.fire(context); } @@ -420,12 +373,37 @@ public class Route extends BaseClass implements Comparable{ return new Vector<>(contacts); } - public String context() { - return REALM_ROUTE+":"+id(); - } - - public boolean isDisabled() { - return disabled; + private Fieldset contactsAndActions() { + Fieldset win = new Fieldset(t("Actions and contacts")); + Tag list = new Tag("ol"); + + Tag setup = new Tag("li").content(t("Setup actions")+NBSP); + ActionList setupActions = triggeredActions.get(ROUTE_SETUP); + if (isNull(setupActions)) { + setupActions = new ActionList(this); + triggeredActions.put(ROUTE_SETUP, setupActions); + } + setupActions.list().addTo(setup).addTo(list); + + Tag start = new Tag("li").content(t("Start actions")+NBSP); + ActionList startActions = triggeredActions.get(ROUTE_START); + if (isNull(startActions)) { + startActions = new ActionList(this); + triggeredActions.put(ROUTE_START, startActions); + } + startActions.list().addTo(start).addTo(list); + + for (Contact c : contacts) { + Tag item = c.link("span", c).addTo(new Tag("li")).content(NBSP); + ActionList actions = triggeredActions.get(c.trigger()); + if (isNull(actions)) { + actions = new ActionList(this); + triggeredActions.put(c.trigger(), actions); + } + actions.list().addTo(item).addTo(list); + } + list.addTo(win); + return win; } public Block endBlock() { @@ -433,6 +411,7 @@ public class Route extends BaseClass implements Comparable{ } public void finish() { + context.clear(); // prevent delayed actions from firing after route has finished setSignals(Signal.STOP); for (Tile tile : path) tile.setRoute(null); Tile lastTile = path.lastElement(); @@ -455,7 +434,7 @@ public class Route extends BaseClass implements Comparable{ triggeredContacts.clear(); } - public boolean fireSetupActions(Context context) { + public boolean fireSetupActions() { ActionList setupActions = triggeredActions.get(ROUTE_SETUP); if (isSet(setupActions) && !setupActions.fire(context)) return false; state = State.PREPARED; @@ -481,6 +460,10 @@ public class Route extends BaseClass implements Comparable{ if (isNull(id)) id = new Id(""+(generateName().hashCode())); return id; } + + public boolean isDisabled() { + return disabled; + } public boolean isFreeFor(Train newTrain) { for (int i=1; i{ LOG.debug("Removing route ({}) {}",id(),this); if (isSet(train)) train.removeChild(this); for (Tile tile : path) { - if (tile.id().equals(Tile.id(4, 6))) { - System.err.println(tile); - } tile.removeChild(this); } conditions.remove(); @@ -823,6 +803,12 @@ public class Route extends BaseClass implements Comparable{ file.write("]}"); file.close(); } + + public Context set(Context newContext) { + context = newContext; + context.route(this); + return context; + } public void setLast(Turnout.State state) { if (isNull(state) || state == Turnout.State.UNDEF) return; @@ -865,7 +851,7 @@ public class Route extends BaseClass implements Comparable{ if (newTrain != train) return false; // can't alter route's train } else train = newTrain; // set new train ActionList startActions = triggeredActions.get(ROUTE_START); - if (isSet(startActions) && !startActions.fire(new Context(this).train(train))) return false; // start actions failed + if (isSet(startActions) && !startActions.fire(context)) return false; // start actions failed state = State.STARTED; return true; } @@ -896,6 +882,17 @@ public class Route extends BaseClass implements Comparable{ return train; } + private Fieldset turnouts() { + Fieldset win = new Fieldset(t("Turnouts")); + Tag list = new Tag("ul"); + for (Entry entry : turnouts.entrySet()) { + Turnout turnout = entry.getKey(); + Plan.addLink(turnout, turnout+": "+t(entry.getValue().toString()), list); + } + list.addTo(win); + return win; + } + public Route unlock() throws IOException { // TODO return this; diff --git a/src/main/java/de/srsoftware/web4rail/actions/Action.java b/src/main/java/de/srsoftware/web4rail/actions/Action.java index 777df7b..3c57f49 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/Action.java +++ b/src/main/java/de/srsoftware/web4rail/actions/Action.java @@ -81,7 +81,7 @@ public abstract class Action extends BaseClass { return null; } - public boolean equals(Action other) { + public boolean corresponsTo(Action other) { return this.toString().equals(other.toString()); } diff --git a/src/main/java/de/srsoftware/web4rail/actions/ActionList.java b/src/main/java/de/srsoftware/web4rail/actions/ActionList.java index 0e7122a..163e9df 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/ActionList.java +++ b/src/main/java/de/srsoftware/web4rail/actions/ActionList.java @@ -58,30 +58,6 @@ public class ActionList extends Action implements Iterable{ return new Tag("span").content(t("Unknown action type: {}",type)).addTo(actionTypeForm()); } - public void addActionsFrom(ActionList other) { - for (Action otherAction : other.actions) { - //LOG.debug("old action ({}): {}",otherAction.getClass().getSimpleName(),otherAction); - boolean exists = false; - int len = actions.size(); - for (int i=0; i other.removeChild(action)); // zugewiesene Aktionen von alter Liste löschen - } - public void clear() { while (!actions.isEmpty()) actions.firstElement().remove(); } @@ -91,6 +67,10 @@ public class ActionList extends Action implements Iterable{ } public boolean fire(Context context) { + if (context.invalidated()) { + LOG.debug("Context has been invalidated, aborting {}",this); + return false; + } if (!isEmpty()) LOG.debug(t("Firing {}"),actions); for (Action action : actions) { if (!action.fire(context)) return false; @@ -151,6 +131,20 @@ public class ActionList extends Action implements Iterable{ return this; } + public void merge(ActionList oldActions) { + for (Action oldAction : oldActions.actions) { + for (Action newAction : actions) { + if (oldAction.corresponsTo(newAction)) { + actions.remove(newAction); + LOG.debug("new action {} replaced by {}",newAction,oldAction); + break; + } + } + add(oldAction); + } + oldActions.actions.clear(); + } + public boolean moveUp(Action action) { if (isNull(action)) return false; if (actions.firstElement() == action && parent() instanceof ActionList) { diff --git a/src/main/java/de/srsoftware/web4rail/actions/PreserveRoute.java b/src/main/java/de/srsoftware/web4rail/actions/PreserveRoute.java index f9cd23c..0161d50 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/PreserveRoute.java +++ b/src/main/java/de/srsoftware/web4rail/actions/PreserveRoute.java @@ -19,13 +19,14 @@ public class PreserveRoute extends Action { if (isNull(train)) return false; if (isNull(route)) return false; - Range waitTime = route.endBlock().getWaitTime(train,route.endDirection); // These are NOT errors: if (!train.usesAutopilot()) return true; - if (waitTime.max > 0) return true; // train is expected to wait in next block. if (train.destination() == route.endBlock()) return true; + Range waitTime = route.endBlock().getWaitTime(train,route.endDirection); + if (waitTime.max > 0) return true; // train is expected to wait in next block. + train.reserveNext(); return true; } diff --git a/src/main/java/de/srsoftware/web4rail/actions/SetRelay.java b/src/main/java/de/srsoftware/web4rail/actions/SetRelay.java index f36945d..f943280 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/SetRelay.java +++ b/src/main/java/de/srsoftware/web4rail/actions/SetRelay.java @@ -75,10 +75,10 @@ public class SetRelay extends Action { @Override protected Object update(HashMap params) { LOG.debug("update: {}",params); - Id relayId = new Id(params.get(RELAY)); + Id relayId = new Id(params.get(Relay.class.getSimpleName())); relay = Relay.get(relayId); String st = params.get(Relay.STATE); if (isSet(st)) state = st.equals("true"); - return properties(); + return context().properties(); } } diff --git a/src/main/java/de/srsoftware/web4rail/actions/SetSpeed.java b/src/main/java/de/srsoftware/web4rail/actions/SetSpeed.java index 35ab810..143d2af 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/SetSpeed.java +++ b/src/main/java/de/srsoftware/web4rail/actions/SetSpeed.java @@ -19,6 +19,11 @@ public class SetSpeed extends Action{ public static final String MAX_SPEED = "max_speed"; private int speed = 0; + @Override + public boolean corresponsTo(Action other) { + return other instanceof SetSpeed; + } + @Override public boolean fire(Context context) { if (isNull(context.train())) return false; @@ -66,10 +71,7 @@ public class SetSpeed extends Action{ try { int s = Integer.parseInt(ms); if (s<0) error = t("Speed must not be less than zero!"); - if (error == null) { - this.speed = s; - return t("Action updated!"); - } + if (isNull(error)) speed = s; } catch (NumberFormatException e) { error = t("Not a valid number!"); } diff --git a/src/main/java/de/srsoftware/web4rail/moving/Train.java b/src/main/java/de/srsoftware/web4rail/moving/Train.java index 4b5932c..2db9306 100644 --- a/src/main/java/de/srsoftware/web4rail/moving/Train.java +++ b/src/main/java/de/srsoftware/web4rail/moving/Train.java @@ -523,7 +523,7 @@ public class Train extends BaseClass implements Comparable { } dest.addTo(propList); - if (isSet(route)) link("li", route).addTo(propList); + if (isSet(route)) route.link("li", route).addTo(propList); int ms = maxSpeed(); if (ms < Integer.MAX_VALUE) new Tag("li").content(t("Max. Speed")+": "+maxSpeed()+NBSP+speedUnit).addTo(propList); @@ -583,10 +583,10 @@ public class Train extends BaseClass implements Comparable { Context context = new Context(this).route(route).block(route.endBlock()).direction(route.endDirection); Route nextRoute = PathFinder.chooseRoute(context); if (isNull(nextRoute)) return; - + nextRoute.set(context); boolean error = !nextRoute.lockIgnoring(route); error = error || !nextRoute.setTurnouts(); - error = error || !nextRoute.fireSetupActions(context); + error = error || !nextRoute.fireSetupActions(); if (error) { nextRoute.reset(); // may unlock tiles belonging to the current route. @@ -686,23 +686,26 @@ public class Train extends BaseClass implements Comparable { return properties(); } - public String start() throws IOException { + public Object start() throws IOException { if (isNull(currentBlock)) return t("{} not in a block",this); if (maxSpeed() == 0) return t("Train has maximum speed of 0 {}, cannot go!",speedUnit); if (isSet(route)) route.reset(); // reset route previously chosen - Context context = new Context(this).block(currentBlock).direction(direction); String error = null; if (isSet(nextRoute)) { route = nextRoute; if (!route.lock()) return t("Was not able to lock {}",route); nextRoute = null; + route.set(new Context(this).block(currentBlock).direction(direction)); } else { + Context context = new Context(this).block(currentBlock).direction(direction); route = PathFinder.chooseRoute(context); if (isNull(route)) return t("No free routes from {}",currentBlock); if (!route.lock()) return t("Was not able to lock {}",route); if (!route.setTurnouts()) error = t("Was not able to set all turnouts!"); - if (isNull(error) && !route.fireSetupActions(context.route(route))) error = t("Was not able to fire all setup actions of route!"); + context.train(this); + route.set(context); + if (isNull(error) && !route.fireSetupActions()) error = t("Was not able to fire all setup actions of route!"); } if (direction != route.startDirection) turn(); @@ -713,7 +716,9 @@ public class Train extends BaseClass implements Comparable { return error; } startSimulation(); - return t("Started {}",this); + Window win = properties(); + new Tag("p").content(t("Started {}",this)).addTo(win); + return win; } public static void startAll() { diff --git a/src/main/java/de/srsoftware/web4rail/tiles/Relay.java b/src/main/java/de/srsoftware/web4rail/tiles/Relay.java index d13179c..4b48e97 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/Relay.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/Relay.java @@ -240,7 +240,7 @@ public class Relay extends Tile implements Device{ new Tag("option").attr("value","0").content(t("unset")).addTo(select); for (Relay relay : BaseClass.listElements(Relay.class)) { if (exclude.contains(relay)) continue; - Tag opt = select.addOption(relay.id, relay); + Tag opt = select.addOption(relay.id(), relay); if (relay == preselected) opt.attr("selected", "selected"); } return select; diff --git a/src/main/java/de/srsoftware/web4rail/tiles/Tile.java b/src/main/java/de/srsoftware/web4rail/tiles/Tile.java index 42f9c22..02400c3 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/Tile.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/Tile.java @@ -55,7 +55,7 @@ public abstract class Tile extends BaseClass implements Comparable{ private int length = DEFAUT_LENGTH; protected Direction oneWay = null; protected Route route = null; - private TreeSet routes = new TreeSet<>(); + private TreeSet routes = new TreeSet<>((r1,r2)->r1.toString().compareTo(r2.toString())); protected Train train = null; public Integer x = null; public Integer y = null; @@ -169,10 +169,11 @@ public abstract class Tile extends BaseClass implements Comparable{ } public Tile load(JSONObject json) { + if (json.has(ID)) json.remove(ID); // id should be created from cordinates super.load(json); JSONObject pos = json.getJSONObject(POS); x = pos.getInt(X); - y = pos.getInt(Y); + y = pos.getInt(Y); if (json.has(DISABLED)) disabled = json.getBoolean(DISABLED); if (json.has(LENGTH)) length = json.getInt(LENGTH); if (json.has(ONEW_WAY)) oneWay = Direction.valueOf(json.getString(ONEW_WAY));