diff --git a/pom.xml b/pom.xml
index 1bbaa0d..70b1264 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
4.0.0
de.srsoftware
web4rail
- 1.4.45
+ 1.4.46
Web4Rail
jar
Java Model Railway Control
diff --git a/resources/translations/Application.de.translation b/resources/translations/Application.de.translation
index dbf9b46..e3f3287 100644
--- a/resources/translations/Application.de.translation
+++ b/resources/translations/Application.de.translation
@@ -119,6 +119,7 @@ custom fields : benutzerdefinierte Felder
CustomFunction : benutzerdefinierte Funktion
Date/Time : Datum/Zeit
Decoder address : Decoder-Adresse
+Decoder function : Decoder-Funktion
decouple : Abkuppeln
decoupler : decoupler
Decoupler : Entkuppler
diff --git a/src/main/java/de/srsoftware/web4rail/Application.java b/src/main/java/de/srsoftware/web4rail/Application.java
index 2ebe68a..637ed0b 100644
--- a/src/main/java/de/srsoftware/web4rail/Application.java
+++ b/src/main/java/de/srsoftware/web4rail/Application.java
@@ -25,6 +25,7 @@ import de.srsoftware.tools.Tag;
import de.srsoftware.web4rail.actions.ActionList;
import de.srsoftware.web4rail.conditions.Condition;
import de.srsoftware.web4rail.devices.Decoder;
+import de.srsoftware.web4rail.functions.Function;
import de.srsoftware.web4rail.moving.Car;
import de.srsoftware.web4rail.moving.Locomotive;
import de.srsoftware.web4rail.moving.Train;
@@ -128,6 +129,8 @@ public class Application extends BaseClass{
return plan.controlUnit().process(params);
case REALM_DECODER:
return Decoder.action(params);
+ case REALM_FUNCTION:
+ return Function.action(params);
case REALM_HISTORY:
return History.action(params);
case REALM_LOCO:
diff --git a/src/main/java/de/srsoftware/web4rail/BaseClass.java b/src/main/java/de/srsoftware/web4rail/BaseClass.java
index b32ef4e..1bc5f17 100644
--- a/src/main/java/de/srsoftware/web4rail/BaseClass.java
+++ b/src/main/java/de/srsoftware/web4rail/BaseClass.java
@@ -25,6 +25,7 @@ import de.srsoftware.web4rail.Plan.Direction;
import de.srsoftware.web4rail.actions.Action;
import de.srsoftware.web4rail.conditions.Condition;
import de.srsoftware.web4rail.devices.Decoder;
+import de.srsoftware.web4rail.functions.Function;
import de.srsoftware.web4rail.moving.Car;
import de.srsoftware.web4rail.moving.Locomotive;
import de.srsoftware.web4rail.moving.Train;
@@ -569,10 +570,9 @@ public abstract class BaseClass implements Constants{
if (this instanceof Contact) return REALM_CONTACT;
if (this instanceof Decoder) return REALM_DECODER;
if (this instanceof Tile) return REALM_PLAN;
-
+ if (this instanceof Function) return REALM_FUNCTION;
if (this instanceof Locomotive) return REALM_LOCO;
- if (this instanceof Car) return REALM_CAR;
-
+ if (this instanceof Car) return REALM_CAR;
if (this instanceof Action) return REALM_ACTIONS;
if (this instanceof Condition) return REALM_CONDITION;
if (this instanceof Route) return REALM_ROUTE;
diff --git a/src/main/java/de/srsoftware/web4rail/Constants.java b/src/main/java/de/srsoftware/web4rail/Constants.java
index b6477c0..a1cf2cd 100644
--- a/src/main/java/de/srsoftware/web4rail/Constants.java
+++ b/src/main/java/de/srsoftware/web4rail/Constants.java
@@ -46,6 +46,7 @@ public interface Constants {
public static final String REALM_CONTACT = "contact";
public static final String REALM_CU = "cu";
public static final String REALM_DECODER = "decoder";
+ public static final String REALM_FUNCTION = "function";
public static final String REALM_HISTORY = "history";
public static final String REALM_LOCO = "loco";
public static final String REALM_MAINTENANCE = "maintenance";
@@ -64,6 +65,7 @@ public interface Constants {
public static final String DISABLED = "disabled";
public static final String DIRECTION = "direction";
public static final String FUNCTION = "function";
+ public static final String FUNCTIONS = "functions";
public static final String GITHUB_URL = "https://github.com/srsoftware-de/Web4Rail";
public static final String ID = "id";
public static final String LOCKED = "locked";
diff --git a/src/main/java/de/srsoftware/web4rail/devices/Decoder.java b/src/main/java/de/srsoftware/web4rail/devices/Decoder.java
index d6f9955..8bb1c65 100644
--- a/src/main/java/de/srsoftware/web4rail/devices/Decoder.java
+++ b/src/main/java/de/srsoftware/web4rail/devices/Decoder.java
@@ -17,6 +17,7 @@ import de.srsoftware.web4rail.Command.Reply;
import de.srsoftware.web4rail.Constants;
import de.srsoftware.web4rail.Params;
import de.srsoftware.web4rail.Protocol;
+import de.srsoftware.web4rail.functions.Function;
import de.srsoftware.web4rail.moving.Locomotive;
import de.srsoftware.web4rail.tags.Button;
import de.srsoftware.web4rail.tags.Fieldset;
@@ -46,7 +47,6 @@ public class Decoder extends BaseClass implements Constants, Device {
private String type;
private Locomotive loco;
private int numFunctions = 2;
- private HashSet enabledFunctions = new HashSet<>();
private int step;
private boolean reverse;
@@ -87,8 +87,13 @@ public class Decoder extends BaseClass implements Constants, Device {
}
public StringBuilder functions() {
+ HashSet enabledFunctions = new HashSet<>();
+ for (Function fun : loco.functions()) {
+ if (fun.enabled(this)) enabledFunctions.add(fun.index());
+ }
+
StringBuilder sb = new StringBuilder();
- for (int i=1; i<=numFunctions; i++) sb.append(" ").append(isEnabled(i)?1:0);
+ for (int i=1; i<=numFunctions; i++) sb.append(" ").append(enabledFunctions.contains(i)?1:0);
return sb;
}
@@ -125,17 +130,13 @@ public class Decoder extends BaseClass implements Constants, Device {
init = true;
}
- public boolean isEnabled(int function) {
- return enabledFunctions.contains(function);
- }
-
-
@Override
public JSONObject json() {
JSONObject json = super.json();
json.put(CVS, cvs);
json.put(PROTOCOL, proto);
json.put(TYPE, type);
+ json.put(FUNCTIONS, numFunctions);
return json;
}
@@ -154,6 +155,7 @@ public class Decoder extends BaseClass implements Constants, Device {
for (String key : jCvs.keySet()) cvs.put(Integer.parseInt(key),jCvs.getInt(key));
}
if (json.has(TYPE)) type = json.getString(TYPE);
+ if (json.has(FUNCTIONS)) numFunctions = json.getInt(FUNCTIONS);
return this;
}
@@ -258,12 +260,9 @@ public class Decoder extends BaseClass implements Constants, Device {
});
}
- public void queue(double speed, boolean reverse) {
- step = (int)(speed*proto.steps);
- this.reverse = reverse;
- queue();
+ public boolean reverse() {
+ return reverse;
}
-
public static Select selector(boolean freeOnly) {
Select selector = new Select(REALM_DECODER);
@@ -276,13 +275,7 @@ public class Decoder extends BaseClass implements Constants, Device {
}
return selector;
}
-
- public void setFunction(Integer function, boolean enabled) {
- if (enabled) {
- if (enabledFunctions.add(function)) queue();
- } else if (enabledFunctions.remove(function)) queue();
- }
-
+
public Decoder setLoco(Locomotive locomotive, boolean log) {
loco = locomotive;
if (log) addLogEntry(t("Mounted into \"{}\".",loco));
@@ -294,6 +287,12 @@ public class Decoder extends BaseClass implements Constants, Device {
this.proto = proto;
}
+ public void setSpeed(double speed, boolean reverse) {
+ step = (int)(speed*proto.steps);
+ this.reverse = reverse;
+ queue();
+ }
+
private Object setting(int cv) {
switch (cv) {
case 1:
@@ -317,11 +316,6 @@ public class Decoder extends BaseClass implements Constants, Device {
return "";
}
- public void toggleFunction(Integer index) {
- if (!enabledFunctions.remove(index)) enabledFunctions.add(index);
- queue();
- }
-
@Override
public String toString() {
return type()+" ("+t("Address")+": "+address()+")";
diff --git a/src/main/java/de/srsoftware/web4rail/functions/DirectedFunction.java b/src/main/java/de/srsoftware/web4rail/functions/DirectedFunction.java
new file mode 100644
index 0000000..1a2f48f
--- /dev/null
+++ b/src/main/java/de/srsoftware/web4rail/functions/DirectedFunction.java
@@ -0,0 +1,54 @@
+package de.srsoftware.web4rail.functions;
+
+import org.json.JSONObject;
+
+import de.srsoftware.web4rail.Params;
+import de.srsoftware.web4rail.devices.Decoder;
+import de.srsoftware.web4rail.tags.Checkbox;
+import de.srsoftware.web4rail.tags.Fieldset;
+
+public class DirectedFunction extends Function {
+
+ private boolean forward,reverse;
+
+ @Override
+ public boolean enabled(Decoder decoder) {
+ if (!super.enabled(decoder)) return false;
+ if (decoder.reverse()) {
+ return reverse;
+ } else return forward;
+ }
+
+ @Override
+ public Fieldset form(Decoder decoder) {
+ Fieldset fieldset = super.form(decoder);
+ String prefix = "functions/"+id()+"/";
+ new Checkbox(prefix+FORWARD, t(FORWARD), forward, true).addTo(fieldset);
+ new Checkbox(prefix+REVERSE, t(REVERSE), reverse, true).addTo(fieldset);
+ return fieldset;
+ }
+
+ @Override
+ public JSONObject json() {
+ JSONObject json = super.json();
+ if (forward) json.put(FORWARD, true);
+ if (reverse) json.put(REVERSE, true);
+ return json;
+ }
+
+ @Override
+ public DirectedFunction load(JSONObject json) {
+ super.load(json);
+ if (json.has(FORWARD)) forward = true;
+ if (json.has(REVERSE)) reverse = true;
+ return this;
+ }
+
+ @Override
+ public Object update(Params params) {
+ if (params.containsKey(FORWARD)) forward = "on".equals(params.get(FORWARD));
+ if (params.containsKey(REVERSE)) reverse = "on".equals(params.get(REVERSE));
+ return super.update(params);
+
+ }
+}
diff --git a/src/main/java/de/srsoftware/web4rail/functions/Function.java b/src/main/java/de/srsoftware/web4rail/functions/Function.java
index 7b00fe6..01ef4e3 100644
--- a/src/main/java/de/srsoftware/web4rail/functions/Function.java
+++ b/src/main/java/de/srsoftware/web4rail/functions/Function.java
@@ -2,8 +2,11 @@ package de.srsoftware.web4rail.functions;
import java.util.List;
+import org.json.JSONObject;
+
import de.srsoftware.tools.Tag;
import de.srsoftware.web4rail.BaseClass;
+import de.srsoftware.web4rail.Constants;
import de.srsoftware.web4rail.Params;
import de.srsoftware.web4rail.devices.Decoder;
import de.srsoftware.web4rail.tags.Fieldset;
@@ -18,23 +21,25 @@ public abstract class Function extends BaseClass{
static final String FORWARD = "forward";
static final String REVERSE = "reverse";
- private int decoderFunction = 1;
+ private int decoderFunction = 1;
+ private boolean enabled;
- public static Tag selector() {
- Select selector = new Select(NEW);
- selector.addOption("", t("Select function"));
-
- for (Class extends Function> fun : List.of(HeadLight.class,TailLight.class,InteriorLight.class,Coupler.class,CustomFunction.class)) {
- String className = fun.getSimpleName();
- selector.addOption(className,t(className));
+ public static Object action(Params params) {
+ String action = params.getString(ACTION);
+ Function function = BaseClass.get(Id.from(params));
+ BaseClass parent = isSet(function) ? function.parent() : null;
+ switch (action) {
+ case ACTION_DROP:
+ if (isSet(function)) {
+ function.remove();
+ return parent.properties();
+ }
}
-
- return selector;
+ String message = t("Unknown action: {}",params.get(Constants.ACTION));
+ return isSet(parent) ? parent.properties(message) : message;
}
public static Function create(String className) {
-
-
if (isNull(className)) return null;
try {
return (Function) Class.forName(PACKAGE+"."+className).getDeclaredConstructor().newInstance();
@@ -43,6 +48,10 @@ public abstract class Function extends BaseClass{
}
return null;
}
+
+ public boolean enabled(Decoder decoder) {
+ return enabled;
+ }
public Fieldset form(Decoder decoder) {
Fieldset fieldset = new Fieldset(name());
@@ -56,9 +65,53 @@ public abstract class Function extends BaseClass{
return fieldset;
}
+
+ public int index() {
+ return decoderFunction;
+ }
+
+ @Override
+ public JSONObject json() {
+ JSONObject json = super.json();
+ json.put(TYPE, type());
+ json.put(INDEX, decoderFunction);
+ return json;
+ }
+
+ @Override
+ public Function load(JSONObject json) {
+ super.load(json);
+ if (json.has(INDEX)) decoderFunction = json.getInt(INDEX);
+ return this;
+ }
+
+ public String name() {
+ return t(type());
+ }
+
+ public static Tag selector() {
+ Select selector = new Select(NEW);
+ selector.addOption("", t("Select function"));
+
+ for (Class extends Function> fun : List.of(HeadLight.class,TailLight.class,InteriorLight.class,Coupler.class,CustomFunction.class)) {
+ String className = fun.getSimpleName();
+ selector.addOption(className,t(className));
+ }
+
+ return selector;
+ }
- private String name() {
- return t(getClass().getSimpleName());
+ public void setState(boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ @Override
+ public String toString() {
+ return name()+"("+decoderFunction+"="+enabled+")";
+ }
+
+ private String type() {
+ return getClass().getSimpleName();
}
@Override
diff --git a/src/main/java/de/srsoftware/web4rail/functions/FunctionList.java b/src/main/java/de/srsoftware/web4rail/functions/FunctionList.java
index a3d62c4..9f62821 100644
--- a/src/main/java/de/srsoftware/web4rail/functions/FunctionList.java
+++ b/src/main/java/de/srsoftware/web4rail/functions/FunctionList.java
@@ -2,24 +2,46 @@ package de.srsoftware.web4rail.functions;
import java.util.HashSet;
+import org.json.JSONArray;
import org.json.JSONObject;
+import de.srsoftware.web4rail.Constants;
+import de.srsoftware.web4rail.moving.Locomotive;
+
/**
* @author Stephan Richter
*
*/
-public class FunctionList extends HashSet{
+public class FunctionList extends HashSet implements Constants{
private static final long serialVersionUID = 8013610745085726979L;
+ private HashSet enabledFunctions = new HashSet<>();
+
+ public boolean enabled(String name) {
+ return enabledFunctions.contains(name);
+ }
+
- public JSONObject json() {
- // TODO Auto-generated method stub
- return null;
+ public JSONArray json() {
+ JSONArray json = new JSONArray();
+ for (Function fun : this) json.put(fun.json());
+ return json;
}
- public void load(JSONObject jsonObject) {
- // TODO Auto-generated method stub
-
+ public void load(JSONArray arr, Locomotive loco) {
+ arr.forEach(o -> {
+ if (o instanceof JSONObject) load((JSONObject)o,loco);
+ });
}
+ private void load(JSONObject json, Locomotive loco) {
+ if (json.has(TYPE)) add(Function.create(json.getString(TYPE)).load(json).parent(loco));
+ }
+
+ public FunctionList toggle(String name) {
+ boolean enabled = !enabledFunctions.remove(name);
+ if (enabled) enabledFunctions.add(name);
+ stream().filter(fun -> name.equals(fun.name())).forEach(fun -> fun.setState(enabled));
+ return this;
+ }
}
diff --git a/src/main/java/de/srsoftware/web4rail/functions/HeadLight.java b/src/main/java/de/srsoftware/web4rail/functions/HeadLight.java
index f7c6265..0080048 100644
--- a/src/main/java/de/srsoftware/web4rail/functions/HeadLight.java
+++ b/src/main/java/de/srsoftware/web4rail/functions/HeadLight.java
@@ -1,32 +1,5 @@
package de.srsoftware.web4rail.functions;
-import de.srsoftware.web4rail.Params;
-import de.srsoftware.web4rail.devices.Decoder;
-import de.srsoftware.web4rail.tags.Checkbox;
-import de.srsoftware.web4rail.tags.Fieldset;
+public class HeadLight extends DirectedFunction {
-public class HeadLight extends Function {
-
- private boolean forward,reverse;
-
- public HeadLight() {
- // TODO Auto-generated constructor stub
- }
-
- @Override
- public Fieldset form(Decoder decoder) {
- Fieldset fieldset = super.form(decoder);
- String prefix = "functions/"+id()+"/";
- new Checkbox(prefix+FORWARD, t(FORWARD), forward).addTo(fieldset);
- new Checkbox(prefix+REVERSE, t(REVERSE), reverse).addTo(fieldset);
- return fieldset;
- }
-
- @Override
- public Object update(Params params) {
- if (params.containsKey(FORWARD)) forward = "on".equals(params.get(FORWARD));
- if (params.containsKey(REVERSE)) reverse = "on".equals(params.get(REVERSE));
- return super.update(params);
-
- }
}
diff --git a/src/main/java/de/srsoftware/web4rail/functions/TailLight.java b/src/main/java/de/srsoftware/web4rail/functions/TailLight.java
index f0844ee..4fc926a 100644
--- a/src/main/java/de/srsoftware/web4rail/functions/TailLight.java
+++ b/src/main/java/de/srsoftware/web4rail/functions/TailLight.java
@@ -1,5 +1,5 @@
package de.srsoftware.web4rail.functions;
-public class TailLight extends Function {
+public class TailLight extends DirectedFunction {
}
diff --git a/src/main/java/de/srsoftware/web4rail/moving/Locomotive.java b/src/main/java/de/srsoftware/web4rail/moving/Locomotive.java
index 2d4c3ad..5821f1d 100644
--- a/src/main/java/de/srsoftware/web4rail/moving/Locomotive.java
+++ b/src/main/java/de/srsoftware/web4rail/moving/Locomotive.java
@@ -6,6 +6,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Vector;
+import java.util.stream.Stream;
import org.json.JSONObject;
@@ -28,14 +29,8 @@ import de.srsoftware.web4rail.tags.Window;
import de.srsoftware.web4rail.tiles.Block;
public class Locomotive extends Car implements Constants{
- private static final String HEADLIGHT = "headlight";
- private static final String TAILLIGHT = "taillight";
- private static final String INTERIOR_LIGHT = "interior";
- private static final String COUPLER = "coupler";
-
public static final String LOCOMOTIVE = "locomotive";
private static final String ACTION_MAPPING = "mapping";
- private static final String FUNCTIONS = "functions";
private FunctionList functions = new FunctionList();
private int speed = 0;
//private TreeMap cvs = new TreeMap();
@@ -106,7 +101,7 @@ public class Locomotive extends Car implements Constants{
int speed = 0;
String realm = null;
Train train = null;
- Locomotive loco = null;
+ final Locomotive loco;
int maxSpeed = 0;
String id = null;
if (locoOrTrain instanceof Locomotive) {
@@ -121,6 +116,7 @@ public class Locomotive extends Car implements Constants{
speed = train.speed;
maxSpeed = train.maxSpeed();
id = "train_"+train.id();
+ loco = null;
} else return null;
HashMap params = new HashMap(Map.of(REALM,realm,ID,locoOrTrain.id()));
@@ -173,15 +169,12 @@ public class Locomotive extends Car implements Constants{
Tag functions = new Tag("p");
- if (isSet(loco) && isSet(loco.decoder)) {
-
- for (int i = 1; i<=loco.decoder.numFunctions(); i++) {
- params.put(ACTION, ACTION_TOGGLE_FUNCTION);
- params.put(FUNCTION,i);
- Button btn = new Button(loco.functionName(i),params);
- if (loco.decoder.isEnabled(i)) btn.clazz("active");
- btn.addTo(functions);
- }
+ if (isSet(loco)) {
+ loco.functionNames().forEach(name -> {
+ Button btn = loco.button(name, Map.of(ACTION,ACTION_TOGGLE_FUNCTION,FUNCTION,name));
+ if (loco.functions.enabled(name)) btn.clazz("active");
+ btn.addTo(functions);
+ });
}
if (isSet(train)) {
@@ -200,6 +193,10 @@ public class Locomotive extends Car implements Constants{
return fieldset;
}
+ public Decoder decoder() {
+ return decoder;
+ }
+
private String detail() {
return getClass().getSimpleName()+"("+name()+", "+decoder.protocol()+", "+decoder.address()+")";
}
@@ -216,27 +213,21 @@ public class Locomotive extends Car implements Constants{
new Input(ACTION, ACTION_MAPPING).hideIn(form);
new Input(ID,id()).hideIn(form);
- for (Function fun : functions) {
- fun.form(decoder).addTo(form);
- }
+ functions.stream()
+ .sorted((a,b) -> a.index() - b.index())
+ .forEach(fun -> fun.button(t("delete"), Map.of(ACTION,ACTION_DROP)).addTo(fun.form(decoder)).addTo(form));
Fieldset newFun = new Fieldset(t("Add function"));
Function.selector().addTo(newFun).addTo(form);
return new Button(t("Save"), form).addTo(form).addTo(fieldset);
}
-
-
- private Tag functionMapping(int index) {
- Fieldset mapping = new Fieldset(t("Function {}",index));
- return mapping;
- }
+ private Stream functionNames() {
+ return functions.stream().map(Function::name).sorted().distinct();
+ }
-
- private static String functionName(Object...parts) {
- StringBuilder sb = new StringBuilder(FUNCTIONS);
- for (Object part : parts) sb.append("/"+part);
- return sb.toString();
+ public FunctionList functions() {
+ return functions;
}
@Override
@@ -267,7 +258,7 @@ public class Locomotive extends Car implements Constants{
}
if (isSet(decoder)) decoder.setLoco(this,false);
- if (loco.has(FUNCTIONS)) functions.load(loco.getJSONObject(FUNCTIONS));
+ if (loco.has(FUNCTIONS)) functions.load(loco.getJSONArray(FUNCTIONS),this);
}
return this;
@@ -308,9 +299,6 @@ public class Locomotive extends Car implements Constants{
return win;
}
-
-
-
@Override
protected Window properties(List