diff --git a/pom.xml b/pom.xml index e19af7c..ed6d913 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 de.srsoftware web4rail - 0.7.12 + 0.7.13 Web4Rail jar Java Model Railway Control diff --git a/src/main/java/de/srsoftware/web4rail/Application.java b/src/main/java/de/srsoftware/web4rail/Application.java index 084100f..a4836a5 100644 --- a/src/main/java/de/srsoftware/web4rail/Application.java +++ b/src/main/java/de/srsoftware/web4rail/Application.java @@ -32,10 +32,29 @@ import de.srsoftware.web4rail.moving.Car; import de.srsoftware.web4rail.moving.Locomotive; import de.srsoftware.web4rail.moving.Train; +/** + * Entry point class for the Web4Rail application + * + * @author Stephan Richter, SRSoftware + * + */ public class Application implements Constants{ - private static Plan plan; + private static Plan plan; // the track layout in use private static final Logger LOG = LoggerFactory.getLogger(Application.class); + /** + * entry point for the application:
+ * creates a http server, loads a plan and directs a browser to the respective page + * @param args + * @throws IOException + * @throws ClassNotFoundException + * @throws InstantiationException + * @throws IllegalAccessException + * @throws IllegalArgumentException + * @throws InvocationTargetException + * @throws NoSuchMethodException + * @throws SecurityException + */ public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException { Configuration config = new Configuration(Configuration.dir("Web4Rail")+"/app.config"); LOG.debug("config: {}",config); @@ -55,6 +74,10 @@ public class Application implements Constants{ Desktop.getDesktop().browse(URI.create("http://"+InetAddress.getLocalHost().getHostName()+":"+config.getInt(PORT)+"/plan")); } + /** + * helper class creating unique ids for use throuout the application + * @return + */ public static int createId() { try { Thread.sleep(1); @@ -64,6 +87,19 @@ public class Application implements Constants{ return new Date().hashCode(); } + /** + * handles request from clients by delegating them to respective classes + * @param params + * @return + * @throws IOException + * @throws ClassNotFoundException + * @throws InstantiationException + * @throws IllegalAccessException + * @throws IllegalArgumentException + * @throws InvocationTargetException + * @throws NoSuchMethodException + * @throws SecurityException + */ private static Object handle(HashMap params) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException { String realm = params.get(REALM); @@ -94,6 +130,11 @@ public class Application implements Constants{ return t("Unknown realm: {}",params.get(REALM)); } + /** + * creates a map from url-encoded data + * @param data + * @return + */ private static HashMap inflate(String data) { //LOG.debug("inflate({})",data); HashMap params = new HashMap(); @@ -108,10 +149,21 @@ public class Application implements Constants{ return params; } + /** + * creates a map from url-encoded data + * @param data + * @return + */ private static HashMap inflate(byte[] data) { return inflate(new String(data,UTF8)); } + /** + * sends a response generated from the application to a given client + * @param client + * @param response + * @throws IOException + */ private static void send(HttpExchange client, Object response) throws IOException { byte[] html; if (response instanceof Page) { @@ -145,6 +197,13 @@ public class Application implements Constants{ os.close(); } + /** + * sends an error to a given client + * @param client + * @param code + * @param msg + * @throws IOException + */ private static void sendError(HttpExchange client, int code, String msg) throws IOException { client.sendResponseHeaders(code, msg.length()); LOG.error(msg); @@ -153,6 +212,11 @@ public class Application implements Constants{ out.close(); } + /** + * sends a requested file to the given client + * @param client + * @throws IOException + */ private static void sendFile(HttpExchange client) throws IOException { URI uri = client.getRequestURI(); File file = new File(System.getProperty("user.dir")+"/resources"+uri); @@ -170,6 +234,11 @@ public class Application implements Constants{ sendError(client,404,t("Could not find \"{}\"",uri)); } + /** + * sends a response to a given client + * @param client + * @throws IOException + */ private static void sendPlan(HttpExchange client) throws IOException { try { HashMap params = inflate(client.getRequestBody().readAllBytes()); @@ -189,6 +258,12 @@ public class Application implements Constants{ send(client,new Page().append(e.getMessage())); } } + + /** + * establishes an event stream connection between the application and a given client + * @param client + * @throws IOException + */ private static void stream(HttpExchange client) throws IOException { client.getResponseHeaders().set("content-type", "text/event-stream"); client.sendResponseHeaders(200, 0); @@ -196,6 +271,12 @@ public class Application implements Constants{ plan.addClient(sseWriter); } + /** + * shorthand for Translations.get(text,fills) + * @param text + * @param fills + * @return + */ private static String t(String text, Object...fills) { return Translation.get(Application.class, text, fills); } diff --git a/src/main/java/de/srsoftware/web4rail/Command.java b/src/main/java/de/srsoftware/web4rail/Command.java index 2558ff3..4ace0e2 100644 --- a/src/main/java/de/srsoftware/web4rail/Command.java +++ b/src/main/java/de/srsoftware/web4rail/Command.java @@ -7,18 +7,31 @@ import java.util.concurrent.TimeoutException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +/** + * handles SRCP commands and their replies received from the SRCP daemon + * @author Stephan Richter, SRSoftware + * + */ public class Command { private static final Logger LOG = LoggerFactory.getLogger(Command.class); private String command; private Reply reply = null; + /** + * encapsulates a reply received from the SRCP daemon + * + */ public static class Reply{ private long secs; private int milis; private int code; private String message; + /** + * parses a reply from the SRCP daemon + * @param scanner + */ public Reply(Scanner scanner) { String word = scanner.next(); secs = Long.parseLong(word.substring(0, word.length()-4)); @@ -28,6 +41,11 @@ public class Command { LOG.info("recv {}.{} {} {}.",secs,milis,code,message); } + /** + * creates a reply with given data + * @param code + * @param message + */ public Reply(int code, String message) { secs = new Date().getTime(); milis = (int) (secs % 1000); @@ -36,14 +54,25 @@ public class Command { this.message = message; } + /** + * checks if a response has a specific code + * @param code + * @return true if the given code equals the response's code + */ public boolean is(int code) { return code == this.code; } + /** + * @return true, if the response code is between 200 and 300 + */ public boolean succeeded() { return (code > 199 && code < 300); } + /** + * @return the message passed along with the response from the SRCP deameon + */ public String message() { return message; } @@ -54,15 +83,27 @@ public class Command { } } + /** + * encapsulates a command to be send to the SRCP daemon + * @param command + */ public Command(String command) { this.command = command; LOG.debug("Created new Command({}).",command); } + /** + * called, if the response indicates an error + * @param reply + */ protected void onFailure(Reply reply) { LOG.warn("onFailure({})",command); } + /** + * called, when a response from the SRCP daemon is received + * @param reply + */ public void onResponse(Reply reply) { this.reply = reply; if (reply.succeeded()) { @@ -70,18 +111,36 @@ public class Command { } else onFailure(reply); } + /** + * called, when the response from the SRCP daemon indicates success of the operation + */ public void onSuccess(){ LOG.debug("onSuccess({})",command); } + /** + * parses the reply from the SRCP daemon + * @param scanner + */ public void readReplyFrom(Scanner scanner) { onResponse(new Reply(scanner)); } + /** + * waits for the reply from the SRCP daemon for one second + * @return + * @throws TimeoutException + */ public Reply reply() throws TimeoutException { return reply(100); } + /** + * waits for the reply from the SRCP daemon for a given timeout + * @param timeout time (in 10ms units) to wait, before a timeout is thrown + * @return + * @throws TimeoutException + */ public Reply reply(int timeout) throws TimeoutException { int counter = 0; while (reply == null) try { @@ -93,6 +152,10 @@ public class Command { return reply; } + /** + * generate a timeout exception + * @throws TimeoutException + */ private void timeout() throws TimeoutException { String msg = command; command = null; diff --git a/src/main/java/de/srsoftware/web4rail/Constants.java b/src/main/java/de/srsoftware/web4rail/Constants.java index 8d0892b..9b36195 100644 --- a/src/main/java/de/srsoftware/web4rail/Constants.java +++ b/src/main/java/de/srsoftware/web4rail/Constants.java @@ -3,6 +3,14 @@ package de.srsoftware.web4rail; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; +import de.keawe.tools.translations.Translation; + +/** + * this interface collects constants inherited to other classes of this application + * + * @author Stephan Richter, SRSoftware + * + */ public interface Constants { public static final String ACTION = "action"; public static final String ACTION_ADD = "add"; diff --git a/src/main/java/de/srsoftware/web4rail/ControlUnit.java b/src/main/java/de/srsoftware/web4rail/ControlUnit.java index e530f5b..a984a76 100644 --- a/src/main/java/de/srsoftware/web4rail/ControlUnit.java +++ b/src/main/java/de/srsoftware/web4rail/ControlUnit.java @@ -23,6 +23,12 @@ import de.srsoftware.web4rail.tags.Form; import de.srsoftware.web4rail.tags.Input; import de.srsoftware.web4rail.tags.Label; +/** + * abstraction of a SRCP daemon (control unit) + * + * @author Stephan Richter, SRSoftware + * + */ public class ControlUnit extends Thread implements Constants{ private static final Logger LOG = LoggerFactory.getLogger(ControlUnit.class); private static final String DEFAULT_HOST = "localhost"; @@ -53,6 +59,11 @@ public class ControlUnit extends Thread implements Constants{ return this; } + /** + * performs a handshake as specified in the SRCP protocol + * @throws TimeoutException + * @throws IOException + */ private void handshake() throws TimeoutException, IOException { String proto = null; if (scanner.hasNext()) { @@ -79,6 +90,9 @@ public class ControlUnit extends Thread implements Constants{ if (!command.reply().succeeded()) throw new IOException("Handshake failed: "+command.reply()); } + /** + * @return json string containing the connection information + */ private JSONObject json() { JSONObject json = new JSONObject(); json.put(HOST, host); @@ -87,6 +101,11 @@ public class ControlUnit extends Thread implements Constants{ return json; } + /** + * load connection information from file + * @param filename + * @throws IOException + */ public void load(String filename) throws IOException { BufferedReader file = new BufferedReader(new FileReader(filename)); JSONObject json = new JSONObject(file.readLine()); @@ -96,6 +115,11 @@ public class ControlUnit extends Thread implements Constants{ if (json.has(HOST)) host = json.getString(HOST); } + /** + * test method + * @param args + * @throws InterruptedException + */ public static void main(String[] args) throws InterruptedException { ControlUnit cu = new ControlUnit(null).setEndpoint("Modellbahn", DEFAULT_PORT).setBus(1).restart(); Thread.sleep(1000); @@ -116,6 +140,11 @@ public class ControlUnit extends Thread implements Constants{ cu.end(); } + /** + * process actions related to the SRCP daemon + * @param params + * @return + */ public Object process(HashMap params) { switch (params.get(ACTION)) { case ACTION_CONNECT: @@ -134,11 +163,19 @@ public class ControlUnit extends Thread implements Constants{ return t("Unknown action: {}",params.get(ACTION)); } + /** + * turn of power immediately + * @return + */ public Object emergency() { power = true; return togglePower(); } + /** + * generate a properties view for the client + * @return + */ public Object properties() { Window win = new Window("cu-props", t("Properties of the control unit")); Form form = new Form(); @@ -155,6 +192,11 @@ public class ControlUnit extends Thread implements Constants{ return win; } + /** + * add a command to the queue of commands to be sent to the server + * @param command + * @return + */ public Command queue(Command command) { queue.add(command); return command; @@ -170,6 +212,9 @@ public class ControlUnit extends Thread implements Constants{ return this; } + /** + * thread, that repeatedly checks the queue for new commands and sends them to the SRCP daemon + */ @Override public void run() { while (!stopped) { @@ -191,6 +236,11 @@ public class ControlUnit extends Thread implements Constants{ } } + /** + * save settings to file + * @param filename + * @throws IOException + */ public void save(String filename) throws IOException { BufferedWriter file = new BufferedWriter(new FileWriter(filename)); file.write(json()+"\n"); @@ -211,11 +261,22 @@ public class ControlUnit extends Thread implements Constants{ command.readReplyFrom(scanner); } + /** + * defines the bus on the SRCP deamon, to which commands shall be assigned + * @param bus + * @return + */ private ControlUnit setBus(int bus) { this.bus = bus; return this; } + /** + * set up the connection endpoint + * @param newHost + * @param newPort + * @return + */ public ControlUnit setEndpoint(String newHost, int newPort){ host = newHost; port = newPort; @@ -235,10 +296,20 @@ public class ControlUnit extends Thread implements Constants{ super.start(); } + /** + * shorthand for Translation.get(text,fills) + * @param text + * @param fills + * @return + */ private static String t(String text,Object...fills) { return Translation.get(Application.class, text, fills); } + /** + * togge power on/off at the SRCP daemon + * @return + */ private Command togglePower() { power = !power; String PW = power?"ON":"OFF"; @@ -259,6 +330,11 @@ public class ControlUnit extends Thread implements Constants{ } + /** + * update connection parameters + * @param params + * @return + */ public String update(HashMap params) { if (params.containsKey(HOST)) host = params.get(HOST); if (params.containsKey(PORT)) port = Integer.parseInt(params.get(PORT)); diff --git a/src/main/java/de/srsoftware/web4rail/Page.java b/src/main/java/de/srsoftware/web4rail/Page.java index d4f9c3e..0d8fe4f 100644 --- a/src/main/java/de/srsoftware/web4rail/Page.java +++ b/src/main/java/de/srsoftware/web4rail/Page.java @@ -4,8 +4,12 @@ import java.util.Vector; import de.srsoftware.tools.Tag; - - +/** + * + * helper class to create html pages + * @author Stephan Richter, SRSoftware + * + */ public class Page { private StringBuffer buf; private Vector cssFiles = new Vector(); @@ -15,11 +19,19 @@ public class Page { buf = new StringBuffer(); } - @Override - public String toString() { - return head().append(body(buf)).toString(); + public Page append(Object code) { + buf.append(code); + return this; } - + + private StringBuffer body(StringBuffer content) { + return new StringBuffer() + .append("\t\n") + .append(content) + .append("\t\n") + .append("\n"); + } + private StringBuffer head() { StringBuffer sb = new StringBuffer() .append("\n") @@ -33,31 +45,23 @@ public class Page { } return sb.append("\t\n"); } - - private StringBuffer body(StringBuffer content) { - return new StringBuffer() - .append("\t\n") - .append(content) - .append("\t\n") - .append("\n"); - } - + public StringBuffer html() { return head().append(body(buf)); } - - public Page append(Object code) { - buf.append(code); + + public Page js(String jsPath) { + jsFiles.add(jsPath); return this; } - + public Page style(String cssPath) { cssFiles.add(cssPath); return this; } - - public Page js(String jsPath) { - jsFiles.add(jsPath); - return this; + + @Override + public String toString() { + return head().append(body(buf)).toString(); } } diff --git a/src/main/java/de/srsoftware/web4rail/Plan.java b/src/main/java/de/srsoftware/web4rail/Plan.java index df93249..4275fda 100644 --- a/src/main/java/de/srsoftware/web4rail/Plan.java +++ b/src/main/java/de/srsoftware/web4rail/Plan.java @@ -58,7 +58,21 @@ import de.srsoftware.web4rail.tiles.TurnoutRN; import de.srsoftware.web4rail.tiles.TurnoutRS; import de.srsoftware.web4rail.tiles.TurnoutRW; +/** + * This class is a central part of the Application, as it loads, holds and saves all kinds of information: + *
    + *
  • Tack layout
  • + *
  • Trains and Cars
  • + *
  • Routes
  • + *
  • ...
  • + *
+ * @author Stephan Richter, SRSoftware + * + */ public class Plan implements Constants{ + /** + * The four directions Trains can be within blocks + */ public enum Direction{ NORTH, SOUTH, EAST, WEST; @@ -73,6 +87,9 @@ public class Plan implements Constants{ } } + /** + * This thread sends a heartbea to the client + */ private class Heartbeat extends Thread { @Override public void run() { @@ -96,15 +113,31 @@ public class Plan implements Constants{ private static final HashMap clients = new HashMap(); private static final String ACTION_QR = "qrcode"; - public HashMap tiles = new HashMap(); - private HashSet blocks = new HashSet(); - private HashMap routes = new HashMap(); - private ControlUnit controlUnit = new ControlUnit(this); + public HashMap tiles = new HashMap(); // The list of tiles of this plan, i.e. the Track layout + private HashSet blocks = new HashSet(); // the list of tiles, that are blocks + private HashMap routes = new HashMap(); // the list of routes of the track layout + private ControlUnit controlUnit = new ControlUnit(this); // the control unit, to which the plan is connected + /** + * creates a new plan, starts to send heart beats + */ public Plan() { new Heartbeat().start(); } + /** + * manages plan-related commands + * @param params the parameters passed from the client + * @return Object returned to the client + * @throws IOException + * @throws ClassNotFoundException + * @throws InstantiationException + * @throws IllegalAccessException + * @throws IllegalArgumentException + * @throws InvocationTargetException + * @throws NoSuchMethodException + * @throws SecurityException + */ public Object action(HashMap params) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException { switch (params.get(ACTION)) { case ACTION_ADD: @@ -123,6 +156,11 @@ public class Plan implements Constants{ return t("Unknown action: {}",params.get(ACTION)); } + /** + * generates the action menu that is appended to the plan + * @return + * @throws IOException + */ private Tag actionMenu() throws IOException { Tag actionMenu = new Tag("div").clazz("actions").content(t("Actions")); Tag actions = new Tag("div").clazz("list").content(""); @@ -133,17 +171,45 @@ public class Plan implements Constants{ return actions.addTo(actionMenu); } + /** + * attaches a new client to the event stream of the plan + * @param client + */ public void addClient(OutputStreamWriter client) { LOG.debug("Client connected."); clients.put(client, 0); } + /** + * helper function: creates a list element with a link that will call the clickTile function of the client side javascript. + * @param tile the tile a click on which shall be simulated + * @param content the text to be displayed to the user + * @param list the tag to which the link tag shall be added + * @return returns the list element itself + * TODO: replace occurences by calls to return request({...});, then remove clickTile from the client javascript + */ public static Tag addLink(Tile tile,String content,Tag list) { Tag li = new Tag("li"); new Tag("span").clazz("link").attr("onclick", "return clickTile("+tile.x+","+tile.y+");").content(content).addTo(li).addTo(list); return li; } + /** + * add a tile of the specified class to the track layout + * @param clazz + * @param xs + * @param ys + * @param configJson + * @return + * @throws ClassNotFoundException + * @throws InstantiationException + * @throws IllegalAccessException + * @throws IllegalArgumentException + * @throws InvocationTargetException + * @throws NoSuchMethodException + * @throws SecurityException + * @throws IOException + */ private String addTile(String clazz, String xs, String ys, String configJson) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, IOException { int x = Integer.parseInt(xs); int y = Integer.parseInt(ys); @@ -161,6 +227,10 @@ public class Plan implements Constants{ return t("Added {}",tile.getClass().getSimpleName()); } + /** + * search all possible routes in the plan + * @return a string giving information how many routes have been found + */ private String analyze() { Vector routes = new Vector(); for (Block block : blocks) { @@ -175,19 +245,38 @@ public class Plan implements Constants{ return t("Found {} routes.",routes.size()); } + /** + * @return the list of blocks known to the plan + */ public Collection blocks() { return blocks; } + /** + * calls tile.click() + * @param tile + * @return + * @throws IOException + */ private Object click(Tile tile) throws IOException { if (tile == null) return null; return tile.click(); } + /** + * @return the control unit currently connected to the plan + */ public ControlUnit controlUnit() { return controlUnit; } + /** + * completes a given route during a call to {@link #analyze()}. + * It therefore traces where the current part of the route comes from and where it may go. + * @param route an incomplete route, that shall be completed + * @param connector + * @return the set of routes, that result from the tracing operation + */ private Collection follow(Route route, Connector connector) { Tile tile = get(Tile.id(connector.x,connector.y),false); Vector results = new Vector<>(); @@ -209,7 +298,6 @@ public class Plan implements Constants{ } Map connectors = tile.connections(connector.from); Listroutes = route.multiply(connectors.size()); - LOG.debug("{}",tile); if (connectors.size()>1) LOG.debug("SPLITTING @ {}",tile); for (Entry entry: connectors.entrySet()) { @@ -223,6 +311,12 @@ public class Plan implements Constants{ return results; } + /** + * returns the tile referenced by the tile id + * @param tileId a combination of the coordinates of the requested tile + * @param resolveShadows if this is set to true, this function will return the overlaying tiles, if the id belongs to a shadow tile. + * @return the tile belonging to the id, or the overlaying tile if the respective tile is a shadow tile. + */ public Tile get(String tileId,boolean resolveShadows) { Tile tile = tiles.get(tileId); if (resolveShadows && tile instanceof Shadow) tile = ((Shadow)tile).overlay(); @@ -230,6 +324,11 @@ public class Plan implements Constants{ } + /** + * generates the hardware menu attached to the plan + * @return + * @throws IOException + */ private Tag hardwareMenu() throws IOException { Tag tileMenu = new Tag("div").clazz("hardware").content(t("Hardware")); Tag list = new Tag("div").clazz("list").content(""); @@ -237,14 +336,26 @@ public class Plan implements Constants{ return list.addTo(tileMenu); } + /** + * prepares the hardware div of the plan + * @return + */ private Tag heartbeat() { return new Div("heartbeat").content(""); } + /** + * send a heatbeat to the client + */ public void heatbeat() { stream("heartbeat @ "+new Date().getTime()); } + /** + * generates a html document of this plan + * @return + * @throws IOException + */ public Page html() throws IOException { Page page = new Page().append("
"); for (Tile tile: tiles.values()) { @@ -261,6 +372,19 @@ public class Plan implements Constants{ .js("js/plan.js"); } + /** + * loads a track layout from a file, along with its assigned cars, trains, routes and control unit settings + * @param filename + * @return + * @throws IOException + * @throws ClassNotFoundException + * @throws InstantiationException + * @throws IllegalAccessException + * @throws IllegalArgumentException + * @throws InvocationTargetException + * @throws NoSuchMethodException + * @throws SecurityException + */ public static Plan load(String filename) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException { Plan plan = new Plan(); try { @@ -292,6 +416,11 @@ public class Plan implements Constants{ return plan; } + /** + * creates the main menu attached to the plan + * @return + * @throws IOException + */ private Tag menu() throws IOException { Tag menu = new Tag("div").clazz("menu"); new Tag("div").clazz("emergency").content(t("Emergency")).attr("onclick","return request({realm:'"+REALM_CU+"',action:'"+ACTION_EMERGENCY+"'});").addTo(menu); @@ -303,10 +432,18 @@ public class Plan implements Constants{ return menu; } + /** + * prepares the messages div of the plan + * @return + */ private Tag messages() { return new Div("messages").content(""); } + /** + * creates the move-tile menu of the plan + * @return + */ private Tag moveMenu() { Tag tileMenu = new Tag("div").clazz("move").title(t("Move tiles")).content(t("↹")); Tag tiles = new Tag("div").clazz("list").content(""); @@ -317,6 +454,14 @@ public class Plan implements Constants{ return tiles.addTo(tileMenu); } + /** + * processes move-tile instructions sent from the client + * @param direction + * @param tileId + * @return + * @throws NumberFormatException + * @throws IOException + */ private String moveTile(String direction, String tileId) throws NumberFormatException, IOException { switch (direction) { case "south": @@ -331,6 +476,13 @@ public class Plan implements Constants{ throw new InvalidParameterException(t("\"{}\" is not a known direction!")); } + /** + * processes move-tile instructions sent from the client (subroutine) + * @param tile + * @param direction + * @return + * @throws IOException + */ private String moveTile(Tile tile, Direction direction) throws IOException { boolean moved = false; if (tile != null) { @@ -353,6 +505,14 @@ public class Plan implements Constants{ return t(moved ? "Tile(s) moved.":"No tile(s) moved."); } + /** + * processes move-tile instructions sent from the client (subroutine) + * @param tile + * @param xstep + * @param ystep + * @return + * @throws IOException + */ private boolean moveTile(Tile tile,int xstep,int ystep) throws IOException { LOG.error("moveTile({} +{}/+{})",tile,xstep,ystep); Stack stack = new Stack(); @@ -372,29 +532,53 @@ public class Plan implements Constants{ return false; } + /** + * adds a new tile to the plan on the client side + * @param tile + * @return + * @throws IOException + */ public Tile place(Tile tile) throws IOException { stream("place "+tile.tag(null)); return tile; } + /** + * adds a command to the control unit's command queue + * @param command + * @return + */ public Command queue(Command command) { return controlUnit.queue(command); } + /** + * adds a new route to the plan + * @param route + * @return + */ Route registerRoute(Route route) { for (Tile tile: route.path()) tile.add(route); routes.put(route.id(), route); return route; } + /** + * removes a tile from the track layout + * @param tile + */ private void remove(Tile tile) { - remove_intern(tile.x,tile.y); + removeTile(tile.x,tile.y); if (tile instanceof Block) blocks.remove(tile); - for (int i=1; i params) { String[] parts = params.get(CONTEXT).split(":"); String realm = parts[0]; @@ -460,6 +678,10 @@ public class Plan implements Constants{ return null; } + /** + * sends some data to the clients + * @param data + */ public synchronized void stream(String data) { data = data.replaceAll("\n", "").replaceAll("\r", ""); //if (!data.startsWith("heartbeat")) LOG.debug("streaming: {}",data); @@ -488,10 +710,21 @@ public class Plan implements Constants{ } } + /** + * shorthand for Translations.get(message,fills) + * @param message + * @param fills + * @return + */ private String t(String message, Object...fills) { return Translation.get(Application.class, message, fills); } + /** + * generates the menu for selecting tiles to be added to the layout + * @return + * @throws IOException + */ private Tag tileMenu() throws IOException { Tag tileMenu = new Tag("div").clazz("addtile").title(t("Add tile")).content("╦"); @@ -529,6 +762,11 @@ public class Plan implements Constants{ return tiles.addTo(tileMenu); } + /** + * generates the train menu + * @return + * @throws IOException + */ private Tag trainMenu() throws IOException { Tag tileMenu = new Tag("div").clazz("trains").content(t("Trains")); Tag tiles = new Tag("div").clazz("list").content(""); @@ -537,10 +775,21 @@ public class Plan implements Constants{ return tiles.addTo(tileMenu); } + /** + * updates a tile + * @param tile + * @param params + * @return + * @throws IOException + */ private Tile update(Tile tile, HashMap params) throws IOException { return tile == null ? null : tile.update(params); } + /** + * sends a Ghost train warning to the client + * @param contact + */ public void warn(Contact contact) { stream(t("Warning: {}",t("Ghost train @ {}",contact))); }