working on autorouting and turnout code
This commit is contained in:
@@ -175,9 +175,9 @@ h2{
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
background: lime;
|
||||
position: absolute;
|
||||
position: fixed;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
left: 10px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -206,3 +206,7 @@ fieldset{
|
||||
border: 1px solid black;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.error{
|
||||
background: red;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
const ADD = 'add';
|
||||
const MOVE = 'move';
|
||||
const SQUARE = 30;
|
||||
const BODY = 'body';
|
||||
const CU = 'cu';
|
||||
const DIV = 'DIV';
|
||||
const SVG = 'svg';
|
||||
const MOVE = 'move';
|
||||
const OPAC = 100;
|
||||
const PLAN = '#plan';
|
||||
const POST = 'POST';
|
||||
const PROPS = 'props';
|
||||
const CU = 'cu';
|
||||
const OPAC = 100;
|
||||
const SQUARE = 30;
|
||||
const SVG = 'svg';
|
||||
var selected = null;
|
||||
var mode = null;
|
||||
var messageTimer = null;
|
||||
@@ -157,6 +157,7 @@ function request(data){
|
||||
data : data,
|
||||
success: function(resp){
|
||||
if (data.realm != 'car' && data.realm != 'loco') closeWindows();
|
||||
if (resp.startsWith('<html')) return;
|
||||
if (resp.startsWith('<svg')){
|
||||
$(PLAN).append($(resp));
|
||||
} else if (resp.startsWith('<')) {
|
||||
@@ -187,12 +188,17 @@ function runAction(ev){
|
||||
|
||||
function stream(ev){
|
||||
var data = ev.data;
|
||||
//console.log("received: ",data);
|
||||
console.log("received: ",data);
|
||||
if (data.startsWith('<svg')) {
|
||||
$(PLAN).append($(data));
|
||||
return false;
|
||||
}
|
||||
if (data.startsWith("heartbeat")) return heartbeat(data);
|
||||
if (data.startsWith("place ")) return place(data.substring(6));
|
||||
if (data.startsWith("remove")) return remove(data.substring(7));
|
||||
if (data.startsWith("addclass")) return addClass(data.substring(9));
|
||||
if (data.startsWith("dropclass")) return dropClass(data.substring(10));
|
||||
addMessage(data);
|
||||
}
|
||||
|
||||
function swapTiling(ev){
|
||||
|
||||
@@ -71,8 +71,8 @@ public class Application implements Constants{
|
||||
return plan.action(params);
|
||||
case REALM_ROUTE:
|
||||
return plan.routeAction(params);
|
||||
case REALM_TRAIN:
|
||||
return Train.action(params);
|
||||
case REALM_TRAIN:
|
||||
return Train.action(params,plan);
|
||||
}
|
||||
|
||||
return t("Unknown realm: {}",params.get(REALM));
|
||||
|
||||
@@ -250,7 +250,7 @@ public class Plan implements Constants{
|
||||
LOG.warn("Was not able to load cars!",e);
|
||||
}
|
||||
try {
|
||||
Train.loadAll(filename+".trains");
|
||||
Train.loadAll(filename+".trains",plan);
|
||||
} catch (Exception e) {
|
||||
LOG.warn("Was not able to load trains!",e);
|
||||
}
|
||||
@@ -437,7 +437,7 @@ public class Plan implements Constants{
|
||||
|
||||
public synchronized void stream(String data) {
|
||||
data = data.replaceAll("\n", "").replaceAll("\r", "");
|
||||
//LOG.debug("streaming: {}",data);
|
||||
//if (!data.startsWith("heartbeat")) LOG.debug("streaming: {}",data);
|
||||
Vector<OutputStreamWriter> badClients = null;
|
||||
for (Entry<OutputStreamWriter, Integer> entry : clients.entrySet()) {
|
||||
OutputStreamWriter client = entry.getKey();
|
||||
|
||||
@@ -308,7 +308,8 @@ public class Route implements Constants{
|
||||
for (Entry<Turnout, State> entry : turnouts.entrySet()) {// try to switch all turnouts of this route
|
||||
CompletableFuture<Reply> reply = entry.getKey().state(entry.getValue()); // switching a turnout is an asynchronous process, so it returns a CompletableFuture here
|
||||
promise = promise == null ? reply : promise.thenCombine(reply, (a,b) -> a);
|
||||
}
|
||||
}
|
||||
if (promise == null) promise = CompletableFuture.completedFuture(null);
|
||||
promise.exceptionally(ex -> {
|
||||
for (Tile tile : locked) try {
|
||||
tile.unlock();
|
||||
|
||||
@@ -12,7 +12,6 @@ import java.util.HashSet;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Random;
|
||||
import java.util.Vector;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import org.json.JSONObject;
|
||||
import org.slf4j.Logger;
|
||||
@@ -22,6 +21,7 @@ import de.keawe.tools.translations.Translation;
|
||||
import de.srsoftware.tools.Tag;
|
||||
import de.srsoftware.web4rail.Application;
|
||||
import de.srsoftware.web4rail.Constants;
|
||||
import de.srsoftware.web4rail.Plan;
|
||||
import de.srsoftware.web4rail.Plan.Direction;
|
||||
import de.srsoftware.web4rail.Route;
|
||||
import de.srsoftware.web4rail.Window;
|
||||
@@ -98,6 +98,8 @@ public class Train implements Constants {
|
||||
|
||||
public int speed = 0;
|
||||
private Autopilot autopilot = null;
|
||||
|
||||
private Plan plan;
|
||||
|
||||
public Train(Locomotive loco) {
|
||||
this(loco,null);
|
||||
@@ -110,15 +112,15 @@ public class Train implements Constants {
|
||||
trains.put(id, this);
|
||||
}
|
||||
|
||||
public static Object action(HashMap<String, String> params) throws IOException {
|
||||
public static Object action(HashMap<String, String> params, Plan plan) throws IOException {
|
||||
String action = params.get(ACTION);
|
||||
if (action == null) return t("No action passed to Train.action!");
|
||||
if (!params.containsKey(Train.ID)) {
|
||||
switch (action) {
|
||||
case ACTION_PROPS:
|
||||
return manager();
|
||||
case ACTION_ADD:
|
||||
return create(params);
|
||||
case ACTION_ADD:
|
||||
return create(params,plan);
|
||||
}
|
||||
return t("No train id passed!");
|
||||
}
|
||||
@@ -140,10 +142,10 @@ public class Train implements Constants {
|
||||
return t("Unknown action: {}",params.get(ACTION));
|
||||
}
|
||||
|
||||
private static Object create(HashMap<String, String> params) {
|
||||
private static Object create(HashMap<String, String> params, Plan plan) {
|
||||
Locomotive loco = (Locomotive) Locomotive.get(params.get(Train.LOCO_ID));
|
||||
if (loco == null) return t("unknown locomotive: {}",params.get(ID));
|
||||
Train train = new Train(loco);
|
||||
Train train = new Train(loco).plan(plan);
|
||||
if (params.containsKey(NAME)) train.name(params.get(NAME));
|
||||
return train;
|
||||
}
|
||||
@@ -215,7 +217,7 @@ public class Train implements Constants {
|
||||
return trains.values();
|
||||
}
|
||||
|
||||
public static void loadAll(String filename) throws IOException {
|
||||
public static void loadAll(String filename, Plan plan) throws IOException {
|
||||
BufferedReader file = new BufferedReader(new FileReader(filename));
|
||||
String line = file.readLine();
|
||||
while (line != null) {
|
||||
@@ -224,18 +226,19 @@ public class Train implements Constants {
|
||||
long id = json.getLong(ID);
|
||||
|
||||
Train train = new Train(null,id);
|
||||
train.load(json);
|
||||
train.load(json).plan(plan);
|
||||
|
||||
line = file.readLine();
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
|
||||
private void load(JSONObject json) {
|
||||
private Train load(JSONObject json) {
|
||||
pushPull = json.getBoolean(PUSH_PULL);
|
||||
if (json.has(NAME)) name = json.getString(NAME);
|
||||
for (Object id : json.getJSONArray(CARS)) add(Car.get((String)id));
|
||||
for (Object id : json.getJSONArray(LOCOS)) add((Locomotive) Car.get((String)id));
|
||||
return this;
|
||||
}
|
||||
|
||||
public static Object manager() {
|
||||
@@ -277,7 +280,12 @@ public class Train implements Constants {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
private Train plan(Plan plan) {
|
||||
this.plan = plan;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Tag props() {
|
||||
Window window = new Window("train-properties",t("Properties of {}",getClass().getSimpleName()));
|
||||
|
||||
@@ -310,6 +318,9 @@ public class Train implements Constants {
|
||||
actions.addTo(list);
|
||||
|
||||
}
|
||||
if (route != null) {
|
||||
new Tag("li").content(t("Current route: {}",route)).addTo(list);
|
||||
}
|
||||
if (direction != null) new Tag("li").content(t("Direction: heading {}",direction)).addTo(list);
|
||||
|
||||
|
||||
@@ -332,8 +343,8 @@ public class Train implements Constants {
|
||||
this.speed = v;
|
||||
}
|
||||
|
||||
public CompletableFuture<String> start() throws IOException {
|
||||
if (block == null) return CompletableFuture.failedFuture(new RuntimeException(t("{} not in a block",this)));
|
||||
public String start() throws IOException {
|
||||
if (block == null) return t("{} not in a block",this);
|
||||
if (route != null) route.unlock().setSignals(Signal.STOP);
|
||||
HashSet<Route> routes = block.routes();
|
||||
Vector<Route> availableRoutes = new Vector<Route>();
|
||||
@@ -352,9 +363,10 @@ public class Train implements Constants {
|
||||
availableRoutes.add(rt);
|
||||
}
|
||||
Random rand = new Random();
|
||||
if (availableRoutes.isEmpty()) return CompletableFuture.failedFuture(new RuntimeException(t("No free routes from {}",block)));
|
||||
if (availableRoutes.isEmpty()) return t("No free routes from {}",block);
|
||||
route = availableRoutes.get(rand.nextInt(availableRoutes.size()));
|
||||
return route.lock(this).thenApply(reply -> {
|
||||
|
||||
route.lock(this).thenApply(reply -> {
|
||||
try {
|
||||
route.setSignals(null);
|
||||
if (direction != route.startDirection) turn();
|
||||
@@ -363,7 +375,13 @@ public class Train implements Constants {
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}).thenAccept(message -> plan.stream(message))
|
||||
.exceptionally(ex -> {
|
||||
plan.stream(ex.getMessage());
|
||||
throw new RuntimeException(ex);
|
||||
});
|
||||
|
||||
return t("Trying to start {}",this);
|
||||
}
|
||||
|
||||
private Object stop() {
|
||||
|
||||
@@ -274,7 +274,7 @@ public abstract class Tile implements Constants{
|
||||
.id((x!=-1 && y!=-1)?(id()):(getClass().getSimpleName()))
|
||||
.clazz(classes())
|
||||
.size(100,100)
|
||||
.attr("name", getClass().getSimpleName())
|
||||
.attr("name", getClass().getSimpleName())
|
||||
.attr("viewbox", "0 0 "+width+" "+height);
|
||||
if (x>-1) style="left: "+(30*x)+"px; top: "+(30*y)+"px;";
|
||||
if (len()>1) style+=" width: "+(30*len())+"px;";
|
||||
@@ -322,10 +322,16 @@ public abstract class Tile implements Constants{
|
||||
.content("?")
|
||||
.addTo(svg);
|
||||
}
|
||||
|
||||
String title = title();
|
||||
if (title!=null) new Tag("title").content(title()).addTo(svg);
|
||||
|
||||
return svg;
|
||||
}
|
||||
|
||||
public String title() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return t("{}({},{})",getClass().getSimpleName(),x,y) ;
|
||||
|
||||
@@ -24,9 +24,10 @@ public abstract class Turnout extends Tile implements Device{
|
||||
|
||||
private Protocol protocol = Protocol.DCC128;
|
||||
protected int address = 0;
|
||||
protected int portA = 0, portB = 0;
|
||||
protected int portA = 0, portB = 1;
|
||||
protected int delay = 400;
|
||||
protected boolean initialized;
|
||||
protected boolean initialized = false;
|
||||
protected boolean error = false;
|
||||
|
||||
public enum State{
|
||||
LEFT,STRAIGHT,RIGHT,UNDEF;
|
||||
@@ -36,11 +37,21 @@ public abstract class Turnout extends Tile implements Device{
|
||||
|
||||
@Override
|
||||
public Object click() throws IOException {
|
||||
LOG.debug("Turnout.click()");
|
||||
LOG.debug(getClass().getSimpleName()+".click()");
|
||||
init();
|
||||
return super.click();
|
||||
}
|
||||
|
||||
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 void init() {
|
||||
if (!initialized) {
|
||||
plan.queue("INIT {} GA "+address+" "+proto());
|
||||
@@ -52,7 +63,7 @@ public abstract class Turnout extends Tile implements Device{
|
||||
public JSONObject json() {
|
||||
JSONObject json = super.json();
|
||||
if (portA != 0) json.put(PORT_A, portA);
|
||||
if (portB != 0) json.put(PORT_B, portB);
|
||||
if (portB != 1) json.put(PORT_B, portB);
|
||||
if (address != 0) json.put(ADDRESS, address);
|
||||
json.put(PROTOCOL, protocol);
|
||||
return json;
|
||||
@@ -101,14 +112,28 @@ public abstract class Turnout extends Tile implements Device{
|
||||
}
|
||||
|
||||
public abstract CompletableFuture<Reply> state(State newState) throws IOException;
|
||||
|
||||
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());
|
||||
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);
|
||||
}
|
||||
|
||||
public void toggle() {
|
||||
state = State.STRAIGHT;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ public class TurnoutL extends Turnout {
|
||||
|
||||
@Override
|
||||
public Object click() throws IOException {
|
||||
LOG.debug("TurnoutL.click()");
|
||||
Object o = super.click();
|
||||
if (route != null) {
|
||||
plan.stream(t("{} is locked by {}!",this,route));
|
||||
@@ -45,13 +44,6 @@ public class TurnoutL extends Turnout {
|
||||
return form;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tile update(HashMap<String, String> params) throws IOException {
|
||||
if (params.containsKey(STRAIGHT)) portA = Integer.parseInt(params.get(STRAIGHT));
|
||||
if (params.containsKey(LEFT)) portB = Integer.parseInt(params.get(LEFT));
|
||||
return super.update(params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Reply> state(State newState) throws IOException {
|
||||
init();
|
||||
@@ -68,10 +60,18 @@ public class TurnoutL extends Turnout {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
return result.thenApply(reply -> {
|
||||
LOG.debug("{} received {}",TurnoutL.this,reply);
|
||||
if (!reply.is(200)) throw new RuntimeException(reply.message());
|
||||
LOG.debug("{} received {}",getClass().getSimpleName(),reply);
|
||||
if (!reply.is(200)) error(reply);
|
||||
state = newState;
|
||||
success();
|
||||
return reply;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tile update(HashMap<String, String> params) throws IOException {
|
||||
if (params.containsKey(STRAIGHT)) portA = Integer.parseInt(params.get(STRAIGHT));
|
||||
if (params.containsKey(LEFT)) portB = Integer.parseInt(params.get(LEFT));
|
||||
return super.update(params);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ public class TurnoutR extends Turnout {
|
||||
|
||||
@Override
|
||||
public Object click() throws IOException {
|
||||
LOG.debug("Turnoutr.click()");
|
||||
Object o = super.click();
|
||||
if (route != null) {
|
||||
plan.stream(t("{} is locked by {}!",this,route));
|
||||
@@ -34,8 +33,8 @@ public class TurnoutR extends Turnout {
|
||||
break;
|
||||
}
|
||||
}
|
||||
new Input(STRAIGHT, portA).addTo(new Label(t("Straight port"))).addTo(fieldset);
|
||||
new Input(RIGHT, portB).addTo(new Label(t("Right port"))).addTo(fieldset);
|
||||
new Input(STRAIGHT, portA).numeric().addTo(new Label(t("Straight port"))).addTo(fieldset);
|
||||
new Input(RIGHT, portB).numeric().addTo(new Label(t("Right port"))).addTo(fieldset);
|
||||
return form;
|
||||
}
|
||||
|
||||
@@ -62,8 +61,10 @@ public class TurnoutR extends Turnout {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
return result.thenApply(reply -> {
|
||||
LOG.debug("{} received {}",reply);
|
||||
if (reply.is(200)) state = newState;
|
||||
LOG.debug("{} received {}",getClass().getSimpleName(),reply);
|
||||
if (!reply.is(200)) error(reply);
|
||||
state = newState;
|
||||
success();
|
||||
return reply;
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user