diff --git a/pom.xml b/pom.xml index 435ed9f..91dadb4 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 de.srsoftware web4rail - 1.3.14 + 1.3.15 Web4Rail jar Java Model Railway Control diff --git a/resources/svg/DecouplerH.svg b/resources/svg/DecouplerH.svg new file mode 100644 index 0000000..28bd712 --- /dev/null +++ b/resources/svg/DecouplerH.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/resources/svg/DecouplerV.svg b/resources/svg/DecouplerV.svg new file mode 100644 index 0000000..2a04410 --- /dev/null +++ b/resources/svg/DecouplerV.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/resources/translations/Application.de.translation b/resources/translations/Application.de.translation index 3276d18..9d3770b 100644 --- a/resources/translations/Application.de.translation +++ b/resources/translations/Application.de.translation @@ -29,6 +29,7 @@ Analyze : analysieren Analyze may overwrite these routes! : Durch die Analyse können diese Fahrstraßen überschrieben werden! Analyzing plan... : Plan wird analysiert... and : und +AlterDirection : Fahrtrichtung ändern AndCondition : Und-Bedingung Application will load "{}" on next launch and will now quit! : Anwendung wird beim nächsten Start „{}“ laden und wird jetzt beendet. Apply : Übernehmen @@ -64,6 +65,7 @@ Click here to select car! : Hier klicken, um Fahrzeug auszuwählen! Click here to select display! : Hier klicken, um Anzeige auszuwählen! Click here to select train! : Hier klicken, um Zug auszuwählen! click here to setup contact : Hier klicken, um Kontakt auszuwählen +Click here to setup decoupler : Hier klicken, um Entkuppler einzurichten click here to setup relay : Hier klicken, um Relais einzurichten click here to setup signal : Hier klicken, um Signal einzurichten Click here to setup tag : Hier klicken, um Markierung anzugeben @@ -111,6 +113,8 @@ Edit json : JSON bearbeiten Effect : Effekt Emergency : Notfall enable {} : {} aktivieren +Engage {} : {} aktivieren +EngageDecoupler : Entkuppler aktivieren Enter new name for plan : Neuen Namen für den Plan eingeben export : exportieren Faster (10 {}) : 10 {} schneller @@ -166,6 +170,7 @@ Move tiles : Kacheln verschieben name\: : Name: new car : neuer Waggon new contact : neuer Kontakt +new direction : neue Fahrtrichtung new locomotive : neue Lok new tag : neue Markierung new train : neuer Zug @@ -211,6 +216,7 @@ rename : umbenennen Rename plan : Plan umbenennen Report Issue : Problem melden reverse : wenden +reverse train : Zug wenden Reversed {}. : {} umgedreht. RIGHT : rechts Right port\: : Port für rechts @@ -240,6 +246,7 @@ Send command "{}" to {} : Kommando „{}“ an {} senden Send command to : Kommando senden an Set {} as context : {} als Kontext setzen SetContextTrain : Zug für Folgeaktionen festlegen +Set direction of train to {} : Setze Richtung des Zugs auf {} SetDisplayText : Anzeige-Text setzen SetRelay : Relais schalten SetSignal : Signal stellen @@ -314,6 +321,7 @@ Trigger a feedback sensor to assign it with this contact! : Rückmeldekontakt au TriggerContact : Kontakt auslösen Trigger contact to learn new contact : Kontakt auslösen, um neuen Kontakt zu lernen Turn : Richtung wechseln +turn train : Richtung des zuges Wechseln Turn allowed : Wenden erlaubt {} turned. : {} gewendet. Turnout : Weiche diff --git a/src/main/java/de/srsoftware/web4rail/Device.java b/src/main/java/de/srsoftware/web4rail/Device.java index 978b206..787a273 100644 --- a/src/main/java/de/srsoftware/web4rail/Device.java +++ b/src/main/java/de/srsoftware/web4rail/Device.java @@ -4,7 +4,6 @@ import de.srsoftware.tools.Tag; public interface Device { public static final String ADDRESS = "address"; - public static final String PORT = "port"; public static final String PROTOCOL = "proto"; public int address(); diff --git a/src/main/java/de/srsoftware/web4rail/Plan.java b/src/main/java/de/srsoftware/web4rail/Plan.java index 39fb400..b80f954 100644 --- a/src/main/java/de/srsoftware/web4rail/Plan.java +++ b/src/main/java/de/srsoftware/web4rail/Plan.java @@ -50,6 +50,8 @@ import de.srsoftware.web4rail.tiles.ContactV; import de.srsoftware.web4rail.tiles.CrossH; import de.srsoftware.web4rail.tiles.CrossPlus; import de.srsoftware.web4rail.tiles.CrossV; +import de.srsoftware.web4rail.tiles.DecouplerH; +import de.srsoftware.web4rail.tiles.DecouplerV; import de.srsoftware.web4rail.tiles.DiagES; import de.srsoftware.web4rail.tiles.DiagNE; import de.srsoftware.web4rail.tiles.DiagSW; @@ -931,6 +933,8 @@ public class Plan extends BaseClass{ new TurnoutLW().tag(null).addTo(tiles); new TurnoutLE().tag(null).addTo(tiles); new Turnout3E().tag(null).addTo(tiles); + new DecouplerH().tag(null).addTo(tiles); + new DecouplerV().tag(null).addTo(tiles); new Relay().setLabel(true,"RL").tag(null).addTo(tiles); new Contact().tag(null).addTo(tiles); new TextDisplay().text("tx").tag(null).addTo(tiles); diff --git a/src/main/java/de/srsoftware/web4rail/actions/Action.java b/src/main/java/de/srsoftware/web4rail/actions/Action.java index f09751c..1be5db9 100644 --- a/src/main/java/de/srsoftware/web4rail/actions/Action.java +++ b/src/main/java/de/srsoftware/web4rail/actions/Action.java @@ -48,6 +48,7 @@ public abstract class Action extends BaseClass { ConditionalAction.class, DelayedAction.class, DetermineTrainInBlock.class, + EngageDecoupler.class, FinishRoute.class, Loop.class, PreserveRoute.class, diff --git a/src/main/java/de/srsoftware/web4rail/actions/EngageDecoupler.java b/src/main/java/de/srsoftware/web4rail/actions/EngageDecoupler.java new file mode 100644 index 0000000..cf0d6f8 --- /dev/null +++ b/src/main/java/de/srsoftware/web4rail/actions/EngageDecoupler.java @@ -0,0 +1,84 @@ +package de.srsoftware.web4rail.actions; + +import java.util.HashMap; +import java.util.List; + +import org.json.JSONObject; + +import de.srsoftware.web4rail.Application; +import de.srsoftware.web4rail.BaseClass; +import de.srsoftware.web4rail.Window; +import de.srsoftware.web4rail.tags.Fieldset; +import de.srsoftware.web4rail.tiles.Decoupler; + +public class EngageDecoupler extends Action { + + private static final String DECOUPLER = Decoupler.class.getSimpleName(); + + public EngageDecoupler(BaseClass parent) { + super(parent); + } + + private Decoupler decoupler = null; + + @Override + public boolean fire(Context context) { + if (isNull(decoupler)) return false; + decoupler.engage(); + return true; + } + + @Override + public JSONObject json() { + JSONObject json = super.json(); + if (isSet(decoupler)) { + json.put(Decoupler.class.getSimpleName(), decoupler.id()); + } + return json; + } + + @Override + public Action load(JSONObject json) { + super.load(json); + if (json.has(DECOUPLER)) { + String decouplerId = json.getString(DECOUPLER); + decoupler = BaseClass.get(new Id(decouplerId)); + if (isNull(decoupler)) Application.threadPool.execute(new Thread() { // if relay not loaded, yet: wait one sec and try again + public void run() { + try { + sleep(1000); + } catch (InterruptedException e) {} + decoupler = BaseClass.get(new Id(decouplerId)); + }; + }); + } + return this; + } + + @Override + protected Window properties(List
preForm, FormInput formInputs, List
postForm) { + + formInputs.add(t("Select decoupler"),Decoupler.selector(decoupler,null)); + + return super.properties(preForm, formInputs, postForm); + } + + @Override + protected void removeChild(BaseClass child) { + if (child == decoupler) decoupler = null; + super.removeChild(child); + } + + public String toString() { + if (isNull(decoupler)) return "["+t("Click here to setup decoupler")+"]"; + return t("Engage {}",decoupler); + }; + + @Override + protected Object update(HashMap params) { + LOG.debug("update: {}",params); + Id decouplerId = new Id(params.get(DECOUPLER)); + decoupler = BaseClass.get(decouplerId); + return context().properties(); + } +} diff --git a/src/main/java/de/srsoftware/web4rail/tiles/Decoupler.java b/src/main/java/de/srsoftware/web4rail/tiles/Decoupler.java new file mode 100644 index 0000000..5a48623 --- /dev/null +++ b/src/main/java/de/srsoftware/web4rail/tiles/Decoupler.java @@ -0,0 +1,174 @@ +package de.srsoftware.web4rail.tiles; + +import java.io.IOException; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Vector; +import java.util.concurrent.TimeoutException; + +import org.json.JSONObject; + +import de.srsoftware.tools.Tag; +import de.srsoftware.web4rail.BaseClass; +import de.srsoftware.web4rail.Command; +import de.srsoftware.web4rail.Command.Reply; +import de.srsoftware.web4rail.Device; +import de.srsoftware.web4rail.Protocol; +import de.srsoftware.web4rail.Window; +import de.srsoftware.web4rail.tags.Fieldset; +import de.srsoftware.web4rail.tags.Input; +import de.srsoftware.web4rail.tags.Radio; +import de.srsoftware.web4rail.tags.Select; + +public abstract class Decoupler extends Tile implements Device{ + + protected int address = 0; + private Protocol protocol = Protocol.DCC128; + protected int port = 0; + private boolean initialized = false; + + @Override + public int address() { + return address; + } + + @Override + public Object click() throws IOException { + Object o = super.click(); + engage(); + return o; + } + + public Reply engage() { + Reply reply = init(); + if (reply != null && !reply.succeeded()) return reply; + try { + + return plan.queue(new Command("SET {} GA "+address+" "+port+" 1 "+100) { + + @Override + public void onSuccess() { + super.onSuccess(); + plan.place(Decoupler.this); + } + + @Override + protected void onFailure(Reply reply) { + super.onFailure(reply); + plan.stream(t("Unable to engage \"{}\": {}",Decoupler.this,reply.message())); + } + + }).reply(); + } catch (TimeoutException e) { + LOG.warn(e.getMessage()); + } + return new Reply(417,t("Timeout while trying to switch {}.",this)); + } + + protected Reply init() { + if (address == 0) return new Reply(200,"OK"); + if (!initialized) { + Command command = new Command("INIT {} GA "+address+" "+proto()) { + + @Override + public void onSuccess() { + super.onSuccess(); + initialized = true; + } + + @Override + public void onFailure(Reply r) { + super.onFailure(r); + initialized = false; + } + + }; + try { + return plan.queue(command).reply(); + } catch (TimeoutException e) { + LOG.warn(e.getMessage()); + } + } + return new Reply(200, "OK"); + } + + @Override + public JSONObject json() { + JSONObject json = super.json(); + json.put(PROTOCOL, protocol); + if (address != 0) json.put(ADDRESS, address); + json.put(PORT, port); + return json; + } + + @Override + public Tile load(JSONObject json) { + if (json.has(ADDRESS)) address = json.getInt(ADDRESS); + if (json.has(PROTOCOL)) protocol = Protocol.valueOf(json.getString(PROTOCOL)); + if (json.has(PORT)) port = json.getInt(PORT); + return super.load(json); + } + + @Override + protected Window properties(List
preForm, FormInput formInputs, List
postForm) { + Tag div = new Tag("div"); + for (Protocol proto : Protocol.values()) { + new Radio(PROTOCOL, proto.toString(), t(proto.toString()), proto == protocol).addTo(div); + } + formInputs.add(t("Protocol"),div); + formInputs.add(t("Address:"),new Input(ADDRESS, address).numeric()); + formInputs.add(t("Port:"),new Input(PORT, port).numeric()); + + return super.properties(preForm, formInputs, postForm); + } + + private char proto() { + switch (protocol) { + case DCC14: + case DCC27: + case DCC28: + case DCC128: + return 'N'; + case MOTO: + return 'M'; + case SELECTRIX: + return 'S'; + default: + return 'P'; + } + } + + public static Select selector(Decoupler preselected, Collection exclude) { + if (isNull(exclude)) exclude = new Vector(); + Select select = new Select(Decoupler.class.getSimpleName()); + new Tag("option").attr("value","0").content(t("unset")).addTo(select); + for (Decoupler decoupler : BaseClass.listElements(Decoupler.class)) { + if (exclude.contains(decoupler)) continue; + Tag opt = select.addOption(decoupler.id(), decoupler); + if (decoupler == preselected) opt.attr("selected", "selected"); + } + return select; + } + + @Override + public Tile update(HashMap params) { + if (params.containsKey(PROTOCOL)) protocol = Protocol.valueOf(params.get(PROTOCOL)); + if (params.containsKey(ADDRESS)) { + int newAddress = Integer.parseInt(params.get(ADDRESS)); + if (newAddress != address) { + initialized = false; + address = newAddress; + } + } + String newPort = params.get(PORT); + if (isSet(newPort)) { + int npa = Integer.parseInt(newPort); + if (npa != port) { + port = npa; + initialized = false; + } + } + return super.update(params); + } +} diff --git a/src/main/java/de/srsoftware/web4rail/tiles/DecouplerH.java b/src/main/java/de/srsoftware/web4rail/tiles/DecouplerH.java new file mode 100644 index 0000000..a6093df --- /dev/null +++ b/src/main/java/de/srsoftware/web4rail/tiles/DecouplerH.java @@ -0,0 +1,30 @@ +package de.srsoftware.web4rail.tiles; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import de.srsoftware.web4rail.Connector; +import de.srsoftware.web4rail.Plan.Direction; +import de.srsoftware.web4rail.tiles.Turnout.State; + +public class DecouplerH extends Decoupler{ + + @Override + public Map connections(Direction from) { + if (isNull(from) || oneWay == from) return new HashMap<>(); + switch (from) { + case WEST: + return Map.of(new Connector(x+width(),y,Direction.WEST),State.UNDEF); + case EAST: + return Map.of(new Connector(x-1,y,Direction.EAST),State.UNDEF); + default: + return new HashMap<>(); + } + } + + @Override + public List possibleDirections() { + return List.of(Direction.EAST,Direction.WEST); + } +} diff --git a/src/main/java/de/srsoftware/web4rail/tiles/DecouplerV.java b/src/main/java/de/srsoftware/web4rail/tiles/DecouplerV.java new file mode 100644 index 0000000..6d14e4d --- /dev/null +++ b/src/main/java/de/srsoftware/web4rail/tiles/DecouplerV.java @@ -0,0 +1,30 @@ +package de.srsoftware.web4rail.tiles; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import de.srsoftware.web4rail.Connector; +import de.srsoftware.web4rail.Plan.Direction; +import de.srsoftware.web4rail.tiles.Turnout.State; + +public class DecouplerV extends Decoupler{ + + @Override + public Map connections(Direction from) { + if (isNull(from) || oneWay == from) return new HashMap<>(); + switch (from) { + case NORTH: + return Map.of(new Connector(x+width(),y,Direction.NORTH),State.UNDEF); + case SOUTH: + return Map.of(new Connector(x-1,y,Direction.SOUTH),State.UNDEF); + default: + return new HashMap<>(); + } + } + + @Override + public List possibleDirections() { + return List.of(Direction.SOUTH,Direction.NORTH); + } +} diff --git a/src/main/java/de/srsoftware/web4rail/tiles/Tile.java b/src/main/java/de/srsoftware/web4rail/tiles/Tile.java index 839380d..76601c9 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/Tile.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/Tile.java @@ -24,6 +24,7 @@ import de.srsoftware.web4rail.Connector; import de.srsoftware.web4rail.PathFinder; import de.srsoftware.web4rail.Plan; import de.srsoftware.web4rail.Plan.Direction; +import de.srsoftware.web4rail.actions.AlterDirection; import de.srsoftware.web4rail.Route; import de.srsoftware.web4rail.Window; import de.srsoftware.web4rail.moving.Train; @@ -195,6 +196,9 @@ public abstract class Tile extends BaseClass implements Comparable{ if (object instanceof JSONObject) { JSONObject json = (JSONObject) object; String clazz = json.getString(TYPE); + if (clazz.equals("TurnTrain")) { + clazz = AlterDirection.class.getSimpleName(); + } try { Tile.inflate(clazz,json,plan); } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException | ClassNotFoundException | IOException e) {