diff --git a/pom.xml b/pom.xml index 7034606..08863d5 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 de.srsoftware web4rail - 1.4.2 + 1.4.3 Web4Rail jar Java Model Railway Control diff --git a/src/main/java/de/srsoftware/web4rail/Plan.java b/src/main/java/de/srsoftware/web4rail/Plan.java index 5bfe737..0580994 100644 --- a/src/main/java/de/srsoftware/web4rail/Plan.java +++ b/src/main/java/de/srsoftware/web4rail/Plan.java @@ -886,31 +886,38 @@ public class Plan extends BaseClass{ * @param data */ public synchronized void stream(String data) { - data = data.replaceAll("\n", "").replaceAll("\r", ""); - //if (!data.startsWith("heartbeat")) LOG.debug("streaming: {}",data); - Vector badClients = null; - for (Entry entry : clients.entrySet()) { - OutputStreamWriter client = entry.getKey(); - try { - client.write("data: "+data+"\n\n"); - client.flush(); - clients.put(client,0); - } catch (IOException e) { - int errorCount = entry.getValue()+1; - LOG.info("Error #{} on client: {}",errorCount,e.getMessage()); - if (errorCount > 4) { - if (isNull(badClients)) badClients = new Vector(); + String fixedData = data.replaceAll("\n", "").replaceAll("\r", ""); + new Thread("Plan") { + @Override + public void run() { + //if (!data.startsWith("heartbeat")) LOG.debug("streaming: {}",data); + Vector badClients = null; + for (Entry entry : clients.entrySet()) { + OutputStreamWriter client = entry.getKey(); try { - client.close(); - } catch (IOException e1) {} - badClients.add(client); - } else clients.put(client,errorCount); + client.write("data: "+fixedData+"\n\n"); + client.flush(); + clients.put(client,0); + } catch (IOException e) { + int errorCount = entry.getValue()+1; + LOG.info("Error #{} on client: {}",errorCount,e.getMessage()); + if (errorCount > 4) { + if (isNull(badClients)) badClients = new Vector(); + try { + client.close(); + } catch (IOException e1) {} + badClients.add(client); + } else clients.put(client,errorCount); + } + } + if (badClients != null) for (OutputStreamWriter client: badClients) { + LOG.info("Disconnecting client."); + clients.remove(client); + } + // TODO Auto-generated method stub } - } - if (badClients != null) for (OutputStreamWriter client: badClients) { - LOG.info("Disconnecting client."); - clients.remove(client); - } + }.start(); + } /** diff --git a/src/main/java/de/srsoftware/web4rail/Route.java b/src/main/java/de/srsoftware/web4rail/Route.java index d6df5fc..e8c77e5 100644 --- a/src/main/java/de/srsoftware/web4rail/Route.java +++ b/src/main/java/de/srsoftware/web4rail/Route.java @@ -232,9 +232,7 @@ public class Route extends BaseClass { } public Integer brakeTime(String brakeId) { - Integer result = brakeTimes.get(brakeId); - Collection values = brakeTimes.values(); - return values.isEmpty() ? BrakeProcess.defaultTimeStep : values.stream().mapToInt(Integer::intValue).sum()/values.size(); + return brakeTimes.get(brakeId); } public void brakeTime(String brakeId, Integer newTimeStep) { @@ -392,6 +390,7 @@ public class Route extends BaseClass { public void finish(Train train) { LOG.debug("{}.finish()",this); train.endRoute(endBlock,endDirection); + setSignals(Signal.RED); freeIgnoring(null); train = null; } diff --git a/src/main/java/de/srsoftware/web4rail/moving/Train.java b/src/main/java/de/srsoftware/web4rail/moving/Train.java index f1a920d..a3522ef 100644 --- a/src/main/java/de/srsoftware/web4rail/moving/Train.java +++ b/src/main/java/de/srsoftware/web4rail/moving/Train.java @@ -362,7 +362,7 @@ public class Train extends BaseClass implements Comparable { public String directedName(Direction dir) { String result = name(); - String mark = ""; //isSet(autopilot) ? "ⓐ" : ""; + String mark = autopilot ? "ⓐ" : ""; if (isNull(dir)) return result; switch (dir) { case NORTH: @@ -418,6 +418,10 @@ public class Train extends BaseClass implements Comparable { public void endRoute(Block endBlock, Direction endDirection) { BrakeProcess brake = endBrake(); + if (endBlock == destination) { + quitAutopilot(); + destination = null; + } if (isSet(brake)) brake.updateTime(); Integer waitTime = route.waitTime(); nextPreparedRoute = route.dropNextPreparedRoute(); @@ -721,11 +725,11 @@ public class Train extends BaseClass implements Comparable { public String quitAutopilot() { if (isSet(routePrepper)) routePrepper.stop(); - try { - return autopilot ? t("Autopilot disabled") : t("Autopilot already was disabled!"); - } finally { + if (autopilot) { autopilot = false; - } + if (isSet(currentBlock)) plan.place(currentBlock); + } + return null; } @Override @@ -826,7 +830,7 @@ public class Train extends BaseClass implements Comparable { if (isNull(tile)) return properties(t("Tile {} not known!",dest)); if (tile instanceof Block) { destination = (Block) tile; - start(false); + start(true); return t("{} now heading for {}",this,destination); } return properties(t("{} is not a block!",tile)); diff --git a/src/main/java/de/srsoftware/web4rail/threads/BrakeProcess.java b/src/main/java/de/srsoftware/web4rail/threads/BrakeProcess.java index 16fd781..9b53539 100644 --- a/src/main/java/de/srsoftware/web4rail/threads/BrakeProcess.java +++ b/src/main/java/de/srsoftware/web4rail/threads/BrakeProcess.java @@ -5,11 +5,14 @@ import org.slf4j.LoggerFactory; import de.srsoftware.web4rail.Application; import de.srsoftware.web4rail.BaseClass; +import de.srsoftware.web4rail.Route; import de.srsoftware.web4rail.moving.Train; public class BrakeProcess extends BaseClass implements Runnable{ private static final Logger LOG = LoggerFactory.getLogger(BrakeProcess.class); + + private static final int SPEED_STEP = 10; public static int defaultTimeStep = 500; private Train train; @@ -19,12 +22,31 @@ public class BrakeProcess extends BaseClass implements Runnable{ private int startSpeed; private int lastSpeed; private long lastTime; + + private Integer timeStep; + + private Route route; + + private String brakeId; public BrakeProcess(Train train) { - this.train = train; + this.train = train; + this.brakeId = train.brakeId(); + this.route = train.route(); new Thread(this, Application.threadName(this)).start(); } + private long calcDistance(Integer ts) { + long dist = 0; + int s = startSpeed; + while (s > Train.defaultEndSpeed) { + s -= SPEED_STEP; + dist += s*ts; + } + LOG.debug("Estimated distamce with {} ms timestep: {}",ts,dist); + return dist; + } + public BrakeProcess end() { LOG.debug("{}.end()",this); ended = true; @@ -33,14 +55,15 @@ public class BrakeProcess extends BaseClass implements Runnable{ @Override public void run() { - Integer delay = train.route().brakeTime(train.brakeId()); + timeStep = train.route().brakeTime(train.brakeId()); + if (timeStep == null) timeStep = defaultTimeStep; startSpeed = train.speed; lastTime = timestamp(); while (!train.hasNextPreparedRoute()) { - sleep(delay); + sleep(timeStep); lastSpeed = train.speed; updateDistance(); - if (lastSpeed > targetSpeed) lastSpeed -= 10; + if (lastSpeed > targetSpeed) lastSpeed -= SPEED_STEP; if (ended) break; if (lastSpeed <= targetSpeed && (ended = true)) lastSpeed = targetSpeed; train.setSpeed(lastSpeed); @@ -62,6 +85,23 @@ public class BrakeProcess extends BaseClass implements Runnable{ public void updateTime() { updateDistance(); LOG.debug("updateTime(): start speed was {} {}.",startSpeed,BaseClass.speedUnit); - // TODO + Integer newTimeStep = timeStep; + long calculated; + int step = 32*newTimeStep; + for (int i=0; i<20; i++) { + step = step/2; + if (step<1) step = 1; + calculated = calcDistance(newTimeStep); + LOG.debug("Calculated distance for step = {} ms: {}",newTimeStep,calculated); + LOG.debug("Update step: {}",step); + newTimeStep = newTimeStep + (calculated > distance ? -step : step); + } + + if (!newTimeStep.equals(timeStep)) { + route.brakeTime(brakeId,newTimeStep); + calculated = calcDistance(newTimeStep); + LOG.debug("Corrected brake timestep for {} @ {} from {} to {} ms.",train,route,timeStep,newTimeStep); + LOG.debug("Differemce from estimated distance: {} ({}%)",distance-calculated,100*(distance-calculated)/(float)distance); + } } } diff --git a/src/main/java/de/srsoftware/web4rail/threads/ControlUnit.java b/src/main/java/de/srsoftware/web4rail/threads/ControlUnit.java index 2aae71c..d4e29f5 100644 --- a/src/main/java/de/srsoftware/web4rail/threads/ControlUnit.java +++ b/src/main/java/de/srsoftware/web4rail/threads/ControlUnit.java @@ -318,7 +318,7 @@ public class ControlUnit extends Thread implements Constants{ case FEEDBACK: int addr = Integer.parseInt(parts[5]); boolean active = !parts[6].equals("0"); - new Thread(Application.threadName("CU.FeedBack("+addr+")")) { + new Thread(Application.threadName("CU.Feedback("+addr+")")) { @Override public void run() { plan.sensor(addr,active); diff --git a/src/main/java/de/srsoftware/web4rail/threads/RoutePrepper.java b/src/main/java/de/srsoftware/web4rail/threads/RoutePrepper.java index b6b1139..0807305 100644 --- a/src/main/java/de/srsoftware/web4rail/threads/RoutePrepper.java +++ b/src/main/java/de/srsoftware/web4rail/threads/RoutePrepper.java @@ -51,13 +51,13 @@ public class RoutePrepper extends BaseClass implements Runnable{ LOG.warn("{}→ {}.availableRoutes called without context.train!", inset, Train.class.getSimpleName()); if (error) return availableRoutes; - Block destination = train.destination(); if (isSet(startDirection)) { LOG.debug("{}- Looking for {}-bound routes from {}", inset, startDirection, block); } else { LOG.debug("{}- Looking for all routes from {}", inset, block); } - + + Block destination = train.destination(); if (isSet(destination) && visitedRoutes.isEmpty()) LOG.debug("{}- Destination: {}", inset, destination); for (Route routeCandidate : block.leavingRoutes()) { @@ -68,9 +68,9 @@ public class RoutePrepper extends BaseClass implements Runnable{ } HashSet stuckTrace = train.stuckTrace(); // if train has been stopped in between two blocks lastly: - // only allow routes that do not conflict with current train + // only allow starting routes that do not conflict with current train // position - if (isSet(stuckTrace) && !routeCandidate.path().containsAll(stuckTrace)) { + if (isSet(stuckTrace) && visitedRoutes.isEmpty() && !routeCandidate.path().containsAll(stuckTrace)) { LOG.debug("Stuck train occupies tiles ({}) outside of {} – not allowed.", stuckTrace, routeCandidate); continue; } diff --git a/src/main/java/de/srsoftware/web4rail/tiles/Contact.java b/src/main/java/de/srsoftware/web4rail/tiles/Contact.java index e36953f..cbf28c5 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/Contact.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/Contact.java @@ -84,17 +84,16 @@ public class Contact extends Tile{ } else { LOG.debug("{} activated.",this); state = true; + stream(); if (isSet(timer)) timer.abort(); Train train = train(); Context context = isSet(train) ? train.contact(this) : new Context(this); actions.fire(context,"Contact("+addr+")"); for (Listener listener : listeners) listener.fired("Contact("+addr+")"); - - stream(); } } - + public int addr() { return addr; } @@ -112,12 +111,7 @@ public class Contact extends Tile{ @Override public Object click(boolean shift) throws IOException { - if (!shift) new Thread(Application.threadName(this)) { - @Override - public void run() { - trigger(200); - } - }.start(); + if (!shift) trigger(200); return super.click(shift); } diff --git a/src/main/java/de/srsoftware/web4rail/tiles/Tile.java b/src/main/java/de/srsoftware/web4rail/tiles/Tile.java index 992243d..9157059 100644 --- a/src/main/java/de/srsoftware/web4rail/tiles/Tile.java +++ b/src/main/java/de/srsoftware/web4rail/tiles/Tile.java @@ -540,7 +540,11 @@ public abstract class Tile extends BaseClass implements Comparable { oneWay = null; } } - if ("on".equals(params.get(DISABLED))) status = Status.DISABLED; + if ("on".equals(params.get(DISABLED))) { + status = Status.DISABLED; + } else { + status = isSet(train) ? Status.OCCUPIED : Status.FREE; + } String len = params.get(LENGTH); if (isSet(len)) length(Integer.parseInt(len)); super.update(params);