added javadoc comments to some classes
This commit is contained in:
@@ -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:<br/>
|
||||
* 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<String, String> 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<String, String> inflate(String data) {
|
||||
//LOG.debug("inflate({})",data);
|
||||
HashMap<String, String> params = new HashMap<String, String>();
|
||||
@@ -108,10 +149,21 @@ public class Application implements Constants{
|
||||
return params;
|
||||
}
|
||||
|
||||
/**
|
||||
* creates a map from url-encoded data
|
||||
* @param data
|
||||
* @return
|
||||
*/
|
||||
private static HashMap<String, String> 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<String, String> 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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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<String, String> 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<String, String> params) {
|
||||
if (params.containsKey(HOST)) host = params.get(HOST);
|
||||
if (params.containsKey(PORT)) port = Integer.parseInt(params.get(PORT));
|
||||
|
||||
@@ -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<String> cssFiles = new Vector<String>();
|
||||
@@ -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<body>\n")
|
||||
.append(content)
|
||||
.append("\t</body>\n")
|
||||
.append("</html>\n");
|
||||
}
|
||||
|
||||
private StringBuffer head() {
|
||||
StringBuffer sb = new StringBuffer()
|
||||
.append("<html>\n")
|
||||
@@ -33,31 +45,23 @@ public class Page {
|
||||
}
|
||||
return sb.append("\t</head>\n");
|
||||
}
|
||||
|
||||
private StringBuffer body(StringBuffer content) {
|
||||
return new StringBuffer()
|
||||
.append("\t<body>\n")
|
||||
.append(content)
|
||||
.append("\t</body>\n")
|
||||
.append("</html>\n");
|
||||
}
|
||||
|
||||
|
||||
public StringBuffer html() {
|
||||
return head().append(body(buf));
|
||||
}
|
||||
|
||||
public Page append(Object code) {
|
||||
buf.append(code);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Page style(String cssPath) {
|
||||
cssFiles.add(cssPath);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Page js(String jsPath) {
|
||||
jsFiles.add(jsPath);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Page style(String cssPath) {
|
||||
cssFiles.add(cssPath);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return head().append(body(buf)).toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
* <ul>
|
||||
* <li>Tack layout</li>
|
||||
* <li>Trains and Cars</li>
|
||||
* <li>Routes</li>
|
||||
* <li>...</li>
|
||||
* </ul>
|
||||
* @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<OutputStreamWriter,Integer> clients = new HashMap<OutputStreamWriter, Integer>();
|
||||
private static final String ACTION_QR = "qrcode";
|
||||
|
||||
public HashMap<String,Tile> tiles = new HashMap<String,Tile>();
|
||||
private HashSet<Block> blocks = new HashSet<Block>();
|
||||
private HashMap<Integer, Route> routes = new HashMap<Integer, Route>();
|
||||
private ControlUnit controlUnit = new ControlUnit(this);
|
||||
public HashMap<String,Tile> tiles = new HashMap<String,Tile>(); // The list of tiles of this plan, i.e. the Track layout
|
||||
private HashSet<Block> blocks = new HashSet<Block>(); // the list of tiles, that are blocks
|
||||
private HashMap<Integer, Route> routes = new HashMap<Integer, Route>(); // 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<String, String> 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 <it>clickTile</it> 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 <it>return request({...});</li>, 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<Route> routes = new Vector<Route>();
|
||||
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<Block> 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<Route> follow(Route route, Connector connector) {
|
||||
Tile tile = get(Tile.id(connector.x,connector.y),false);
|
||||
Vector<Route> results = new Vector<>();
|
||||
@@ -209,7 +298,6 @@ public class Plan implements Constants{
|
||||
}
|
||||
Map<Connector, State> connectors = tile.connections(connector.from);
|
||||
List<Route>routes = route.multiply(connectors.size());
|
||||
LOG.debug("{}",tile);
|
||||
if (connectors.size()>1) LOG.debug("SPLITTING @ {}",tile);
|
||||
|
||||
for (Entry<Connector, State> 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("<div id=\"plan\">");
|
||||
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<Tile> stack = new Stack<Tile>();
|
||||
@@ -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<tile.len(); i++) remove_intern(tile.x+i, tile.y); // remove shadow tiles
|
||||
for (int i=1; i<tile.height(); i++) remove_intern(tile.x, tile.y+i); // remove shadow tiles
|
||||
for (int i=1; i<tile.len(); i++) removeTile(tile.x+i, tile.y); // remove shadow tiles
|
||||
for (int i=1; i<tile.height(); i++) removeTile(tile.x, tile.y+i); // remove shadow tiles
|
||||
if (tile != null) stream("remove "+tile.id());
|
||||
}
|
||||
|
||||
/**
|
||||
* removes a route from the track layout
|
||||
* @param route
|
||||
*/
|
||||
public void remove(Route route) {
|
||||
for (Tile tile : route.path()) tile.remove(route);
|
||||
for (Train train : Train.list()) {
|
||||
@@ -404,10 +588,20 @@ public class Plan implements Constants{
|
||||
stream(t("Removed {}.",route));
|
||||
}
|
||||
|
||||
private void remove_intern(int x, int y) {
|
||||
/**
|
||||
* removes a tile from the track layout (subroutine)
|
||||
* @param x
|
||||
* @param y
|
||||
*/
|
||||
private void removeTile(int x, int y) {
|
||||
LOG.debug("removed {} from tile list",tiles.remove(Tile.id(x, y)));
|
||||
}
|
||||
|
||||
/**
|
||||
* returns a specific route from the list of routes assigned to this plan
|
||||
* @param routeId the id of the route requestd
|
||||
* @return
|
||||
*/
|
||||
public Route route(int routeId) {
|
||||
return routes.get(routeId);
|
||||
}
|
||||
@@ -425,6 +619,12 @@ public class Plan implements Constants{
|
||||
return t("Unknown action: {}",params.get(ACTION));
|
||||
}
|
||||
|
||||
/**
|
||||
* saves the plan to a set of files, along with its cars, tiles, trains, routes and control unit settings
|
||||
* @param name
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
private String saveTo(String name) throws IOException {
|
||||
if (name == null || name.isEmpty()) throw new NullPointerException("Name must not be empty!");
|
||||
Car.saveAll(name+".cars");
|
||||
@@ -435,20 +635,38 @@ public class Plan implements Constants{
|
||||
return t("Plan saved as \"{}\".",name);
|
||||
}
|
||||
|
||||
/**
|
||||
* adds a tile to the plan at a specific position
|
||||
* @param x
|
||||
* @param y
|
||||
* @param tile
|
||||
* @throws IOException
|
||||
*/
|
||||
public void set(int x,int y,Tile tile) throws IOException {
|
||||
if (tile == null) return;
|
||||
if (tile instanceof Block) blocks.add((Block) tile);
|
||||
for (int i=1; i<tile.len(); i++) set(x+i,y,new Shadow(tile));
|
||||
for (int i=1; i<tile.height(); i++) set(x,y+i,new Shadow(tile));
|
||||
set_intern(x,y,tile);
|
||||
setIntern(x,y,tile);
|
||||
place(tile);
|
||||
}
|
||||
|
||||
private void set_intern(int x, int y, Tile tile) {
|
||||
/**
|
||||
* adds a tile to the plan at a specific position (subroutine)
|
||||
* @param x
|
||||
* @param y
|
||||
* @param tile
|
||||
*/
|
||||
private void setIntern(int x, int y, Tile tile) {
|
||||
tile.position(x, y).plan(this);
|
||||
tiles.put(tile.id(),tile);
|
||||
}
|
||||
|
||||
/**
|
||||
* shows the properties of an entity specified in the params.context value
|
||||
* @param params
|
||||
* @return
|
||||
*/
|
||||
public Window showContext(HashMap<String, String> 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<String, String> 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)));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user