overhauled brake time processor
This commit is contained in:
2
pom.xml
2
pom.xml
@@ -4,7 +4,7 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>de.srsoftware</groupId>
|
||||
<artifactId>web4rail</artifactId>
|
||||
<version>1.3.4</version>
|
||||
<version>1.3.5</version>
|
||||
<name>Web4Rail</name>
|
||||
<packaging>jar</packaging>
|
||||
<description>Java Model Railway Control</description>
|
||||
|
||||
@@ -502,6 +502,10 @@ public abstract class BaseClass implements Constants{
|
||||
return Translation.get(Application.class, txt, fills);
|
||||
}
|
||||
|
||||
public static long timestamp() {
|
||||
return new Date().getTime();
|
||||
}
|
||||
|
||||
public BaseClass unregister() {
|
||||
return registry.remove(this.id());
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import java.io.BufferedWriter;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
@@ -77,27 +76,31 @@ public class Route extends BaseClass {
|
||||
|
||||
private static final String ROUTE_SETUP = "route_setup";
|
||||
|
||||
private int startSpeed;
|
||||
private static HashMap<Id, String> names = new HashMap<Id, String>(); // maps id to name. needed to keep names during plan.analyze()
|
||||
private int startSpeed;
|
||||
private static HashMap<Id, String> names = new HashMap<Id, String>(); // maps id to name. needed to keep names during plan.analyze()
|
||||
|
||||
private class BrakeProcessor extends Thread {
|
||||
private class BrakeProcessor extends Thread {
|
||||
private long timestamp;
|
||||
private static final int SPEED_STEP = 5;
|
||||
private Integer timeStep;
|
||||
private Route route;
|
||||
private Train train;
|
||||
private boolean aborted = false;
|
||||
private String brakeId;
|
||||
private long estimatedDistance; // Unit: s*km/h "km/h-Sekunden"
|
||||
|
||||
public BrakeProcessor(Route route, Train train) {
|
||||
this.train = train;
|
||||
this.route = route;
|
||||
|
||||
startSpeed = train.speed;
|
||||
|
||||
estimatedDistance = 0;
|
||||
brakeId = train.brakeId();
|
||||
startSpeed = train.speed;
|
||||
|
||||
timeStep = brakeTimes.get(brakeId);
|
||||
// if no brake time is available for this train, use the fastest brake time already known for this route:
|
||||
if (isNull(timeStep)) timeStep = route.brakeTimes.values().stream().min(Integer::compare).orElse(100);
|
||||
if (isNull(timeStep)) timeStep = 100;
|
||||
start();
|
||||
}
|
||||
|
||||
@@ -106,19 +109,35 @@ public class Route extends BaseClass {
|
||||
train.setSpeed(startSpeed);
|
||||
}
|
||||
|
||||
private long calcDistance(Integer ts) {
|
||||
long dist = 0;
|
||||
int s = startSpeed;
|
||||
while (s > endSpeed) {
|
||||
s -= SPEED_STEP;
|
||||
dist += s*ts;
|
||||
}
|
||||
LOG.debug("Estimated distamce with {} ms timestep: {}",ts,dist);
|
||||
return dist;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called from route.finish when train came to stop
|
||||
*/
|
||||
public void finish() {
|
||||
long timestamp2 = new Date().getTime();
|
||||
//int remainingSpeed = train.speed;
|
||||
LOG.debug("BrakeProcessor.finish(){}",aborted?" got aborted":"");
|
||||
if (aborted) return;
|
||||
long runtime = timestamp2 - timestamp;
|
||||
int quotient = startSpeed - endSpeed;
|
||||
if (quotient<1) quotient = 1;
|
||||
int newTimeStep = 5*(int) runtime/quotient;
|
||||
|
||||
int diff = newTimeStep - timeStep;
|
||||
int absDiff = diff < 0 ? -diff : diff;
|
||||
if (absDiff > timeStep/4) absDiff=timeStep/4;
|
||||
newTimeStep = diff < 0 ? timeStep - absDiff : timeStep + absDiff;
|
||||
long runtime = BaseClass.timestamp() - timestamp;
|
||||
estimatedDistance += train.speed * runtime;
|
||||
train.setSpeed(0);
|
||||
LOG.debug("Estimated distance: {}",estimatedDistance);
|
||||
|
||||
Integer newTimeStep = timeStep;
|
||||
while (calcDistance(newTimeStep) < estimatedDistance) {
|
||||
newTimeStep += 1+newTimeStep/8;
|
||||
}
|
||||
while (calcDistance(newTimeStep) > estimatedDistance) {
|
||||
newTimeStep -= 1+newTimeStep/16;
|
||||
}
|
||||
|
||||
if (newTimeStep != timeStep) {
|
||||
route.brakeTimes.put(brakeId,newTimeStep);
|
||||
@@ -128,12 +147,29 @@ public class Route extends BaseClass {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
timestamp = new Date().getTime();
|
||||
timestamp = timestamp();
|
||||
Integer nextRouteSpeed = null;
|
||||
if (train.speed == 0) aborted = true;
|
||||
while (train.speed > endSpeed) {
|
||||
if (aborted || train.nextRoutePrepared()) break;
|
||||
if (train.nextRoutePrepared()) {
|
||||
if (isNull(nextRouteSpeed)) nextRouteSpeed = train.nextRoute().startSpeed(); // get the starting speed of the next route
|
||||
if (isNull(nextRouteSpeed)) {
|
||||
LOG.warn("Train has prepared next route, but that route seems to have no start speed!?");
|
||||
nextRouteSpeed = 0; // assume starting speed of zero
|
||||
}
|
||||
if (train.speed < nextRouteSpeed) { // train already is slower: stop braking, set train speed to next route's start speed
|
||||
train.setSpeed(nextRouteSpeed);
|
||||
abort();
|
||||
}
|
||||
// if train is still faster than starting speed for next route: continue braking
|
||||
}
|
||||
if (aborted) break;
|
||||
|
||||
LOG.debug("BrakeProcessor({}) setting Speed of {}.",route,train);
|
||||
train.setSpeed(Math.max(train.speed - 5,endSpeed));
|
||||
long runtime = BaseClass.timestamp() - timestamp;
|
||||
timestamp = timestamp+runtime;
|
||||
estimatedDistance += train.speed * runtime;
|
||||
train.setSpeed(Math.max(train.speed - SPEED_STEP,endSpeed));
|
||||
try {
|
||||
sleep(timeStep);
|
||||
} catch (InterruptedException e) {
|
||||
@@ -320,7 +356,8 @@ public class Route extends BaseClass {
|
||||
}
|
||||
|
||||
public void brakeCancel() {
|
||||
if (isSet(brakeProcessor)) brakeProcessor.abort();
|
||||
if (isSet(brakeProcessor)) brakeProcessor.abort();
|
||||
brakeProcessor = null;
|
||||
}
|
||||
|
||||
public void brakeStart() {
|
||||
@@ -427,19 +464,30 @@ public class Route extends BaseClass {
|
||||
return context.clone();
|
||||
}
|
||||
|
||||
public void dropBraketimes(String...brakeIds) {
|
||||
for (String brakeId : brakeIds) brakeTimes.remove(brakeId);
|
||||
}
|
||||
|
||||
public Block endBlock() {
|
||||
return endBlock;
|
||||
}
|
||||
|
||||
public void finish() {
|
||||
LOG.debug("{}.finish()",this);
|
||||
if (isSet(train)) {
|
||||
if (train.nextRoutePrepared()) {
|
||||
LOG.debug("{} has prepared next route: {}",train,train.nextRoute());
|
||||
if (isSet(brakeProcessor)) brakeProcessor.abort();
|
||||
} else {
|
||||
train.setSpeed(0);
|
||||
if (isSet(brakeProcessor)) brakeProcessor.finish();
|
||||
LOG.debug("{} has no next route.",train);
|
||||
if (isSet(brakeProcessor)) {
|
||||
brakeProcessor.finish();
|
||||
} else {
|
||||
train.setSpeed(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
brakeProcessor = null;
|
||||
|
||||
context.clear(); // prevent delayed actions from firing after route has finished
|
||||
setSignals(Signal.RED);
|
||||
@@ -911,7 +959,14 @@ public class Route extends BaseClass {
|
||||
|
||||
public Block startBlock() {
|
||||
return startBlock;
|
||||
}
|
||||
}
|
||||
|
||||
public Integer startSpeed() {
|
||||
ActionList startActions = triggeredActions.get(ROUTE_START);
|
||||
Context context = new Context(this);
|
||||
return isSet(startActions) ? startActions.getSpeed(context) : null;
|
||||
}
|
||||
|
||||
|
||||
protected static String t(String txt, Object...fills) {
|
||||
return Translation.get(Application.class, txt, fills);
|
||||
@@ -968,4 +1023,8 @@ public class Route extends BaseClass {
|
||||
}
|
||||
return properties();
|
||||
}
|
||||
|
||||
public Integer brakeTime(String brakeId) {
|
||||
return brakeTimes.get(brakeId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,6 +93,20 @@ public class ActionList extends Action implements Iterable<Action>{
|
||||
return true;
|
||||
}
|
||||
|
||||
public Integer getSpeed(Context context) {
|
||||
Integer speed = null;
|
||||
for (Action action : this) {
|
||||
if (action instanceof SetSpeed) {
|
||||
speed = ((SetSpeed)action).getSpeed();
|
||||
}
|
||||
if (action instanceof ActionList) {
|
||||
Integer listSpeed = ((ActionList)action).getSpeed(context);
|
||||
if (isSet(listSpeed)) speed = listSpeed;
|
||||
}
|
||||
}
|
||||
return speed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Action> iterator() {
|
||||
return actions.iterator();
|
||||
|
||||
@@ -34,6 +34,14 @@ public class ConditionalAction extends ActionList {
|
||||
return super.fire(context.clone()); // actions, that happen within the conditional action list must not modify the global context.
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getSpeed(Context context) {
|
||||
for (Condition condition : conditions) {
|
||||
if (!condition.fulfilledBy(context)) return null;
|
||||
}
|
||||
return super.getSpeed(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JSONObject json() {
|
||||
JSONObject json = super.json();
|
||||
|
||||
@@ -51,6 +51,10 @@ public class SetSpeed extends Action{
|
||||
return super.properties(preForm, formInputs, postForm);
|
||||
}
|
||||
|
||||
public int getSpeed() {
|
||||
return speed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return t("Set speed to {} {}",speed,speedUnit);
|
||||
|
||||
@@ -174,6 +174,8 @@ public class Train extends BaseClass implements Comparable<Train> {
|
||||
return train.start();
|
||||
case ACTION_STOP:
|
||||
return train.stopNow();
|
||||
case ACTION_TIMES:
|
||||
return train.removeBrakeTimes();
|
||||
case ACTION_TURN:
|
||||
return train.turn();
|
||||
case ACTION_UPDATE:
|
||||
@@ -237,6 +239,25 @@ public class Train extends BaseClass implements Comparable<Train> {
|
||||
return brakeId;
|
||||
}
|
||||
|
||||
private Fieldset brakeTimes() {
|
||||
Fieldset fieldset = new Fieldset(t("Brake time table"));
|
||||
Table timeTable = new Table();
|
||||
timeTable.addRow(t("forward"),t("backward"),t("Route"));
|
||||
List<Route> routes = BaseClass.listElements(Route.class);
|
||||
Collections.sort(routes, (r1,r2)->r1.name().compareTo(r2.name()));
|
||||
String forwardId = brakeId(false);
|
||||
String backwardId = brakeId(true);
|
||||
for (Route route: routes) {
|
||||
Integer forwardTime = route.brakeTime(forwardId);
|
||||
Integer reverseTime = route.brakeTime(backwardId);
|
||||
timeTable.addRow(isSet(forwardTime)?forwardTime+" ms":"-",isSet(reverseTime)?reverseTime+" ms":"-",route.name());
|
||||
}
|
||||
|
||||
timeTable.addTo(fieldset);
|
||||
this.button(t("Drop brake times"),Map.of(ACTION,ACTION_TIMES)).addTo(fieldset);
|
||||
return fieldset;
|
||||
}
|
||||
|
||||
private Tag carList() {
|
||||
Tag locoProp = new Tag("li").content(t("Locomotives and cars")+":");
|
||||
Tag carList = new Tag("ul").clazz("carlist");
|
||||
@@ -506,6 +527,11 @@ public class Train extends BaseClass implements Comparable<Train> {
|
||||
this.name = newName;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Route nextRoute() {
|
||||
return nextRoute;
|
||||
}
|
||||
|
||||
|
||||
public boolean nextRoutePrepared() {
|
||||
return isSet(nextRoute) && nextRoute.state() == Route.State.PREPARED;
|
||||
@@ -518,7 +544,7 @@ public class Train extends BaseClass implements Comparable<Train> {
|
||||
|
||||
@Override
|
||||
protected Window properties(List<Fieldset> preForm, FormInput formInputs, List<Fieldset> postForm) {
|
||||
Fieldset otherTrainProsps = new Fieldset(t("other train properties"));
|
||||
Fieldset otherTrainProps = new Fieldset(t("other train properties"));
|
||||
|
||||
Tag propList = new Tag("ul").clazz("proplist");
|
||||
|
||||
@@ -558,14 +584,15 @@ public class Train extends BaseClass implements Comparable<Train> {
|
||||
ul.addTo(li).addTo(propList);
|
||||
}
|
||||
|
||||
propList.addTo(otherTrainProsps);
|
||||
propList.addTo(otherTrainProps);
|
||||
|
||||
formInputs.add(t("Name"), new Input(NAME,name));
|
||||
formInputs.add(t("Push-pull train"),new Checkbox(PUSH_PULL, t("Push-pull train"), pushPull));
|
||||
formInputs.add(t("Tags"), new Input(TAGS,String.join(", ", tags)));
|
||||
|
||||
preForm.add(Locomotive.cockpit(this));
|
||||
postForm.add(otherTrainProsps);
|
||||
postForm.add(otherTrainProps);
|
||||
postForm.add(brakeTimes());
|
||||
|
||||
return super.properties(preForm, formInputs, postForm);
|
||||
}
|
||||
@@ -583,6 +610,12 @@ public class Train extends BaseClass implements Comparable<Train> {
|
||||
} else return t("autopilot not active.");
|
||||
}
|
||||
|
||||
private Window removeBrakeTimes() {
|
||||
List<Route> routes = BaseClass.listElements(Route.class);
|
||||
for (Route route: routes) route.dropBraketimes(brakeId(false),brakeId(true));
|
||||
return properties();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeChild(BaseClass child) {
|
||||
LOG.debug("{}.removeChild({})",this,child);
|
||||
@@ -715,7 +748,6 @@ public class Train extends BaseClass implements Comparable<Train> {
|
||||
|
||||
public void setSpeed(int newSpeed) {
|
||||
LOG.debug("{}.setSpeed({})",this,newSpeed);
|
||||
if (speed == 0 && newSpeed > 0) Thread.dumpStack();
|
||||
speed = Math.min(newSpeed,maxSpeed());
|
||||
if (speed < 0) speed = 0;
|
||||
cars.stream().filter(c -> c instanceof Locomotive).forEach(car -> ((Locomotive)car).setSpeed(speed));
|
||||
@@ -834,16 +866,12 @@ public class Train extends BaseClass implements Comparable<Train> {
|
||||
|
||||
public Object stopNow() {
|
||||
quitAutopilot();
|
||||
setSpeed(0);
|
||||
if (isSet(nextRoute)) {
|
||||
nextRoute.reset();
|
||||
nextRoute = null;
|
||||
}
|
||||
if (isSet(route)) {
|
||||
route.brakeCancel();
|
||||
route.reset();
|
||||
route = null;
|
||||
}
|
||||
setSpeed(0);
|
||||
|
||||
return properties();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user