Browse Source

started to re-implement actions with promises

lookup-tables
Stephan Richter 5 years ago
parent
commit
3dc11c18e1
  1. 2
      pom.xml
  2. 18
      src/main/java/de/srsoftware/web4rail/Application.java
  3. 24
      src/main/java/de/srsoftware/web4rail/Command.java
  4. 27
      src/main/java/de/srsoftware/web4rail/ControlUnit.java
  5. 8
      src/main/java/de/srsoftware/web4rail/Plan.java
  6. 36
      src/main/java/de/srsoftware/web4rail/Route.java
  7. 29
      src/main/java/de/srsoftware/web4rail/moving/Train.java
  8. 7
      src/main/java/de/srsoftware/web4rail/tiles/Tile.java
  9. 29
      src/main/java/de/srsoftware/web4rail/tiles/Turnout.java
  10. 5
      src/main/java/de/srsoftware/web4rail/tiles/Turnout3E.java
  11. 30
      src/main/java/de/srsoftware/web4rail/tiles/TurnoutL.java
  12. 19
      src/main/java/de/srsoftware/web4rail/tiles/TurnoutR.java

2
pom.xml

@ -4,7 +4,7 @@ @@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>de.srsoftware</groupId>
<artifactId>web4rail</artifactId>
<version>0.5.6</version>
<version>0.6.1</version>
<name>Web4Rail</name>
<packaging>jar</packaging>
<description>Java Model Railway Control</description>

18
src/main/java/de/srsoftware/web4rail/Application.java

