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.
 
 
 
 

335 lines
9.5 KiB

package de.srsoftware.web4rail.moving;
import java.io.IOException;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.json.JSONObject;
import de.srsoftware.tools.Tag;
import de.srsoftware.web4rail.BaseClass;
import de.srsoftware.web4rail.Command;
import de.srsoftware.web4rail.Constants;
import de.srsoftware.web4rail.Device;
import de.srsoftware.web4rail.Plan;
import de.srsoftware.web4rail.Protocol;
import de.srsoftware.web4rail.Window;
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.Label;
import de.srsoftware.web4rail.tags.Radio;
import de.srsoftware.web4rail.tags.Table;
import de.srsoftware.web4rail.tiles.Block;
public class Locomotive extends Car implements Constants,Device{
private static final String REVERSE = "reverse";
public static final String LOCOMOTIVE = "locomotive";
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, Id 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 : BaseClass.get(new Id(id));
switch (params.get(ACTION)) {
case ACTION_ADD:
new Locomotive(params.get(Locomotive.NAME)).parent(plan).register();
return Locomotive.manager();
case ACTION_FASTER10:
return loco.faster(10);
case ACTION_MOVE:
return loco.moveUp();
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));
}
@Override
public int address() {
return address;
}
public static Fieldset cockpit(Object locoOrTrain) {
Id id = null;
int speed = 0;
String realm = null;
Train train = null;
Locomotive loco = null;
if (locoOrTrain instanceof Locomotive) {
loco = (Locomotive) locoOrTrain;
realm = REALM_LOCO;
id = loco.id();
speed = loco.speed;
} else if (locoOrTrain instanceof 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"));
fieldset.clazz("cockpit");
new Tag("span").content(t("Current velocity: {} {}",speed,speedUnit)).addTo(fieldset);
Tag par = new Tag("p");
Map.of(t("Slower (10 {})",speedUnit),ACTION_SLOWER10,t("Faster (10 {})",speedUnit),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");
if ((isSet(train) && train.speed > 0) || (isSet(loco) && loco.speed > 0)) {
params.put(ACTION, ACTION_STOP);
new Button(t("Stop"),params).clazz(ACTION_STOP).addTo(direction);
}
params.put(ACTION, ACTION_TURN);
new Button(t("Turn"),params).clazz(ACTION_TURN).addTo(direction);
if (isSet(train)) {
Block currentBlock = train.currentBlock();
if (isSet(currentBlock)) {
if (isNull(train.route)) {
params.put(ACTION, ACTION_START);
new Button(t("start"),params).addTo(direction);
}
if (train.usesAutopilot()) {
params.put(ACTION, ACTION_QUIT);
new Button(t("quit autopilot"),params).addTo(direction);
} else {
params.put(ACTION, ACTION_AUTO);
new Button(t("auto"),params).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;
}
private String detail() {
return getClass().getSimpleName()+"("+name()+", "+proto+", "+address+")";
}
public Object faster(int steps) {
setSpeed(speed + steps);
return properties();
}
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(REVERSE, orientation);
loco.put(PROTOCOL, proto);
loco.put(ADDRESS, address);
json.put(LOCOMOTIVE, loco);
return json;
}
@Override
public Car load(JSONObject json) {
super.load(json);
if (json.has(LOCOMOTIVE)) {
JSONObject loco = json.getJSONObject(LOCOMOTIVE);
if (loco.has(REVERSE)) orientation = 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("Max. Speed",speedUnit),t("Protocol"),t("Address"),t("Length"),t("Tags"));
List<Locomotive> locos = BaseClass.listElements(Locomotive.class);
locos.sort(Comparator.comparing(loco -> loco.address));
locos.sort(Comparator.comparing(loco -> loco.stockId));
for (Locomotive loco : locos) {
String maxSpeed = (loco.maxSpeedForward == 0 ? "–":""+loco.maxSpeedForward)+NBSP;
if (loco.maxSpeedReverse != loco.maxSpeedForward) maxSpeed += "("+loco.maxSpeedReverse+")"+NBSP;
table.addRow(loco.stockId,loco.link(),maxSpeed+speedUnit,loco.proto,loco.address,loco.length+NBSP+lengthUnit,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
protected Window properties(List<Fieldset> preForm, FormInput formInputs, List<Fieldset> postForm) {
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);
}
formInputs.add(t("Protocol"),div);
formInputs.add(t("Address"),new Input(ADDRESS, address).numeric());
return super.properties(preForm, formInputs, postForm);
}
private void queue() {
int step = proto.steps * speed / (maxSpeedForward == 0 ? 100 : maxSpeedForward);
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)) {
@Override
public void onFailure(Reply reply) {
super.onFailure(reply);
plan.stream(t("Failed to send command to {}: {}",this,reply.message()));
}
});
}
/**
* Sets the speed of the locomotive to the given velocity in [plan.speedUnit]s
* @param newSpeed
* @return
*/
public String setSpeed(int newSpeed) {
LOG.debug(this.detail()+".setSpeed({})",newSpeed);
init();
speed = newSpeed;
if (speed > maxSpeedForward && maxSpeedForward > 0) speed = maxSpeed();
if (speed < 0) speed = 0;
queue();
return t("Speed of {} set to {}.",this,speed);
}
public Object stop() {
setSpeed(0);
return properties();
}
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 String turn() {
stop();
super.turn();
return t("Stopped and reversed {}.",this);
}
@Override
protected Window 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 properties();
}
}