From e00e6f8124ba8c2c417ce3b5954f28eda34713e4 Mon Sep 17 00:00:00 2001 From: Stephan Richter Date: Sat, 16 Jan 2021 19:33:59 +0100 Subject: [PATCH] added CoupleTrain action, improved Destination functions, fixed bug in AddRemoveTag, improved CarOrientation functions --- pom.xml | 2 +- .../translations/Application.de.translation | 15 +++- .../java/de/srsoftware/web4rail/Route.java | 22 +++++- .../srsoftware/web4rail/actions/Action.java | 1 + .../web4rail/actions/AddDestination.java | 14 +++- .../web4rail/actions/AddRemoveTag.java | 20 +++--- .../web4rail/actions/CoupleTrain.java | 71 +++++++++++++++++++ .../web4rail/conditions/CarOrientation.java | 31 ++++++-- .../de/srsoftware/web4rail/moving/Car.java | 7 +- .../de/srsoftware/web4rail/moving/Train.java | 45 +++++++++--- .../de/srsoftware/web4rail/tiles/Block.java | 10 ++- 11 files changed, 205 insertions(+), 33 deletions(-) create mode 100644 src/main/java/de/srsoftware/web4rail/actions/CoupleTrain.java diff --git a/pom.xml b/pom.xml index e23397d..860f45e 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 de.srsoftware web4rail - 1.3.26 + 1.3.27 Web4Rail jar Java Model Railway Control diff --git a/resources/translations/Application.de.translation b/resources/translations/Application.de.translation index b779be0..edeac7d 100644 --- a/resources/translations/Application.de.translation +++ b/resources/translations/Application.de.translation @@ -59,6 +59,8 @@ Brake time¹, reverse : Bremszeit¹, rückwärts Brake time table : Bremszeit-Tabelle CarInTrain : Fahrzeug im Zug Car manager : Waggon-Verwaltung +Car of train : Fahrzeug des Zuges +Car {} of train : Fahrzeug {} des Zuges CarOrientation : Wagen-Laufrichtung Cars : Waggons  cars :  Fahrzeugen teilen @@ -89,9 +91,14 @@ Control : Steuerung Control unit : Zentrale copy : kopieren Counterpart : Gegenstück +Couple : kupple +Couple first parked train : ersten geparkten Zug kuppeln +Couple last parked train : letzten geparkten Zug kuppeln +CoupleTrain : Zug kuppeln Create action : Aktion erzeugen Current location : Aufenthaltsort Current location\: {} : Aufenthaltsort: {} +Current orientation : aktuelle Fahrtrichtung Current velocity\: {} {} : Aktuelle Geschwindigkeit: {} {} custom fields : benutzerdefinierte Felder Decoder address : Decoder-Adresse @@ -145,6 +152,7 @@ Help : Hilfe Hold : an lassen (id\: {}, length\: {}) : (Id: {}, Länge: {}) if ({}) : falls ({}) +If car of train\: inspect car number : Falls Fahrzeug aus Zug: Untersuche Fahrzeug Nummer If checked, tiles behind the train are freed according to the length of the train and the tiles. If it is unchecked, tiles will not get free before route is finished. Falls aktiviert, wird die Strecke anhand von Zug- und Kachel-Länge hinter dem Zug freigegeben, Falls deaktiviert wird die Strecke hinter dem Zug erst bei Abschluss der Route freigegeben. internal contacts : interne Kontakte @@ -156,6 +164,7 @@ known locomotives : bekannte Lokomotiven known trains : bekannte Züge Label for state {} : Beschriftung für Status {} Last blocks : Letzte Blöcke +last parked train : letzten geparkten Zug learn : lernen LEFT : links Left port : Port für links @@ -295,12 +304,15 @@ State : Status StopAllTrains : Alle Züge stoppen StartStopAuto : Automatikmodus an/abschalten Stop autopilot : Autopilot abschalten +Stopped and reversed {}. : {} angehalten und gewendet. {} stopping at next block. : {} hält im nächsten Block. Stopsettings : Halte-Einstellungen StopTrain : Zug stoppen Stop train immediately : Zug sofort anhalten Straight port : Port für gerade STRAIGHT : gerade +Swap order : Reihenfolge umkehren +Swap order of trains : Reihenfolge der Züge tauschen SwitchFunction : Funktion schalten Switch power off : Strom ausschalten Switch power on : Strom anschalten @@ -325,7 +337,7 @@ train is longer than {} {} : Zug ist länger als {} {} train is not a push-pull train : Zug ist kein Wendezug train is not shunting : Zug rangiert nicht train is shorter than {} {} : Zug ist kürzer als {} {} -TrainIsShunting : Zug rangiert +TrainIsShunting : Rangierfahrt train is shunting : Zug rangiert train is slower than {} {} : Zug ist langsamer als {} {} TrainLength : Zug-Länge @@ -357,6 +369,7 @@ Turnouts : Weichen turn within train : innerhalb des Zugs drehen Turns the train, as if it went through a loop. : Dreht den ZUg, als wenn er eine Wendeschleife passiert hätte. Unknown action\: {} : Unbekannte Aktion: {} +Use negative number to count from end. : Nutze negative Nummern, um von Ende zu zählen. unset : ungesetzt WaitForContact : Auf Kontakt warten Wait for {}, then : auf {} warten, dann diff --git a/src/main/java/de/srsoftware/web4rail/Route.java b/src/main/java/de/srsoftware/web4rail/Route.java index e9d625b..9c00c88 100644 --- a/src/main/java/de/srsoftware/web4rail/Route.java +++ b/src/main/java/de/srsoftware/web4rail/Route.java @@ -75,6 +75,11 @@ public class Route extends BaseClass { private static final String ROUTE_START = "route_start"; private static final String ROUTE_SETUP = "route_setup"; + + public static final String DESTINATION_PREFIX = "@"; + public static final char TURN_FLAG = '±'; + public static final char FLAG_SEPARATOR = '+'; + public static final char SHUNTING_FLAG = '¥'; private int startSpeed; private static HashMap names = new HashMap(); // maps id to name. needed to keep names during plan.analyze() @@ -508,7 +513,7 @@ public class Route extends BaseClass { if (endBlock == train.destination()) { String destTag = null; for (String tag : train.tags()) { - if (tag.startsWith("@")) { + if (tag.startsWith(DESTINATION_PREFIX)) { destTag = tag; break; } @@ -517,8 +522,19 @@ public class Route extends BaseClass { if (isSet(destTag)) { String[] parts = destTag.split("@"); String destId = parts[1]; - boolean turn = destId.endsWith("+turn"); - if (turn) destId = destId.substring(0,destId.length()-5); + boolean turn = false; + + for (int i=destId.length()-1; i>0; i--) { + switch (destId.charAt(i)) { + case FLAG_SEPARATOR: + destId = destId.substring(0,i); + i=0; + break; + case TURN_FLAG: + turn = true; + break; + } + } if (destId.equals(endBlock.id().toString())) { if (turn) train.turn(); train.removeTag(destTag); diff --git a/src/main/java/de/srsoftware/web4rail/actions/Action.java b/src/main/java/de/srsoftware/web4rail/actions/Action.java index 5d7e1a6..09c03bc 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/Action.java +++ b/src/main/java/de/srsoftware/web4rail/actions/Action.java @@ -47,6 +47,7 @@ public abstract class Action extends BaseClass { BrakeCancel.class, BrakeStart.class, ConditionalAction.class, + CoupleTrain.class, DelayedAction.class, DetermineTrainInBlock.class, DisableEnableBlock.class, diff --git a/src/main/java/de/srsoftware/web4rail/actions/AddDestination.java b/src/main/java/de/srsoftware/web4rail/actions/AddDestination.java index 7afb0be..02b1d2d 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/AddDestination.java +++ b/src/main/java/de/srsoftware/web4rail/actions/AddDestination.java @@ -10,6 +10,7 @@ import org.json.JSONObject; import de.srsoftware.tools.Tag; import de.srsoftware.web4rail.Application; import de.srsoftware.web4rail.BaseClass; +import de.srsoftware.web4rail.Route; import de.srsoftware.web4rail.Window; import de.srsoftware.web4rail.moving.Train; import de.srsoftware.web4rail.tags.Checkbox; @@ -20,8 +21,10 @@ import de.srsoftware.web4rail.tiles.Tile; public class AddDestination extends Action { private static final String TURN = "turn"; + private static final String SHUNTING = "shunting"; private Block destination; private boolean turnAtDestination; + private boolean shunting; public AddDestination(BaseClass parent) { super(parent); @@ -39,9 +42,12 @@ public class AddDestination extends Action { } return true; } - String dest = "@"+destination.id()+(turnAtDestination?"+turn":""); + String flags = "+"; + if (turnAtDestination) flags += Route.TURN_FLAG; + if (shunting) flags += Route.SHUNTING_FLAG; + String dest = Route.DESTINATION_PREFIX+destination.id() + (flags.length()>1 ? flags : ""); for (String tag: train.tags()) { - if (tag.startsWith("@")) { + if (tag.startsWith(Route.DESTINATION_PREFIX)) { train.removeTag(tag); dest = tag+dest; break; @@ -56,12 +62,14 @@ public class AddDestination extends Action { JSONObject json = super.json(); if (isSet(destination)) json.put(Train.DESTINATION,destination.id().toString()); if (turnAtDestination) json.put(TURN,true); + if (shunting) json.put(SHUNTING, true); return json; } @Override public Action load(JSONObject json) { if (json.has(TURN)) turnAtDestination = json.getBoolean(TURN); + if (json.has(SHUNTING)) shunting = json.getBoolean(SHUNTING); if (json.has(Train.DESTINATION)) { Id blockId = new Id(json.getString(Train.DESTINATION)); destination = BaseClass.get(blockId); @@ -89,6 +97,7 @@ public class AddDestination extends Action { button(t("Clear destinations"),Map.of(ACTION,ACTION_UPDATE,Train.DESTINATION,"0")).addTo(span); formInputs.add(t("Destination")+": "+(isNull(destination) ? t("Clear destinations") : destination),span); formInputs.add(t("Turn at destination"),new Checkbox(TURN, t("Turn"), turnAtDestination)); + formInputs.add(t("Shunting"),new Checkbox(SHUNTING, t("Shunting"), shunting)); return super.properties(preForm, formInputs, postForm); } @@ -113,6 +122,7 @@ public class AddDestination extends Action { } } turnAtDestination = "on".equals(params.get(TURN)); + shunting = "on".equals(params.get(SHUNTING)); return context().properties(); } } diff --git a/src/main/java/de/srsoftware/web4rail/actions/AddRemoveTag.java b/src/main/java/de/srsoftware/web4rail/actions/AddRemoveTag.java index 1875bcd..fea65b5 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/AddRemoveTag.java +++ b/src/main/java/de/srsoftware/web4rail/actions/AddRemoveTag.java @@ -21,15 +21,15 @@ public class AddRemoveTag extends Action{ } private String tag = "test"; - private boolean add = true; + private boolean remove = false; @Override public boolean fire(Context context) { if (isNull(context.train())) return false; - if (add) { - context.train().tags().add(tag); + if (remove) { + context.train().removeTag(tag); } else { - context.train().tags().remove(tag); + context.train().addTag(tag); } return true; } @@ -38,13 +38,15 @@ public class AddRemoveTag extends Action{ public JSONObject json() { JSONObject json = super.json(); json.put(TAG, tag); + if (remove) json.put(ACTION_DROP, true); return json; } @Override public Action load(JSONObject json) { super.load(json); - tag = json.getString(TAG); + if (json.has(TAG)) tag = json.getString(TAG); + if (json.has(ACTION_DROP)) remove = json.getBoolean(ACTION_DROP); return this; } @@ -52,21 +54,21 @@ public class AddRemoveTag extends Action{ protected Window properties(List
preForm, FormInput formInputs, List
postForm) { formInputs.add(t("Tag"),new Input(TAG, tag)); Tag div = new Tag("div"); - new Radio(TYPE, ACTION_ADD, t("add"), add).addTo(div); - new Radio(TYPE, ACTION_DROP, t("delete"), !add).addTo(div); + new Radio(TYPE, ACTION_ADD, t("add"), !remove).addTo(div); + new Radio(TYPE, ACTION_DROP, t("delete"), remove).addTo(div); formInputs.add(t("Action"),div); return super.properties(preForm, formInputs, postForm); } @Override public String toString() { - return add ? t("Add tag \"{}\" to train",tag) : t("Remove tag \"{}\" from train",tag); + return remove ? t("Remove tag \"{}\" from train",tag) : t("Add tag \"{}\" to train",tag); } @Override protected Object update(HashMap params) { tag = params.get(TAG); - add = ACTION_ADD.equals(params.get(TYPE)); + remove = ACTION_DROP.equals(params.get(TYPE)); return super.update(params); } } diff --git a/src/main/java/de/srsoftware/web4rail/actions/CoupleTrain.java b/src/main/java/de/srsoftware/web4rail/actions/CoupleTrain.java new file mode 100644 index 0000000..1bc13ab --- /dev/null +++ b/src/main/java/de/srsoftware/web4rail/actions/CoupleTrain.java @@ -0,0 +1,71 @@ +package de.srsoftware.web4rail.actions; + +import java.util.HashMap; +import java.util.List; + +import org.json.JSONObject; + +import de.srsoftware.web4rail.BaseClass; +import de.srsoftware.web4rail.Window; +import de.srsoftware.web4rail.moving.Train; +import de.srsoftware.web4rail.tags.Checkbox; +import de.srsoftware.web4rail.tags.Fieldset; +import de.srsoftware.web4rail.tiles.Block; + +public class CoupleTrain extends Action { + + private static final String LAST = "last"; + private static final String SWAP = "swap"; + private boolean last = false; + private boolean swap = false; + + public CoupleTrain(BaseClass parent) { + super(parent); + } + + @Override + public boolean fire(Context context) { + Train train = context.train(); + if (isNull(train)) return false; + Block block = train.currentBlock(); + if (isNull(block)) return false; + Train parkingTrain = block.parkedTrains(last); + if (isNull(parkingTrain)) return false; + train.coupleWith(parkingTrain,swap); + return true; + } + + @Override + public JSONObject json() { + JSONObject json = super.json(); + if (last) json.put(LAST, last); + if (swap) json.put(SWAP, swap); + return json; + } + + @Override + public Action load(JSONObject json) { + if (json.has(LAST)) last = json.getBoolean(LAST); + if (json.has(SWAP)) swap = json.getBoolean(SWAP); + return super.load(json); + } + + @Override + public String toString() { + return last ? t("Couple last parked train") : t("Couple first parked train"); + } + + @Override + protected Window properties(List
preForm, FormInput formInputs, List
postForm) { + formInputs.add(t("Couple"),new Checkbox(LAST, t("last parked train"), last)); + formInputs.add(t("Swap order"),new Checkbox(SWAP, t("Swap order of trains"), swap)); + return super.properties(preForm, formInputs, postForm); + } + + @Override + protected Object update(HashMap params) { + last = "on".equals(params.get(LAST)); + swap = "on".equals(params.get(SWAP)); + return super.update(params); + } +} diff --git a/src/main/java/de/srsoftware/web4rail/conditions/CarOrientation.java b/src/main/java/de/srsoftware/web4rail/conditions/CarOrientation.java index 99c4835..7a5677a 100644 --- a/src/main/java/de/srsoftware/web4rail/conditions/CarOrientation.java +++ b/src/main/java/de/srsoftware/web4rail/conditions/CarOrientation.java @@ -9,25 +9,44 @@ import de.srsoftware.tools.Tag; import de.srsoftware.web4rail.BaseClass; import de.srsoftware.web4rail.Window; import de.srsoftware.web4rail.moving.Car; +import de.srsoftware.web4rail.moving.Train; import de.srsoftware.web4rail.tags.Fieldset; +import de.srsoftware.web4rail.tags.Input; import de.srsoftware.web4rail.tags.Radio; public class CarOrientation extends Condition { private static final String ORIENTATION = "orientation"; private static final String CAR = "car"; + private static final String POSITION = "position"; private boolean orientation = Car.FORWARD; + private int position = 1; private Car car; @Override public boolean fulfilledBy(Context context) { + Car car = this.car; + if (isNull(car)) { + Train train = context.train(); + if (position == 0 || isNull(train)) return false; + List cars = train.cars(); + if (position > 0) { + if (position>cars.size()) return false; + car = cars.get(position-1); + } else { + if (-position>cars.size()) return false; + car = cars.get(cars.size()+position); + } + } + if (isNull(car)) return false; return inverted ? car.orientation() != orientation : car.orientation() == orientation; } @Override public JSONObject json() { JSONObject json = super.json().put(ORIENTATION, orientation); - if (isSet(car)) json.put(CAR, car.id().toString()); + if (isSet(car)) json.put(CAR, car.id().toString()); + json.put(POSITION,position); return json; } @@ -35,23 +54,26 @@ public class CarOrientation extends Condition { super.load(json); if (json.has(CAR)) car = BaseClass.get(new Id(json.getString(CAR))); if (json.has(ORIENTATION)) orientation = json.getBoolean(ORIENTATION); + if (json.has(POSITION)) position = json.getInt(POSITION); return this; } @Override protected Window properties(List
preForm, FormInput formInputs, List
postForm) { - formInputs.add(t("Select car"),Car.selector(car, null)); + formInputs.add(t("Select car"),Car.selector(isSet(car) ? car : t("Car of train"), null)); + formInputs.add(t("If car of train: inspect car number"),new Input(POSITION, position).numeric().addTo(new Tag("span")).content(NBSP+"("+t("Use negative number to count from end.")+")")); Tag radioGroup = new Tag("span"); new Radio(ORIENTATION, "f", t("forward"), orientation).addTo(radioGroup); new Radio(ORIENTATION, "r", t("revers"), !orientation).addTo(radioGroup); - + return super.properties(preForm, formInputs, postForm); } @Override public String toString() { - return t("{} is oriented {}",car,inverted ? t("backward") : t("forward")); + String c = isSet(car) ? car.toString() : t("Car {} of train",position); + return t("{} is oriented {}",c,inverted ? t("backward") : t("forward")); } @Override @@ -67,6 +89,7 @@ public class CarOrientation extends Condition { } String carId = params.get(Car.class.getSimpleName()); if (isSet(carId)) car = BaseClass.get(new Id(carId)); + if (params.containsKey(POSITION)) position = Integer.parseInt(params.get(POSITION)); return super.update(params); } } diff --git a/src/main/java/de/srsoftware/web4rail/moving/Car.java b/src/main/java/de/srsoftware/web4rail/moving/Car.java index 4916a0b..0129259 100644 --- a/src/main/java/de/srsoftware/web4rail/moving/Car.java +++ b/src/main/java/de/srsoftware/web4rail/moving/Car.java @@ -247,6 +247,7 @@ public class Car extends BaseClass implements Comparable{ new Input(MAX_SPEED_REVERSE, maxSpeedReverse).numeric().addTo(new Tag("p")).content(NBSP+speedUnit+NBSP+t("backward")).addTo(div); formInputs.add(t("Maximum Speed"),div); if (train != null) formInputs.add(t("Train"), train.link()); + formInputs.add(t("Current orientation"),new Tag("span").content(orientation ? t("forward") : t("reverse"))); return super.properties(preForm,formInputs,postForm); } @@ -309,10 +310,12 @@ public class Car extends BaseClass implements Comparable{ return t("Reversed {}.",this); } - public static Select selector(Car preselected,Collection exclude) { + public static Select selector(Object preset,Collection exclude) { + Car preselected = preset instanceof Car ? (Car) preset : null; + String firstEntry = preset instanceof String ? (String) preset : t("unset"); if (isNull(exclude)) exclude = new Vector(); Select select = new Select(Car.class.getSimpleName()); - new Tag("option").attr("value","0").content(t("unset")).addTo(select); + new Tag("option").attr("value","0").content(firstEntry).addTo(select); List cars = BaseClass.listElements(Car.class); cars.sort((c1,c2) -> { if (isSet(c1.stockId)) return c1.stockId.compareTo(c2.stockId); diff --git a/src/main/java/de/srsoftware/web4rail/moving/Train.java b/src/main/java/de/srsoftware/web4rail/moving/Train.java index 1c2a9ae..dcaf8fc 100644 --- a/src/main/java/de/srsoftware/web4rail/moving/Train.java +++ b/src/main/java/de/srsoftware/web4rail/moving/Train.java @@ -318,6 +318,16 @@ public class Train extends BaseClass implements Comparable { return name().compareTo(o.toString()); } + public void coupleWith(Train parkingTrain,boolean swap) { + if (isSet(direction) && isSet(parkingTrain.direction) && parkingTrain.direction != direction) parkingTrain.turn(); + if (swap) { + Vector dummy = new Vector(parkingTrain.cars); + dummy.addAll(cars); + cars = dummy; + } else cars.addAll(parkingTrain.cars); + parkingTrain.remove(); + } + private static Object create(HashMap params, Plan plan) { String locoId = params.get(Train.LOCO_ID); if (isNull(locoId)) return t("Need loco id to create new train!"); @@ -336,18 +346,30 @@ public class Train extends BaseClass implements Comparable { public Block destination() { if (isNull(destination)) { - String destTag = null; + String destId = null; for (String tag : tags) { - if (tag.startsWith("@")) { - destTag = tag; + if (tag.startsWith(Route.DESTINATION_PREFIX)) { + destId = tag; break; } } - if (isSet(destTag)) { - String[] parts = destTag.split("@"); - destTag = parts[1]; - if (destTag.endsWith("+turn")) destTag = destTag.substring(0,destTag.length()-5); - BaseClass object = BaseClass.get(new Id(destTag)); + if (isSet(destId)) { + String[] parts = destId.split(Route.DESTINATION_PREFIX); + destId = parts[1]; + + for (int i=destId.length()-1; i>0; i--) { + switch (destId.charAt(i)) { + case Route.FLAG_SEPARATOR: + destId = destId.substring(0,i); + i=0; + break; + case Route.SHUNTING_FLAG: + shunting = true; + break; + } + } + + BaseClass object = BaseClass.get(new Id(destId)); if (object instanceof Block) destination = (Block) object; } } @@ -876,12 +898,17 @@ public class Train extends BaseClass implements Comparable { Car car = cars.remove(position); LOG.debug("Moving {} from {} to {}",car,this,remaining); remaining.add(car); + if (isNull(remaining.name)) { + remaining.name = car.name(); + } else if (remaining.name.length()+car.name().length()<20){ + remaining.name += ", "+car.name(); + } } else { LOG.debug("Skipping {}",cars.get(i)); } } if (remaining.cars.isEmpty()) return false; - remaining.name = this.name; + remaining.direction = this.direction; this.name = null; currentBlock.add(remaining); remaining.currentBlock = currentBlock; diff --git a/src/main/java/de/srsoftware/web4rail/tiles/Block.java b/src/main/java/de/srsoftware/web4rail/tiles/Block.java index 3f15b00..9084bf0 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/Block.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/Block.java @@ -213,7 +213,7 @@ public abstract class Block extends StretchableTile{ public Range getWaitTime(Train train,Direction dir) { for (WaitTime wt : waitTimes) { if (train.tags().contains(wt.tag)) { - LOG.info(t("{} using rule for \"{}\".",train,wt.tag)); + LOG.info(t("{} @ {} using rule for \"{}\".",train,this,wt.tag)); return wt.get(train.direction()); } } @@ -308,6 +308,12 @@ public abstract class Block extends StretchableTile{ return fieldset; } + public Train parkedTrains(boolean last) { + if (parkedTrains.isEmpty()) return null; + return last ? parkedTrains.lastElement() : parkedTrains.firstElement(); + } + + @Override protected Window properties(List
preForm, FormInput formInputs, List
postForm) { formInputs.add(t("Name"),new Input(NAME, name)); @@ -372,7 +378,7 @@ public abstract class Block extends StretchableTile{ Vector trainNames = new Vector(); if (isSet(train)) trainNames.add(train.directedName()); for (Train t:parkedTrains) { - if (isSet(t)) trainNames.add(t.name()); + if (isSet(t)) trainNames.add(t.directedName()); } if (!trainNames.isEmpty())replacements.put("%text%",String.join(" | ", trainNames)); Tag tag = super.tag(replacements);