You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
331 lines
9.0 KiB
331 lines
9.0 KiB
package de.srsoftware.web4rail.moving; |
|
|
|
import java.io.IOException; |
|
import java.util.Comparator; |
|
import java.util.HashMap; |
|
import java.util.Map; |
|
import java.util.Vector; |
|
|
|
import org.json.JSONObject; |
|
|
|
import de.srsoftware.tools.Tag; |
|
import de.srsoftware.web4rail.Command; |
|
import de.srsoftware.web4rail.Constants; |
|
import de.srsoftware.web4rail.Device; |
|
import de.srsoftware.web4rail.Plan; |
|
import de.srsoftware.web4rail.Window; |
|
import de.srsoftware.web4rail.tags.Button; |
|
import de.srsoftware.web4rail.tags.Fieldset; |
|
import de.srsoftware.web4rail.Protocol; |
|
import de.srsoftware.web4rail.tags.Form; |
|
import de.srsoftware.web4rail.tags.Input; |
|
import de.srsoftware.web4rail.tags.Label; |
|
import de.srsoftware.web4rail.tags.Radio; |
|
import de.srsoftware.web4rail.tags.Table; |
|
|
|
public class Locomotive extends Car implements Constants,Device{ |
|
|
|
private static final String REVERSE = "reverse"; |
|
public static final String LOCOMOTIVE = "locomotive"; |
|
private static final int VMAX = 128; |
|
private boolean reverse = false; |
|
private Protocol proto = Protocol.DCC128; |
|
private int address = 3; |
|
private int speed = 0; |
|
private boolean f1,f2,f3,f4; |
|
private boolean init = false; |
|
|
|
public Locomotive(String name) { |
|
super(name); |
|
} |
|
|
|
public Locomotive(String name, Integer id) { |
|
super(name,id); |
|
} |
|
|
|
public static Object action(HashMap<String, String> params, Plan plan) throws IOException { |
|
String id = params.get(ID); |
|
Locomotive loco = id == null ? null : Locomotive.get(id); |
|
switch (params.get(ACTION)) { |
|
case ACTION_ADD: |
|
new Locomotive(params.get(Locomotive.NAME)).plan(plan); |
|
return Locomotive.manager(); |
|
case ACTION_FASTER10: |
|
return loco.faster(10); |
|
case ACTION_PROPS: |
|
return loco == null ? Locomotive.manager() : loco.properties(); |
|
case ACTION_SLOWER10: |
|
return loco.faster(-10); |
|
case ACTION_STOP: |
|
return loco.stop(); |
|
case ACTION_TOGGLE_F1: |
|
return loco.toggleFunction(1); |
|
case ACTION_TOGGLE_F2: |
|
return loco.toggleFunction(2); |
|
case ACTION_TOGGLE_F3: |
|
return loco.toggleFunction(3); |
|
case ACTION_TOGGLE_F4: |
|
return loco.toggleFunction(4); |
|
case ACTION_TURN: |
|
return loco.turn(); |
|
case ACTION_UPDATE: |
|
loco.update(params); |
|
return Locomotive.manager(); |
|
} |
|
|
|
return t("Unknown action: {}",params.get(ACTION)); |
|
} |
|
|
|
public static Tag cockpit(Object locoOrTrain) { |
|
String realm = null; |
|
int id = 0; |
|
int speed = 0; |
|
if (locoOrTrain instanceof Locomotive) { |
|
Locomotive loco = (Locomotive) locoOrTrain; |
|
realm = REALM_LOCO; |
|
id = loco.id(); |
|
speed = loco.speed; |
|
} else if (locoOrTrain instanceof Train) { |
|
Train train = (Train)locoOrTrain; |
|
realm = REALM_TRAIN; |
|
id = train.id; |
|
speed = train.speed; |
|
} |
|
|
|
HashMap<String,Object> params = new HashMap<String, Object>(Map.of(REALM,realm,ID,id)); |
|
|
|
Fieldset fieldset = new Fieldset(t("Control")); |
|
|
|
new Tag("span").content(t("Current velocity: {} km/h",speed)).addTo(fieldset); |
|
|
|
Tag par = new Tag("p"); |
|
Map.of("Slower (10 steps)",ACTION_SLOWER10,"Faster (10 steps)",ACTION_FASTER10).entrySet().forEach(e -> { |
|
params.put(ACTION, e.getValue()); |
|
new Button(t(e.getKey()),params).addTo(par); |
|
}); |
|
par.addTo(fieldset); |
|
|
|
Tag direction = new Tag("p"); |
|
Map.of("Turn",ACTION_TURN,"Stop",ACTION_STOP).entrySet().forEach(e -> { |
|
params.put(ACTION, e.getValue()); |
|
new Button(t(e.getKey()),params).clazz(e.getValue()).addTo(direction); |
|
}); |
|
direction.addTo(fieldset); |
|
|
|
Tag functions = new Tag("p"); |
|
Map.of("F1",ACTION_TOGGLE_F1,"F2",ACTION_TOGGLE_F2,"F3",ACTION_TOGGLE_F3,"F4",ACTION_TOGGLE_F4).entrySet().forEach(e -> { |
|
params.put(ACTION, e.getValue()); |
|
new Button(t(e.getKey()),params).addTo(functions); |
|
}); |
|
functions.addTo(fieldset); |
|
return fieldset.clazz("cockpit"); |
|
} |
|
|
|
private String detail() { |
|
return getClass().getSimpleName()+"("+name()+", "+proto+", "+address+")"; |
|
} |
|
|
|
|
|
public Object faster(int steps) { |
|
return setSpeed(speed + steps); |
|
} |
|
|
|
public static Locomotive get(Object id) { |
|
Car car = Car.get(id); |
|
if (car instanceof Locomotive) return (Locomotive) car; |
|
return null; |
|
} |
|
|
|
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: |
|
proto = "N 1 14 5"; break; // TODO: make configurable |
|
case DCC27: |
|
proto = "N 1 27 5"; break; // TODO: make configurable |
|
case DCC28: |
|
proto = "N 1 28 5"; break; // TODO: make configurable |
|
case DCC128: |
|
proto = "N 1 128 5"; break; // TODO: make configurable |
|
case SELECTRIX: |
|
proto = "S"; break; |
|
case ZIMO: |
|
proto = "Z"; 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(REVERSE, reverse); |
|
loco.put(PROTOCOL, proto); |
|
loco.put(ADDRESS, address); |
|
|
|
json.put(LOCOMOTIVE, loco); |
|
return json; |
|
} |
|
|
|
static Vector<Car> list() { |
|
Vector<Car> locos = new Vector<Car>(); |
|
for (Car car : Car.cars.values()) { |
|
if (car instanceof Locomotive) locos.add((Locomotive) car); |
|
} |
|
return locos; |
|
} |
|
|
|
@Override |
|
protected Car load(JSONObject json) { |
|
super.load(json); |
|
if (json.has(LOCOMOTIVE)) { |
|
JSONObject loco = json.getJSONObject(LOCOMOTIVE); |
|
if (loco.has(REVERSE)) reverse = loco.getBoolean(REVERSE); |
|
if (loco.has(PROTOCOL)) proto = Protocol.valueOf(loco.getString(PROTOCOL)); |
|
if (loco.has(ADDRESS)) address = loco.getInt(ADDRESS); |
|
} |
|
return this; |
|
} |
|
|
|
public static Window manager() { |
|
Window win = new Window("loco-manager", t("Locomotive manager")); |
|
new Tag("h4").content(t("known locomotives")).addTo(win); |
|
|
|
new Tag("p").content(t("Click on a name to edit the entry.")).addTo(win); |
|
|
|
Table table = new Table().addHead(t("Stock ID"),t("Name"),t("Protocol"),t("Address"),t("Length"),t("Tags")); |
|
cars.values() |
|
.stream() |
|
.filter(car -> car instanceof Locomotive) |
|
.map(car -> (Locomotive)car) |
|
.sorted(Comparator.comparing(loco -> loco.address)) |
|
.sorted(Comparator.comparing(loco -> loco.stockId)) |
|
.forEach(loco -> table.addRow(loco.stockId,loco.link(),loco.proto,loco.address,loco.length,String.join(", ", loco.tags()))); |
|
table.addTo(win); |
|
|
|
|
|
Form form = new Form("add-loco-form"); |
|
new Input(ACTION, ACTION_ADD).hideIn(form); |
|
new Input(REALM,REALM_LOCO).hideIn(form); |
|
Fieldset fieldset = new Fieldset(t("add new locomotive")); |
|
new Input(Locomotive.NAME, t("new locomotive")).addTo(new Label(t("Name:")+NBSP)).addTo(fieldset); |
|
new Button(t("Apply"),form).addTo(fieldset); |
|
fieldset.addTo(form).addTo(win); |
|
return win; |
|
} |
|
|
|
@Override |
|
public Window properties() { |
|
Window win = super.properties(); |
|
Tag cockpit = cockpit(this); |
|
win.children().insertElementAt(cockpit, 2); |
|
return win; |
|
} |
|
|
|
@Override |
|
public Form propertyForm() { |
|
Form form = super.propertyForm(); |
|
for (Tag tag : form.children()) { |
|
if (REALM.equals(tag.get(Input.NAME)) && REALM_CAR.equals(tag.get(Input.VALUE))) { |
|
tag.attr("value", REALM_LOCO); |
|
break; |
|
} |
|
} |
|
Fieldset fieldset = new Fieldset("Decoder settings"); |
|
Label protocol = new Label(t("Protocol:")); |
|
for (Protocol proto : Protocol.values()) { |
|
new Radio(PROTOCOL, proto.toString(), t(proto.toString()), proto == this.proto).addTo(protocol); |
|
} |
|
protocol.addTo(fieldset); |
|
new Input(ADDRESS, address).attr("type", "number").addTo(new Label(t("Address:"))).addTo(fieldset); |
|
fieldset.addTo(form); |
|
return form; |
|
} |
|
|
|
private void queue() { |
|
plan.queue(new Command("SET {} GL "+address+" "+(reverse?1:0)+" "+speed+" "+VMAX+" "+(f1?1:0)+" "+(f2?1:0)+" "+(f3?1:0)+" "+(f4?1:0)) { |
|
|
|
@Override |
|
public void onFailure(Reply reply) { |
|
super.onFailure(reply); |
|
plan.stream(t("Failed to send command to {}: {}",this,reply.message())); |
|
} |
|
}); |
|
} |
|
|
|
public String setSpeed(int newSpeed) { |
|
LOG.debug(this.detail()+".setSpeed({})",newSpeed); |
|
init(); |
|
speed = newSpeed; |
|
if (speed > 128) speed = 128; |
|
if (speed < 0) speed = 0; |
|
|
|
queue(); |
|
return t("Speed of {} set to {}.",this,speed); |
|
} |
|
|
|
public Object stop() { |
|
setSpeed(0); |
|
return t("Stopped {}",this); |
|
} |
|
|
|
private Object toggleFunction(int f) { |
|
boolean active; |
|
switch (f) { |
|
case 1: |
|
f1 =! f1; |
|
active = f1; |
|
break; |
|
case 2: |
|
f2 =! f2; |
|
active = f2; |
|
break; |
|
case 3: |
|
f3 =! f3; |
|
active = f3; |
|
break; |
|
case 4: |
|
f4 =! f4; |
|
active = f4; |
|
break; |
|
default: |
|
return t("Unknown function: {}",f); |
|
} |
|
queue(); |
|
return t("{} F{}",t(active?"Activated":"Deavtivated"),f); |
|
} |
|
|
|
public Object turn() { |
|
reverse = !reverse; |
|
stop(); |
|
return t("Stopped and reversed {}.",this); |
|
} |
|
|
|
@Override |
|
public Car update(HashMap<String, String> params) { |
|
super.update(params); |
|
if (params.containsKey(PROTOCOL)) proto = Protocol.valueOf(params.get(PROTOCOL)); |
|
if (params.containsKey(ADDRESS)) address = Integer.parseInt(params.get(ADDRESS)); |
|
return this; |
|
} |
|
}
|
|
|