diff --git a/resources/js/plan.js b/resources/js/plan.js index 7786226..0d93b14 100644 --- a/resources/js/plan.js +++ b/resources/js/plan.js @@ -157,5 +157,9 @@ window.onload = function () { $('.menu .addtile .list svg').click(enableAdding); $('.menu .move .list div').click(enableMove); $('.menu .actions .list > div').click(runAction); - $(BODY).click(bodyClick); + $(BODY).click(bodyClick); + var stream = new EventSource("stream"); + stream.onmessage = function(ev){ + console.log(ev); + } } diff --git a/src/main/java/de/srsoftware/web4rail/Application.java b/src/main/java/de/srsoftware/web4rail/Application.java index bff7dce..fce1e46 100644 --- a/src/main/java/de/srsoftware/web4rail/Application.java +++ b/src/main/java/de/srsoftware/web4rail/Application.java @@ -6,6 +6,7 @@ import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.OutputStream; +import java.io.OutputStreamWriter; import java.lang.reflect.InvocationTargetException; import java.net.InetSocketAddress; import java.net.URI; @@ -38,6 +39,7 @@ public class Application { server.createContext("/plan", client -> sendPlan(client)); server.createContext("/css" , client -> sendFile(client)); server.createContext("/js" , client -> sendFile(client)); + server.createContext("/stream", client -> stream(client)); server.setExecutor(java.util.concurrent.Executors.newCachedThreadPool()); server.start(); try { @@ -47,36 +49,7 @@ public class Application { } Desktop.getDesktop().browse(URI.create("http://localhost:"+config.getInt(PORT)+"/plan")); } - - private static void sendFile(HttpExchange client) throws IOException { - URI uri = client.getRequestURI(); - File file = new File(System.getProperty("user.dir")+"/resources"+uri); - LOG.debug("requesting file: {}",file); - if (file.exists()) { - client.getResponseHeaders().add("Content-Type", Files.probeContentType(file.toPath())); - client.sendResponseHeaders(200, file.length()); - OutputStream out = client.getResponseBody(); - FileInputStream in = new FileInputStream(file); - in.transferTo(out); - in.close(); - out.close(); - return; - } - sendError(client,404,t("Could not find \"{}\"",uri)); - } - - private static void sendError(HttpExchange client, int code, String msg) throws IOException { - client.sendResponseHeaders(code, msg.length()); - LOG.error(msg); - OutputStream out = client.getResponseBody(); - out.write(msg.getBytes(UTF8)); - out.close(); - } - - private static HashMap inflate(byte[] data) { - return inflate(new String(data,UTF8)); - } - + private static HashMap inflate(String data) { LOG.debug("inflate({})",data); HashMap params = new HashMap(); @@ -89,8 +62,12 @@ public class Application { } return params; - } + } + private static HashMap inflate(byte[] data) { + return inflate(new String(data,UTF8)); + } + private static void send(HttpExchange client, Object response) throws IOException { byte[] html; if (response instanceof Page) { @@ -106,6 +83,31 @@ public class Application { os.write(html); os.close(); } + + private static void sendError(HttpExchange client, int code, String msg) throws IOException { + client.sendResponseHeaders(code, msg.length()); + LOG.error(msg); + OutputStream out = client.getResponseBody(); + out.write(msg.getBytes(UTF8)); + out.close(); + } + + private static void sendFile(HttpExchange client) throws IOException { + URI uri = client.getRequestURI(); + File file = new File(System.getProperty("user.dir")+"/resources"+uri); + LOG.debug("requesting file: {}",file); + if (file.exists()) { + client.getResponseHeaders().add("Content-Type", Files.probeContentType(file.toPath())); + client.sendResponseHeaders(200, file.length()); + OutputStream out = client.getResponseBody(); + FileInputStream in = new FileInputStream(file); + in.transferTo(out); + in.close(); + out.close(); + return; + } + sendError(client,404,t("Could not find \"{}\"",uri)); + } private static void sendPlan(HttpExchange client) throws IOException { try { @@ -117,6 +119,13 @@ public class Application { } } + private static void stream(HttpExchange client) throws IOException { + client.getResponseHeaders().set("content-type", "text/event-stream"); + client.sendResponseHeaders(200, 0); + OutputStreamWriter sseWriter = new OutputStreamWriter(client.getResponseBody()); + plan.addClient(sseWriter); + } + private static String t(String text, Object...fills) { return Translation.get(Application.class, text, fills); } diff --git a/src/main/java/de/srsoftware/web4rail/Plan.java b/src/main/java/de/srsoftware/web4rail/Plan.java index b91fa85..48abf08 100644 --- a/src/main/java/de/srsoftware/web4rail/Plan.java +++ b/src/main/java/de/srsoftware/web4rail/Plan.java @@ -6,9 +6,11 @@ import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; +import java.io.OutputStreamWriter; import java.lang.reflect.InvocationTargetException; import java.security.InvalidParameterException; import java.util.Collection; +import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -62,6 +64,21 @@ public class Plan { public enum Direction{ NORTH, SOUTH, EAST, WEST } + + private class Heartbeat extends Thread { + @Override + public void run() { + try { + while (true) { + sleep(10000); + heatbeat(); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + private static final String ACTION = "action"; private static final String ACTION_ADD = "add"; private static final String ACTION_ANALYZE = "analyze"; @@ -78,11 +95,47 @@ public class Plan { private static final String ACTION_ROUTE = "openRoute"; private static final String ID = "id"; private static final String ROUTE = "route"; + private static final HashMap clients = new HashMap(); private HashMap> tiles = new HashMap>(); private HashSet blocks = new HashSet(); private HashMap routes = new HashMap(); + public Plan() { + new Heartbeat().start(); + } + + public void heatbeat() { + stream("hearbeat @ "+new Date().getTime()); + } + + private void stream(String data) { + 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 (badClients == null) 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); + } + } + private Tag actionMenu() throws IOException { Tag tileMenu = new Tag("div").clazz("actions").content(t("Actions")); StringBuffer tiles = new StringBuffer(); @@ -91,6 +144,11 @@ public class Plan { return new Tag("div").clazz("list").content(tiles.toString()).addTo(tileMenu); } + public void addClient(OutputStreamWriter client) { + LOG.debug("Client connected."); + clients.put(client, 0); + } + public static void addLink(Tile tile,String content,Tag list) { new Tag("li").clazz("link").attr("onclick", "return clickTile("+tile.x+","+tile.y+");").content(content).addTo(list); } @@ -187,7 +245,7 @@ public class Plan { String line = br.readLine().trim(); String[] parts = line.split("=",2); try { - String id = parts[0]; + //String id = parts[0]; JSONObject json = new JSONObject(parts[1]); Route route = new Route(); json.getJSONArray(Route.PATH).forEach(entry -> {