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
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); |
|
} |
|
}
|
|
|