@ -15,6 +15,7 @@ import java.nio.charset.Charset; @@ -15,6 +15,7 @@ import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.HashMap;
import java.util.concurrent.CompletableFuture;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -73,6 +74,23 @@ public class Application { @@ -73,6 +74,23 @@ public class Application {
if (response instanceof Page) {
html = ((Page)response).html().toString().getBytes(UTF8);
client.getResponseHeaders().add("content-type", "text/html");
} else if (response instanceof CompletableFuture) {
CompletableFuture<?> promise = (CompletableFuture<?>) response;
promise.thenAccept(object -> {
try {
send(client,object);
} catch (IOException e) {
LOG.warn("Was not able to send {}!",object);
}
}).exceptionally(ex -> {
try {
send(client,ex.getMessage());
} catch (IOException e) {
LOG.warn("Was not able to send {}!",ex);
}
throw new RuntimeException(ex);
});
return;
} else {
html = (response == null ? "" : response.toString()).getBytes(UTF8);
client.getResponseHeaders().add("content-type", "text/plain");

24
src/main/java/de/srsoftware/web4rail/Command.java

@ -0,0 +1,24 @@ @@ -0,0 +1,24 @@
package de.srsoftware.web4rail;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Command extends CompletableFuture<ControlUnit.Reply> {
private static final Logger LOG = LoggerFactory.getLogger(Command.class);
private String command;
public Command(String command) {
this.command = command;
this.orTimeout(500, TimeUnit.MILLISECONDS);
LOG.debug("Created new Command({}).",command);
}
@Override
public String toString() {
return command;
}
}

27
src/main/java/de/srsoftware/web4rail/ControlUnit.java

@ -33,7 +33,7 @@ public class ControlUnit extends Thread implements Constants{ @@ -33,7 +33,7 @@ public class ControlUnit extends Thread implements Constants{
private static final String PORT = "port";
private static final String BUS = "bus";
private class Reply{
public class Reply{
private long secs;
private int milis;
private int code;
@ -47,11 +47,19 @@ public class ControlUnit extends Thread implements Constants{ @@ -47,11 +47,19 @@ public class ControlUnit extends Thread implements Constants{
message = scanner.nextLine().trim();
LOG.info("recv {}.{} {} {}.",secs,milis,code,message);
}
public String message() {
return message;
}
@Override
public String toString() {
return "Reply("+secs+"."+milis+" / "+code+" / "+message+")";
}
public boolean is(int code) {
return code == this.code;
}
}
@ -60,7 +68,7 @@ public class ControlUnit extends Thread implements Constants{ @@ -60,7 +68,7 @@ public class ControlUnit extends Thread implements Constants{
private int port = DEFAULT_PORT;
private int bus = 0;
private boolean stopped = true;
private LinkedList<String> queue = new LinkedList<String>();
private LinkedList<Command> queue = new LinkedList<Command>();
private Socket socket;
private Scanner scanner;
private boolean power = false;
@ -156,8 +164,10 @@ public class ControlUnit extends Thread implements Constants{ @@ -156,8 +164,10 @@ public class ControlUnit extends Thread implements Constants{
return win;
}
public void queue(String command) {
queue.add(command);
public Command queue(String command) {
Command promise = new Command(command);
queue.add(promise);
return promise;
}
/**
@ -176,7 +186,10 @@ public class ControlUnit extends Thread implements Constants{ @@ -176,7 +186,10 @@ public class ControlUnit extends Thread implements Constants{
try {
if (queue.isEmpty()) {
Thread.sleep(10);
} else send(queue.poll());
} else {
Command command = queue.pop();
command.complete(send(command));
}
} catch (InterruptedException | IOException e) {
e.printStackTrace();
}
@ -200,9 +213,9 @@ public class ControlUnit extends Thread implements Constants{ @@ -200,9 +213,9 @@ public class ControlUnit extends Thread implements Constants{
* @return
* @throws IOException
*/
private Reply send(String command) throws IOException {
private Reply send(Command command) throws IOException {
if (command == null) return null;
writeln(command);
writeln(command.toString());
return new Reply(scanner);
}

8
src/main/java/de/srsoftware/web4rail/Plan.java

@ -13,6 +13,7 @@ import java.util.Map; @@ -13,6 +13,7 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.Stack;
import java.util.Vector;
import java.util.concurrent.CompletableFuture;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -426,8 +427,9 @@ public class Plan implements Constants{ @@ -426,8 +427,9 @@ public class Plan implements Constants{
return false;
}
public void place(Tile tile) throws IOException {
public Tile place(Tile tile) throws IOException {
stream("place "+tile.tag(null));
return tile;
}
private Object planAction(HashMap<String, String> params) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, IOException {
@ -580,7 +582,7 @@ public class Plan implements Constants{ @@ -580,7 +582,7 @@ public class Plan implements Constants{
stream(t("Warning: {}",t("Ghost train @ {}",contact)));
}
public void queue(String command) {
controlUnit.queue(command);
public CompletableFuture<ControlUnit.Reply> queue(String command) {
return controlUnit.queue(command);
}
}

36
src/main/java/de/srsoftware/web4rail/Route.java

@ -12,6 +12,7 @@ import java.util.List; @@ -12,6 +12,7 @@ import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Vector;
import java.util.concurrent.CompletableFuture;
import org.json.JSONArray;
import org.json.JSONException;
@ -21,6 +22,7 @@ import org.slf4j.LoggerFactory; @@ -21,6 +22,7 @@ import org.slf4j.LoggerFactory;
import de.keawe.tools.translations.Translation;
import de.srsoftware.tools.Tag;
import de.srsoftware.web4rail.ControlUnit.Reply;
import de.srsoftware.web4rail.Plan.Direction;
import de.srsoftware.web4rail.actions.Action;
import de.srsoftware.web4rail.actions.ActivateRoute;
@ -290,15 +292,35 @@ public class Route implements Constants{ @@ -290,15 +292,35 @@ public class Route implements Constants{
file.close();
}
public Route lock(Train train) throws IOException {
this.train = train;
for (Entry<Turnout, State> entry : turnouts.entrySet()) {
entry.getKey().state(entry.getValue());
public CompletableFuture<Reply> lock(Train train) throws IOException {
Vector<Tile> locked = new Vector<Tile>();
try {
for (Tile tile : path) locked.add(tile.lock(this)); // try to lock all tiles along the path
} catch (Exception e) { // if something fails: unlock all tiles locked so far
for (Tile tile : locked) try {
tile.unlock();
} catch (IOException ex) {
LOG.warn("Problem while unlocking {}",ex);
}
throw e;
}
for (Tile tile : path) tile.lock(this);
return this;
CompletableFuture<Reply> promise = null;
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);
}
promise.exceptionally(ex -> {
for (Tile tile : locked) try {
tile.unlock();
} catch (IOException e) {
LOG.warn("Problem while unlocking {}",e);
}
throw new RuntimeException(ex);
}).thenRun(() -> this.train = train);
return promise;
}
public List<Route> multiply(int size) {
Vector<Route> routes = new Vector<Route>();
for (int i=0; i<size; i++) routes.add(i==0 ? this : this.clone());

29
src/main/java/de/srsoftware/web4rail/moving/Train.java

@ -12,6 +12,7 @@ import java.util.HashSet; @@ -12,6 +12,7 @@ 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;
@ -331,8 +332,8 @@ public class Train implements Constants { @@ -331,8 +332,8 @@ public class Train implements Constants {
this.speed = v;
}
public String start() throws IOException {
if (block == null) return t("{} not in a block",this);
public CompletableFuture<String> start() throws IOException {
if (block == null) return CompletableFuture.failedFuture(new RuntimeException(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>();
@ -351,12 +352,18 @@ public class Train implements Constants { @@ -351,12 +352,18 @@ public class Train implements Constants {
availableRoutes.add(rt);
}
Random rand = new Random();
if (availableRoutes.isEmpty()) return t("No free routes from {}",block);
int sel = rand.nextInt(availableRoutes.size());
route = availableRoutes.get(sel).lock(this).setSignals(null);
if (direction != route.startDirection) turn();
setSpeed(100);
return t("started {}",this);
if (availableRoutes.isEmpty()) return CompletableFuture.failedFuture(new RuntimeException(t("No free routes from {}",block)));
route = availableRoutes.get(rand.nextInt(availableRoutes.size()));
return route.lock(this).thenApply(reply -> {
try {
route.setSignals(null);
if (direction != route.startDirection) turn();
setSpeed(100);
return t("started {}",this);
} catch (Exception e) {
throw new RuntimeException(e);
}
});
}
private Object stop() {
@ -375,7 +382,11 @@ public class Train implements Constants { @@ -375,7 +382,11 @@ public class Train implements Constants {
}
private void turn() throws IOException {
if (direction != null) direction = direction.inverse();
LOG.debug("train.turn()");
if (direction != null) {
direction = direction.inverse();
for (Locomotive loco : locos) loco.turn();
}
if (block != null) block.train(this);
}

7
src/main/java/de/srsoftware/web4rail/tiles/Tile.java

@ -155,9 +155,10 @@ public abstract class Tile implements Constants{ @@ -155,9 +155,10 @@ public abstract class Tile implements Constants{
return this;
}
public void lock(Route route) throws IOException {
this.route = route;
plan.place(this);
public Tile lock(Route lockingRoute) throws IOException {
if (route != null && route != lockingRoute) throw new IllegalStateException(this.toString());
route = lockingRoute;
return plan.place(this);
}
public Plan plan() {

29
src/main/java/de/srsoftware/web4rail/tiles/Turnout.java

@ -3,10 +3,12 @@ package de.srsoftware.web4rail.tiles; @@ -3,10 +3,12 @@ package de.srsoftware.web4rail.tiles;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import org.json.JSONObject;
import de.srsoftware.tools.Tag;
import de.srsoftware.web4rail.ControlUnit.Reply;
import de.srsoftware.web4rail.Device;
import de.srsoftware.web4rail.Protocol;
import de.srsoftware.web4rail.tags.Fieldset;
@ -35,29 +37,8 @@ public abstract class Turnout extends Tile implements Device{ @@ -35,29 +37,8 @@ public abstract class Turnout extends Tile implements Device{
@Override
public Object click() throws IOException {
LOG.debug("Turnout.click()");
Object o = super.click();
if (address != 0 && !initialized) {
String p = null;
switch (protocol) {
case DCC14:
case DCC27:
case DCC28:
case DCC128:
p = "N";
break;
case MOTO:
p = "M";
break;
case SELECTRIX:
p = "S";
break;
default:
p = "P";
}
plan.queue("INIT {} GA "+address+" "+p);
initialized = true;
}
return o;
init();
return super.click();
}
protected void init() {
@ -119,7 +100,7 @@ public abstract class Turnout extends Tile implements Device{ @@ -119,7 +100,7 @@ public abstract class Turnout extends Tile implements Device{
return state;
}
public abstract void state(State newState) throws IOException;
public abstract CompletableFuture<Reply> state(State newState) throws IOException;
@Override
public Tag tag(Map<String, Object> replacements) throws IOException {

5
src/main/java/de/srsoftware/web4rail/tiles/Turnout3E.java

@ -3,8 +3,10 @@ package de.srsoftware.web4rail.tiles; @@ -3,8 +3,10 @@ package de.srsoftware.web4rail.tiles;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import de.srsoftware.web4rail.Connector;
import de.srsoftware.web4rail.ControlUnit.Reply;
import de.srsoftware.web4rail.Plan.Direction;
public class Turnout3E extends Turnout{
@ -29,8 +31,9 @@ public class Turnout3E extends Turnout{ @@ -29,8 +31,9 @@ public class Turnout3E extends Turnout{
}
@Override
public void state(State newState) throws IOException {
public CompletableFuture<Reply> state(State newState) throws IOException {
// TODO Auto-generated method stub
LOG.warn("Turnout3E.state({}) not implemented, yet!",newState);
return null;
}
}

30
src/main/java/de/srsoftware/web4rail/tiles/TurnoutL.java

@ -2,8 +2,10 @@ package de.srsoftware.web4rail.tiles; @@ -2,8 +2,10 @@ package de.srsoftware.web4rail.tiles;
import java.io.IOException;
import java.util.HashMap;
import java.util.concurrent.CompletableFuture;
import de.srsoftware.tools.Tag;
import de.srsoftware.web4rail.ControlUnit.Reply;
import de.srsoftware.web4rail.tags.Fieldset;
import de.srsoftware.web4rail.tags.Input;
import de.srsoftware.web4rail.tags.Label;
@ -18,7 +20,13 @@ public class TurnoutL extends Turnout { @@ -18,7 +20,13 @@ public class TurnoutL extends Turnout {
Object o = super.click();
if (route != null) {
plan.stream(t("{} is locked by {}!",this,route));
} else state(state == State.STRAIGHT ? State.LEFT : State.STRAIGHT);
} else {
CompletableFuture<Reply> promise = state(state == State.STRAIGHT ? State.LEFT : State.STRAIGHT);
promise.exceptionally(ex -> {
LOG.warn("Failed to toggle turnout: ",ex);
throw new RuntimeException(ex);
}).thenAccept(reply -> LOG.debug("Success: {}",reply));
}
return o;
}
@ -45,21 +53,25 @@ public class TurnoutL extends Turnout { @@ -45,21 +53,25 @@ public class TurnoutL extends Turnout {
}
@Override
public void state(State newState) throws IOException {
public CompletableFuture<Reply> state(State newState) throws IOException {
init();
LOG.debug("Setting {} to {}",this,newState);
int p = 0;
LOG.debug("Requesting to set {} to {}",this,newState);
CompletableFuture<Reply> result;
switch (newState) {
case LEFT:
p = portB;
result = plan.queue("SET {} GA "+address+" "+portB+" 1 "+delay);
break;
case STRAIGHT:
p = portA;
result = plan.queue("SET {} GA "+address+" "+portA+" 1 "+delay);
break;
default:
throw new IllegalStateException();
}
if (p != 0) plan.queue("SET {} GA "+address+" "+p+" 1 "+delay);
state = newState;
plan.stream("place "+tag(null));
return result.thenApply(reply -> {
LOG.debug("{} received {}",TurnoutL.this,reply);
if (!reply.is(200)) throw new RuntimeException(reply.message());
state = newState;
return reply;
});
}
}

19
src/main/java/de/srsoftware/web4rail/tiles/TurnoutR.java

@ -2,8 +2,10 @@ package de.srsoftware.web4rail.tiles; @@ -2,8 +2,10 @@ package de.srsoftware.web4rail.tiles;
import java.io.IOException;
import java.util.HashMap;
import java.util.concurrent.CompletableFuture;
import de.srsoftware.tools.Tag;
import de.srsoftware.web4rail.ControlUnit.Reply;
import de.srsoftware.web4rail.tags.Fieldset;
import de.srsoftware.web4rail.tags.Input;
import de.srsoftware.web4rail.tags.Label;
@ -45,21 +47,24 @@ public class TurnoutR extends Turnout { @@ -45,21 +47,24 @@ public class TurnoutR extends Turnout {
}
@Override
public void state(State newState) throws IOException {
public CompletableFuture<Reply> state(State newState) throws IOException {
init();
LOG.debug("Setting {} to {}",this,newState);
int p = 0;
CompletableFuture<Reply> result;
switch (newState) {
case RIGHT:
p = portB;
result = plan.queue("SET {} GA "+address+" "+portB+" 1 "+delay);
break;
case STRAIGHT:
p = portA;
result = plan.queue("SET {} GA "+address+" "+portA+" 1 "+delay);
break;
default:
throw new IllegalStateException();
}
if (p != 0) plan.queue("SET {} GA "+address+" "+p+" 1 "+delay);
state = newState;
plan.stream("place "+tag(null));
return result.thenApply(reply -> {
LOG.debug("{} received {}",reply);
if (reply.is(200)) state = newState;
return reply;
});
}
}

Loading…
Cancel
Save