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("