You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

270 lines
7.3 KiB

package de.srsoftware.web4rail;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Scanner;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import de.keawe.tools.translations.Translation;
import de.srsoftware.web4rail.tags.Button;
import de.srsoftware.web4rail.tags.Fieldset;
import de.srsoftware.web4rail.tags.Form;
import de.srsoftware.web4rail.tags.Input;
import de.srsoftware.web4rail.tags.Label;
public class ControlUnit extends Thread implements Constants{
private static final Logger LOG = LoggerFactory.getLogger(ControlUnit.class);
private static final String DEFAULT_HOST = "localhost";
private static final int DEFAULT_PORT = 4303;
private static final int OK_PROTO = 201;
private static final int OK_MODE = 202;
private static final int OK = 200;
private static final String HOST = "host";
private static final String PORT = "port";
private static final String BUS = "bus";
public class Reply{
private long secs;
private int milis;
private int code;
private String message;
public Reply(Scanner scanner) {
String word = scanner.next();
secs = Long.parseLong(word.substring(0, word.length()-4));
milis = Integer.parseInt(word.substring(word.length()-3));
code = scanner.nextInt();
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;
}
}
private String host = DEFAULT_HOST;
private int port = DEFAULT_PORT;
private int bus = 0;
private boolean stopped = true;
private LinkedList<Command> queue = new LinkedList<Command>();
private Socket socket;
private Scanner scanner;
private boolean power = false;
/**
* @return stops the loop at the next interval
*/
public ControlUnit end() {
stopped = true;
return this;
}
private void handshake() throws IOException {
String proto = null;
if (scanner.hasNext()) {
String line = scanner.nextLine();
LOG.debug("recv: "+line);
for (String part : line.split(";")) {
part = part.trim();
if (part.startsWith("SRCP ")) proto = part.substring(5);
}
if (proto == null) throw new IOException("Handshake failed: "+line);
if (!proto.startsWith("0.8.")) throw new IOException("Unsupported protocol: "+proto);
writeln("SET PROTOCOL SRCP "+proto);
} else throw new IOException("Handshake expected.");
Reply reply = new Reply(scanner);
if (reply.code != OK_PROTO) throw new IOException("Handshake failed: "+reply);
writeln("SET CONNECTIONMODE SRCP COMMAND"); // preset following mode: COMMAND MODE
reply = new Reply(scanner);
if (reply.code != OK_MODE) throw new IOException("Handshake failed: "+reply);
writeln("GO"); // switch mode
reply = new Reply(scanner);
if (reply.code != OK) throw new IOException("Handshake failed: "+reply);
}
private JSONObject json() {
JSONObject json = new JSONObject();
json.put(HOST, host);
json.put(PORT, port);
json.put(BUS, bus);
return json;
}
public void load(String filename) throws IOException {
BufferedReader file = new BufferedReader(new FileReader(filename));
JSONObject json = new JSONObject(file.readLine());
file.close();
if (json.has(PORT)) port = json.getInt(PORT);
if (json.has(BUS)) bus = json.getInt(BUS);
if (json.has(HOST)) host = json.getString(HOST);
}
public static void main(String[] args) throws InterruptedException {
ControlUnit cu = new ControlUnit().setEndpoint("Modellbahn", DEFAULT_PORT).setBus(1).restart();
Thread.sleep(1000);
cu.queue("SET {} POWER ON");
cu.queue("SET {} GL 1 0 10 128");
Thread.sleep(1000);
cu.end();
}
public Object process(HashMap<String, String> params) {
switch (params.get(ACTION)) {
case ACTION_CONNECT:
restart();
return t("Control unit (re)started.");
case ACTION_POWER:
return togglePower();
case ACTION_PROPS:
return properties();
case ACTION_UPDATE:
return update(params);
}
return t("Unknown action: {}",params.get(ACTION));
}
public Object properties() {
Window win = new Window("cu-props", t("Properties of the control unit"));
Form form = new Form();
new Input(ACTION,ACTION_UPDATE).hideIn(form);
new Input(REALM,REALM_CU).hideIn(form);
Fieldset fieldset = new Fieldset(t("Server connection"));
new Input(HOST,host).addTo(new Label(t("Hostname"))).addTo(fieldset);
new Input(PORT,port).numeric().addTo(new Label(t("Port"))).addTo(fieldset);
new Input(BUS,bus).numeric().addTo(new Label(t("Bus"))).addTo(fieldset);
new Button(t("Save")).addTo(fieldset).addTo(form).addTo(win);
fieldset = new Fieldset("Actions");
new Button(t("Connect"),"connectCu();").addTo(fieldset).addTo(win);
return win;
}
public Command queue(String command) {
Command promise = new Command(command);
queue.add(promise);
return promise;
}
/**
* Should close the server connection and establish new server connection
* @return
*/
public ControlUnit restart() {
end();
start();
return this;
}
@Override
public void run() {
while (!stopped) {
try {
if (queue.isEmpty()) {
Thread.sleep(10);
} else {
Command command = queue.pop();
command.complete(send(command));
}
} catch (InterruptedException | IOException e) {
e.printStackTrace();
}
}
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public void save(String filename) throws IOException {
BufferedWriter file = new BufferedWriter(new FileWriter(filename));
file.write(json()+"\n");
file.close();
}
/**
* send command to Server
* @param command
* @return
* @throws IOException
*/
private Reply send(Command command) throws IOException {
if (command == null) return null;
writeln(command.toString());
return new Reply(scanner);
}
private ControlUnit setBus(int bus) {
this.bus = bus;
return this;
}
public ControlUnit setEndpoint(String newHost, int newPort){
host = newHost;
port = newPort;
return this;
}
@Override
public synchronized void start() {
try {
socket = new Socket(host, port);
scanner = new Scanner(socket.getInputStream());
handshake();
stopped = false;
} catch (IOException e) {
throw new IllegalStateException(e);
}
super.start();
}
private static String t(String text,Object...fills) {
return Translation.get(Application.class, text, fills);
}
private Object togglePower() {
power = !power;
String PW = power?"ON":"OFF";
queue("SET {} POWER "+PW);
return t("Turned power {}.",PW);
}
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));
if (params.containsKey(BUS)) bus = Integer.parseInt(params.get(BUS));
return t("Updated control unit settings");
}
private void writeln(String data) throws IOException {
data = data.replace("{}", ""+bus);
socket.getOutputStream().write((data+"\n").getBytes(StandardCharsets.US_ASCII));
LOG.info("sent {}.",data);
}
}