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.
 
 
 
 

257 lines
6.9 KiB

package de.srsoftware.web4rail.tiles;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
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.devices.Device;
import de.srsoftware.web4rail.Protocol;
import de.srsoftware.web4rail.moving.Train;
import de.srsoftware.web4rail.tags.Fieldset;
import de.srsoftware.web4rail.tags.Input;
import de.srsoftware.web4rail.tags.Radio;
import de.srsoftware.web4rail.tags.Select;
import de.srsoftware.web4rail.tags.Window;
/**
* Base class for Turnouts
* @author Stephan Richter, SRSoftware
*
*/
public abstract class Turnout extends Tile implements Device{
private static final String PORT_A = "port_a";
private static final String PORT_B = "port_b";
public static final String STATE = "state";
protected static final String STRAIGHT = "straight";
protected int address = 0;
protected int delay = 400;
protected boolean error = false;
protected boolean initialized = false;
private Protocol protocol = Protocol.DCC128;
protected int portA = 0, portB = 1;
protected State state = State.STRAIGHT;
public enum State{
LEFT,STRAIGHT,RIGHT,UNDEF;
}
public int address() {
return address;
}
@Override
public Object click(boolean shift) throws IOException {
LOG.debug(getClass().getSimpleName()+".click()");
init();
return super.click(shift);
}
protected abstract String commandFor(State newState);
public void error(Reply reply) {
this.error = true;
try {
plan.stream(tag(null).toString());
} catch (IOException e) {
LOG.error("Was not able to stream: ",e);
}
throw new RuntimeException(reply.message());
}
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();
if (portA != 0) json.put(PORT_A, portA);
if (portB != 1) json.put(PORT_B, portB);
if (address != 0) json.put(ADDRESS, address);
json.put(PROTOCOL, protocol);
json.put(STATE, state);
return json;
}
@Override
public Tile load(JSONObject json) {
if (json.has(ADDRESS)) address = json.getInt(ADDRESS);
if (json.has(PORT_A)) portA = json.getInt(PORT_A);
if (json.has(PORT_B)) portB = json.getInt(PORT_B);
if (json.has(PROTOCOL)) protocol = Protocol.valueOf(json.getString(PROTOCOL));
if (json.has(STATE)) state = State.valueOf(json.getString(STATE));
return super.load(json);
}
@Override
protected Window properties(List<Fieldset> preForm, FormInput formInputs, List<Fieldset> postForm,String...errors) {
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());
return super.properties(preForm, formInputs, postForm,errors);
}
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(Turnout preselect, Collection<Turnout> exclude) {
Select selector = new Select(TURNOUT);
List<Turnout> turnouts = BaseClass.listElements(Turnout.class);
turnouts.sort((t1,t2) -> t1.x == t2.x ? t1.y - t2.y : t1.x - t2.x);
for (Turnout turnout : turnouts) {
if (isSet(exclude) && exclude.contains(turnout)) continue;
Tag option = selector.addOption(turnout.id(), turnout);
if (turnout == preselect) option.attr("selected", "selected");
}
return selector;
}
public State state() {
return state;
}
public Reply state(State newState,boolean shift) {
Train lockingTrain = lockingTrain();
if (isDisabled()) {
// shift allows to switch disabled turnouts...
if (newState != state && !shift) return new Reply(415, t("{} is disabled!",this));
} else {
if (isSet(lockingTrain)) {
// shift allows to switch locked turnouts...
if (newState != state && !shift) return new Reply(415, t("{} locked by {}!",this,lockingTrain));
} else if (shift) return new Reply(200,"OK"); // shift on a non-locked turnout skips the switch process
}
if (address == 0) {
sleep(300);
state = newState;
plan.place(this);
return new Reply(200,"OK");
}
Reply reply = init();
if (reply != null && !reply.succeeded()) return reply;
try {
return plan.queue(new Command(commandFor(newState)) {
@Override
public void onSuccess() {
super.onSuccess();
Turnout.this.state = newState;
plan.place(Turnout.this);
}
@Override
protected void onFailure(Reply reply) {
super.onFailure(reply);
plan.stream(t("Unable to switch \"{}\": {}",Turnout.this,reply.message()));
}
}).reply();
} catch (TimeoutException e) {
LOG.warn(e.getMessage());
}
return new Reply(417,t("Timeout while trying to switch {}.",this));
}
public abstract List<State> states();
public void success() {
this.error = false;
try {
plan.stream(tag(null).toString());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public Tag tag(Map<String, Object> replacements) throws IOException {
Tag tag = super.tag(replacements);
tag.clazz(tag.get("class")+(" "+state).toLowerCase()+(error?" error":""));
return tag;
}
@Override
public String title() {
return getClass().getSimpleName()+t("(Address: {}, Ports {} and {}) @ ({}, {})",address,portA,portB,x,y);
}
@Override
public Tile update(HashMap<String, String> 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_A);
if (isSet(newPort)) {
int npa = Integer.parseInt(newPort);
if (npa != portA) {
portA = npa;
initialized = false;
}
}
newPort = params.get(PORT_B);
if (isSet(newPort)) {
int npb = Integer.parseInt(newPort);
if (npb != portB) {
portB = npb;
initialized = false;
}
}
return super.update(params);
}
}