diff --git a/doc/images/noobs.jpg b/doc/images/noobs.jpg new file mode 100644 index 0000000..e9b8cd7 Binary files /dev/null and b/doc/images/noobs.jpg differ diff --git a/pom.xml b/pom.xml index bde219e..1377c14 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 de.srsoftware web4rail - 1.4.33 + 1.4.34 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 f8d97a7..ddfa88a 100644 --- a/src/main/java/de/srsoftware/web4rail/BaseClass.java +++ b/src/main/java/de/srsoftware/web4rail/BaseClass.java @@ -55,6 +55,7 @@ public abstract class BaseClass implements Constants{ public static final Logger LOG = LoggerFactory.getLogger(BaseClass.class); private static final String CUSTOM_FIELDS = "custom_Fields"; private static final String NEW_CUSTOM_FIELD_NAME = "new_custom_field_name"; + protected static final String PROPS_BASIC = "props-basic"; protected HashMap customFieldValues = new HashMap(); private BaseClass parent; @@ -517,7 +518,7 @@ public abstract class BaseClass implements Constants{ formInputs.add(new AbstractMap.SimpleEntry(t("Notes"),new TextArea(NOTES,notes))); form(getClass().getSimpleName()+"-prop-form",formInputs) - .addTo(new Fieldset(t("Basic properties")).id("props-basic")) + .addTo(new Fieldset(t("Basic properties")).id(PROPS_BASIC)) .addTo(win); postForm.forEach(fieldset -> fieldset.addTo(win)); diff --git a/src/main/java/de/srsoftware/web4rail/Plan.java b/src/main/java/de/srsoftware/web4rail/Plan.java index 6989e2c..0374717 100644 --- a/src/main/java/de/srsoftware/web4rail/Plan.java +++ b/src/main/java/de/srsoftware/web4rail/Plan.java @@ -26,6 +26,7 @@ import org.slf4j.LoggerFactory; import de.srsoftware.localconfig.Configuration; import de.srsoftware.tools.Tag; +import de.srsoftware.web4rail.devices.Device; import de.srsoftware.web4rail.moving.Car; import de.srsoftware.web4rail.moving.Train; import de.srsoftware.web4rail.tags.Button; diff --git a/src/main/java/de/srsoftware/web4rail/devices/Decoder.java b/src/main/java/de/srsoftware/web4rail/devices/Decoder.java new file mode 100644 index 0000000..24a7b6c --- /dev/null +++ b/src/main/java/de/srsoftware/web4rail/devices/Decoder.java @@ -0,0 +1,206 @@ +package de.srsoftware.web4rail.devices; + +import java.util.List; +import java.util.Set; +import java.util.TreeMap; +import java.util.Map.Entry; +import java.util.concurrent.TimeoutException; + +import org.json.JSONObject; + +import de.srsoftware.tools.Tag; +import de.srsoftware.tools.Tools; +import de.srsoftware.web4rail.BaseClass; +import de.srsoftware.web4rail.Command; +import de.srsoftware.web4rail.Command.Reply; +import de.srsoftware.web4rail.tags.Button; +import de.srsoftware.web4rail.tags.Fieldset; +import de.srsoftware.web4rail.tags.Form; +import de.srsoftware.web4rail.tags.Input; +import de.srsoftware.web4rail.tags.Radio; +import de.srsoftware.web4rail.tags.Table; +import de.srsoftware.web4rail.tags.Window; +import de.srsoftware.web4rail.Constants; +import de.srsoftware.web4rail.Protocol; + +public class Decoder extends BaseClass implements Constants, Device { + + public static final String DECODER = "decoder"; + public static final String CVS = "cvs"; + private Integer address = null; + public TreeMap cvs = new TreeMap(); + private boolean init = false; + private Protocol proto = Protocol.DCC128; + public static final Integer CV_ADDR = 1; + private static final String ACTION_PROGRAM = "program"; + private static final String MODE = "mode"; + private static final String POM = "pom"; + private static final String TRACK = "track"; + private static final String VALUE = "val"; + private static final String CV = "cv"; + + + @Override + public int address() { + if (isNull(address)) address = cvs.get(CV_ADDR); + return address; + } + + public void init() { + if (init) return; + String proto = null; + switch (this.proto) { + case FLEISCH: + proto = "F"; break; + case MOTO: + proto = "M 2 100 0"; break; // TODO: make configurable + case DCC14: + case DCC27: + case DCC28: + case DCC128: + proto = "N 1 "+this.proto.steps+" 5"; break; // TODO: make configurable + case SELECTRIX: + proto = "S"; break; + } + plan.queue(new Command("INIT {} GL "+address()+" "+proto) { + + @Override + public void onSuccess() { + super.onSuccess(); + plan.stream(t("{} initialized.",this)); + } + + @Override + public void onFailure(Reply r) { + super.onFailure(r); + plan.stream(t("Was not able to initialize {}!",this)); + } + }); + init = true; + } + + @Override + public JSONObject json() { + JSONObject json = super.json(); + json.put(CVS, cvs); + json.put(PROTOCOL, proto); + return json; + } + + @Override + public Tag link(String... args) { + Tools.notImplemented("Decoder.link(…)"); + return new Tag("span").content("[[Decoder.link() not implemented]]"); + } + + @Override + public Decoder load(JSONObject json) { + super.load(json); + if (json.has(PROTOCOL)) proto = Protocol.valueOf(json.getString(PROTOCOL)); + if (json.has(CVS)) { // Legacy + JSONObject jCvs = json.getJSONObject(CVS); + for (String key : jCvs.keySet()) cvs.put(Integer.parseInt(key),jCvs.getInt(key)); + } + return this; + } + + private String program(int cv,int val,boolean pom) { + if (cv != 0) { + if (val < 0) { + cvs.remove(cv); + return null; + } + init(); + Command command = new Command("SET {} SM "+(pom?address():-1)+" CV "+cv+" "+val); + try { + Reply reply = plan.queue(command).reply(); + if (reply.succeeded()) { + cvs.put(cv, val); + address = cvs.get(CV_ADDR); // update address field: + return null; + } + + return reply.message(); + } catch (TimeoutException e) { + return t("Timeout while sending programming command!"); + } + } + return null; + } + + private Fieldset programming() { + Fieldset fieldset = new Fieldset(t("Programming")).id("props-cv"); + + Form form = new Form("cv-form"); + new Input(REALM,REALM_LOCO).hideIn(form); + new Input(ID,id()).hideIn(form); + new Input(ACTION,ACTION_PROGRAM).hideIn(form); + + Table table = new Table(); + table.addHead(t("setting"),t("CV"),t("value"),t("actions")); + for (int cv=1; cv<19; cv++) { + Object val = cvs.get(cv); + if (isNull(val)) { + if (Set.of(7, 8, 10, 11, 12, 13, 14, 15, 16).contains(cv)) continue; + val = t("no value"); + } + table.addRow(setting(cv),cv,val,new Button(t("edit"), "copyCv(this);")); + } + for (Entry entry : cvs.entrySet()){ + int cv = entry.getKey(); + if (cv<10) continue; + int val = entry.getValue(); + table.addRow(setting(cv),cv,val,new Button(t("edit"), "copyCv(this);")); + } + Tag mode = new Tag("div"); + new Radio(MODE, POM, t("program on main"), true).addTo(mode); + new Radio(MODE, TRACK, t("prgramming track"), false).addTo(mode); + table.addRow(mode,new Input(CV,0).numeric(),new Input(VALUE,0).numeric(),new Button(t("Apply"),form)); + return table.addTo(form).addTo(fieldset); + } + + @Override + protected Window properties(List
preForm, FormInput formInputs, List
postForm, String... errorMessages) { + Tag div = new Tag("div"); + for (Protocol proto : Protocol.values()) { + new Radio(PROTOCOL, proto.toString(), t(proto.toString()), proto == this.proto).addTo(div); + } + formInputs.add(t("Protocol"),div); + formInputs.add(t("Address"),new Input(ADDRESS, address).numeric()); + postForm.add(programming()); + return super.properties(preForm, formInputs, postForm, errorMessages); + } + + public Protocol protocol() { + return proto; + } + + public void setProtocol(Protocol proto) { + this.proto = proto; + } + + private Object setting(int cv) { + switch (cv) { + case 1: + return t("Address"); + case 2: + return t("minimum starting voltage vmin"); + case 3: + return t("starting delay"); + case 4: + return t("braking delay"); + case 5: + return t("maximum speed vmax"); + case 6: + return t("mid speed vmid"); + case 9: + return t("PWM rate"); + case 17: + case 18: + return t("extended address"); + } + return ""; + } + + +} diff --git a/src/main/java/de/srsoftware/web4rail/devices/Device.java b/src/main/java/de/srsoftware/web4rail/devices/Device.java index f32bcfc..eb411de 100644 --- a/src/main/java/de/srsoftware/web4rail/devices/Device.java +++ b/src/main/java/de/srsoftware/web4rail/devices/Device.java @@ -1,4 +1,4 @@ -package de.srsoftware.web4rail; +package de.srsoftware.web4rail.devices; import de.srsoftware.tools.Tag; diff --git a/src/main/java/de/srsoftware/web4rail/moving/Locomotive.java b/src/main/java/de/srsoftware/web4rail/moving/Locomotive.java index 4177807..47ddc3e 100644 --- a/src/main/java/de/srsoftware/web4rail/moving/Locomotive.java +++ b/src/main/java/de/srsoftware/web4rail/moving/Locomotive.java @@ -8,6 +8,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.TreeMap; +import java.util.Vector; import java.util.concurrent.TimeoutException; import org.json.JSONObject; @@ -16,8 +17,9 @@ 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.devices.Decoder; +import de.srsoftware.web4rail.devices.Device; import de.srsoftware.web4rail.Constants; -import de.srsoftware.web4rail.Device; import de.srsoftware.web4rail.Plan; import de.srsoftware.web4rail.Protocol; import de.srsoftware.web4rail.tags.Button; @@ -31,23 +33,13 @@ import de.srsoftware.web4rail.tags.Table; import de.srsoftware.web4rail.tags.Window; import de.srsoftware.web4rail.tiles.Block; -public class Locomotive extends Car implements Constants,Device{ +public class Locomotive extends Car implements Constants{ public static final String LOCOMOTIVE = "locomotive"; - private static final Integer CV_ADDR = 1; - private static final String CVS = "cvs"; - private static final String ACTION_PROGRAM = "program"; - private static final String CV = "cv"; - private static final String VALUE = "val"; - private static final String MODE = "mode"; - private static final String POM = "pom"; - private static final String TRACK = "track"; - private Protocol proto = Protocol.DCC128; - private int address = 3; private int speed = 0; private boolean f1,f2,f3,f4; - private boolean init = false; - private TreeMap cvs = new TreeMap(); + //private TreeMap cvs = new TreeMap(); + private Decoder decoder; public Locomotive(String name) { super(name); @@ -68,8 +60,8 @@ public class Locomotive extends Car implements Constants,Device{ return loco.faster(Train.defaultSpeedStep); case ACTION_MOVE: return loco.moveUp(); - case ACTION_PROGRAM: - return loco.update(params); +/* case ACTION_PROGRAM: + return loco.update(params); */ case ACTION_PROPS: return loco == null ? Locomotive.manager() : loco.properties(); case ACTION_SET_SPEED: @@ -95,11 +87,6 @@ public class Locomotive extends Car implements Constants,Device{ return t("Unknown action: {}",params.get(ACTION)); } - - @Override - public int address() { - return address; - } public static Fieldset cockpit(BaseClass locoOrTrain) { int speed = 0; @@ -212,55 +199,22 @@ public class Locomotive extends Car implements Constants,Device{ } private String detail() { - return getClass().getSimpleName()+"("+name()+", "+proto+", "+address+")"; + return getClass().getSimpleName()+"("+name()+", "+decoder.protocol()+", "+decoder.address()+")"; } public Tag faster(int steps) { return setSpeed(speed + steps); } - - private void init() { - if (init) return; - String proto = null; - switch (this.proto) { - case FLEISCH: - proto = "F"; break; - case MOTO: - proto = "M 2 100 0"; break; // TODO: make configurable - case DCC14: - case DCC27: - case DCC28: - case DCC128: - proto = "N 1 "+this.proto.steps+" 5"; break; // TODO: make configurable - case SELECTRIX: - proto = "S"; break; - } - plan.queue(new Command("INIT {} GL "+address+" "+proto) { - - @Override - public void onSuccess() { - super.onSuccess(); - plan.stream(t("{} initialized.",this)); - } - - @Override - public void onFailure(Reply r) { - super.onFailure(r); - plan.stream(t("Was not able to initialize {}!",this)); - } - }); - init = true; - } - @Override public JSONObject json() { JSONObject json = super.json(); JSONObject loco = new JSONObject(); - loco.put(PROTOCOL, proto); + //loco.put(PROTOCOL, proto); json.put(LOCOMOTIVE, loco); - loco.put(CVS, cvs); + //loco.put(CVS, cvs); + loco.put(Decoder.DECODER,decoder.json()); return json; } @@ -268,13 +222,15 @@ public class Locomotive extends Car implements Constants,Device{ public Car load(JSONObject json) { super.load(json); if (json.has(LOCOMOTIVE)) { + if (isNull(decoder)) decoder = new Decoder(); + JSONObject loco = json.getJSONObject(LOCOMOTIVE); - if (loco.has(PROTOCOL)) proto = Protocol.valueOf(loco.getString(PROTOCOL)); - if (loco.has(ADDRESS)) setAddress(loco.getInt(ADDRESS)); - if (loco.has(CVS)) { - JSONObject jCvs = loco.getJSONObject(CVS); - for (String key : jCvs.keySet()) cvs.put(Integer.parseInt(key),jCvs.getInt(key)); - address = cvs.get(CV_ADDR); + if (loco.has(Decoder.DECODER)) decoder.load(json.getJSONObject(Decoder.DECODER)); + if (loco.has(Decoder.CVS)) { // Legacy + JSONObject jCvs = loco.getJSONObject(Decoder.CVS); + for (String key : jCvs.keySet()) { + decoder.cvs.put(Integer.parseInt(key),jCvs.getInt(key)); + } } } return this; @@ -288,7 +244,7 @@ public class Locomotive extends Car implements Constants,Device{ Table table = new Table().addHead(t("Stock ID"),t("Name"),t("Max. Speed",speedUnit),t("Protocol"),t("Address"),t("Length"),t("driven distance"),t("Tags")); List locos = BaseClass.listElements(Locomotive.class); - locos.sort(Comparator.comparing(loco -> loco.address)); + locos.sort(Comparator.comparing(loco -> isSet(loco.decoder) ? loco.decoder.address() : 0)); locos.sort(Comparator.comparing(loco -> loco.stockId)); for (Locomotive loco : locos) { String maxSpeed = (loco.maxSpeedForward == 0 ? "–":""+loco.maxSpeedForward)+NBSP; @@ -296,8 +252,8 @@ public class Locomotive extends Car implements Constants,Device{ table.addRow(loco.stockId, loco.link(), maxSpeed+NBSP+speedUnit, - loco.proto, - loco.address, + isSet(loco.decoder) ? loco.decoder.protocol() : null, + isSet(loco.decoder) ? loco.decoder.address() : null, loco.length+NBSP+lengthUnit, loco.distanceCounter, String.join(", ", loco.tags())); @@ -315,77 +271,32 @@ public class Locomotive extends Car implements Constants,Device{ return win; } - private String program(int cv,int val,boolean pom) { - if (cv != 0) { - if (val < 0) { - cvs.remove(cv); - return null; - } - init(); - Command command = new Command("SET {} SM "+(pom?address:-1)+" CV "+cv+" "+val); - try { - Reply reply = plan.queue(command).reply(); - if (reply.succeeded()) { - cvs.put(cv, val); - if (cv == CV_ADDR) address = val; - return null; - } - return reply.message(); - } catch (TimeoutException e) { - return t("Timeout while sending programming command!"); - } - } - return null; - } - - private Fieldset programming() { - Fieldset fieldset = new Fieldset(t("Programming")).id("props-cv"); - Form form = new Form("cv-form"); - new Input(REALM,REALM_LOCO).hideIn(form); - new Input(ID,id()).hideIn(form); - new Input(ACTION,ACTION_PROGRAM).hideIn(form); + - Table table = new Table(); - table.addHead(t("setting"),t("CV"),t("value"),t("actions")); - for (int cv=1; cv<19; cv++) { - Object val = cvs.get(cv); - if (isNull(val)) { - if (Set.of(7, 8, 10, 11, 12, 13, 14, 15, 16).contains(cv)) continue; - val = t("no value"); - } - table.addRow(setting(cv),cv,val,new Button(t("edit"), "copyCv(this);")); - } - for (Entry entry : cvs.entrySet()){ - int cv = entry.getKey(); - if (cv<10) continue; - int val = entry.getValue(); - table.addRow(setting(cv),cv,val,new Button(t("edit"), "copyCv(this);")); - } - Tag mode = new Tag("div"); - new Radio(MODE, POM, t("program on main"), true).addTo(mode); - new Radio(MODE, TRACK, t("prgramming track"), false).addTo(mode); - table.addRow(mode,new Input(CV,0).numeric(),new Input(VALUE,0).numeric(),new Button(t("Apply"),form)); - return table.addTo(form).addTo(fieldset); - } @Override protected Window properties(List
preForm, FormInput formInputs, List
postForm,String...errors) { preForm.add(cockpit(this)); - Tag div = new Tag("div"); - for (Protocol proto : Protocol.values()) { - new Radio(PROTOCOL, proto.toString(), t(proto.toString()), proto == this.proto).addTo(div); + Window props = super.properties(preForm, formInputs, postForm,errors); + if (isSet(decoder)) { + Tag basicProps = props.children().stream().filter(tag -> BaseClass.PROPS_BASIC.equals(tag.get("id"))).findFirst().get(); + Tag form = basicProps.children().stream().filter(tag -> tag.is("form")).findFirst().get(); + Table table = (Table) form.children().stream().filter(tag -> tag.is("table")).findFirst().get(); + table.addRow(t("Decoder"),decoder.link()); + Vector cols = table.children(); + Tag lastRow = cols.lastElement(); + cols.remove(cols.size()-1); + cols.insertElementAt(lastRow, 5); } - formInputs.add(t("Protocol"),div); - formInputs.add(t("Address"),new Input(ADDRESS, address).numeric()); - postForm.add(programming()); - return super.properties(preForm, formInputs, postForm,errors); + return props; } - + private void queue() { - int step = proto.steps * speed / (maxSpeedForward == 0 ? 100 : maxSpeedForward); - init(); - plan.queue(new Command("SET {} GL "+address+" "+(orientation == FORWARD ? 0 : 1)+" "+step+" "+proto.steps+" "+(f1?1:0)+" "+(f2?1:0)+" "+(f3?1:0)+" "+(f4?1:0)) { + if (isNull(decoder)) return; + int step = decoder.protocol().steps * speed / (maxSpeedForward == 0 ? 100 : maxSpeedForward); + decoder.init(); + plan.queue(new Command("SET {} GL "+decoder.address()+" "+(orientation == FORWARD ? 0 : 1)+" "+step+" "+decoder.protocol().steps+" "+(f1?1:0)+" "+(f2?1:0)+" "+(f3?1:0)+" "+(f4?1:0)) { @Override public void onFailure(Reply reply) { @@ -395,13 +306,6 @@ public class Locomotive extends Car implements Constants,Device{ }); } - private Locomotive setAddress(int newAddress) { - address = newAddress; - cvs.put(CV_ADDR, newAddress); - init = false; - return this; - } - public String setFunction(int num, boolean active) { switch (num) { case 1: @@ -439,29 +343,7 @@ public class Locomotive extends Car implements Constants,Device{ return properties(); } - - private Object setting(int cv) { - switch (cv) { - case 1: - return t("Address"); - case 2: - return t("minimum starting voltage vmin"); - case 3: - return t("starting delay"); - case 4: - return t("braking delay"); - case 5: - return t("maximum speed vmax"); - case 6: - return t("mid speed vmid"); - case 9: - return t("PWM rate"); - case 17: - case 18: - return t("extended address"); - } - return ""; - } + public Object stop() { @@ -493,19 +375,19 @@ public class Locomotive extends Car implements Constants,Device{ @Override protected Window update(HashMap params) { super.update(params); - if (params.containsKey(PROTOCOL)) proto = Protocol.valueOf(params.get(PROTOCOL)); - if (params.containsKey(ADDRESS)) { - int newAddress = Integer.parseInt(params.get(ADDRESS)); - if (newAddress != address) setAddress(newAddress); + if (isSet(decoder) && params.containsKey(Device.PROTOCOL)) decoder.setProtocol(Protocol.valueOf(params.get(Device.PROTOCOL))); + if (isSet(decoder) && params.containsKey(Device.ADDRESS)) { + int newAddress = Integer.parseInt(params.get(Device.ADDRESS)); + if (newAddress != decoder.address()) decoder.cvs.put(Decoder.CV_ADDR, newAddress); } String error = null; - if (params.get(ACTION).equals(ACTION_PROGRAM)) try { + /*if (params.get(ACTION).equals(ACTION_PROGRAM)) try { int cv = Integer.parseInt(params.get(CV)); int val = Integer.parseInt(params.get(VALUE)); boolean pom = !params.get(MODE).equals(TRACK); error = program(cv,val,pom); - } catch (NumberFormatException e) {} + } catch (NumberFormatException e) {}*/ Window props = properties(); if (isSet(error)) new Tag("span").content(error).addTo(props); return props; diff --git a/src/main/java/de/srsoftware/web4rail/tiles/Decoupler.java b/src/main/java/de/srsoftware/web4rail/tiles/Decoupler.java index d3a7157..c43061d 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/Decoupler.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/Decoupler.java @@ -13,7 +13,7 @@ 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.devices.Device; import de.srsoftware.web4rail.Protocol; import de.srsoftware.web4rail.tags.Fieldset; import de.srsoftware.web4rail.tags.Input; diff --git a/src/main/java/de/srsoftware/web4rail/tiles/Relay.java b/src/main/java/de/srsoftware/web4rail/tiles/Relay.java index d6a15af..25ebe72 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/Relay.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/Relay.java @@ -11,7 +11,7 @@ import org.json.JSONObject; import de.srsoftware.tools.Tag; import de.srsoftware.web4rail.Command; import de.srsoftware.web4rail.Command.Reply; -import de.srsoftware.web4rail.Device; +import de.srsoftware.web4rail.devices.Device; import de.srsoftware.web4rail.Protocol; import de.srsoftware.web4rail.tags.Fieldset; import de.srsoftware.web4rail.tags.Input; diff --git a/src/main/java/de/srsoftware/web4rail/tiles/Turnout.java b/src/main/java/de/srsoftware/web4rail/tiles/Turnout.java index 72fb9ff..9d97330 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/Turnout.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/Turnout.java @@ -13,7 +13,7 @@ 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.devices.Device; import de.srsoftware.web4rail.Protocol; import de.srsoftware.web4rail.moving.Train; import de.srsoftware.web4rail.tags.Fieldset;