stdio interface is working

This commit is contained in:
hneemann 2018-03-07 16:26:54 +01:00
parent b44c92c379
commit 11ded0ca3b
5 changed files with 161 additions and 45 deletions

View File

@ -84,7 +84,7 @@ public class External extends Node implements Element {
@Override @Override
public void writeOutputs() throws NodeException { public void writeOutputs() throws NodeException {
try { try {
processHandler.readValuesTo(outputs); processHandler.readValues(outputs);
} catch (IOException e) { } catch (IOException e) {
throw new NodeException(Lang.get("err_errorReadingDataToProcess"), this, -1, outputs, e); throw new NodeException(Lang.get("err_errorReadingDataToProcess"), this, -1, outputs, e);
} }

View File

@ -5,6 +5,7 @@
*/ */
package de.neemann.digital.core.extern; package de.neemann.digital.core.extern;
import de.neemann.digital.core.extern.handler.Generic;
import de.neemann.digital.lang.Lang; import de.neemann.digital.lang.Lang;
import java.io.IOException; import java.io.IOException;
@ -43,6 +44,8 @@ public final class ProcessFactory {
*/ */
public static ProcessHandler create(Type type, String code, String params) throws IOException { public static ProcessHandler create(Type type, String code, String params) throws IOException {
switch (type) { switch (type) {
case Generic:
return new Generic(params);
default: default:
throw new IOException(Lang.get("err_processType_N_notFound", type.name())); throw new IOException(Lang.get("err_processType_N_notFound", type.name()));
} }

View File

@ -29,6 +29,6 @@ public interface ProcessHandler extends Closeable {
* @param values the values to write to * @param values the values to write to
* @throws IOException IOException * @throws IOException IOException
*/ */
void readValuesTo(ObservableValues values) throws IOException; void readValues(ObservableValues values) throws IOException;
} }

View File

@ -0,0 +1,24 @@
/*
* Copyright (c) 2018 Helmut Neemann.
* Use of this source code is governed by the GPL v3 license
* that can be found in the LICENSE file.
*/
package de.neemann.digital.core.extern.handler;
import java.io.IOException;
/**
* Starts the string given in params as an external application
*/
public class Generic extends StdIOProcess {
/**
* Creates a new simple process
*
* @param name the name of the application to start
* @throws IOException IOException
*/
public Generic(String name) throws IOException {
ProcessBuilder pb = new ProcessBuilder(name);
setProcess(pb.start());
}
}

View File

@ -10,24 +10,29 @@ import de.neemann.digital.core.ObservableValues;
import de.neemann.digital.core.extern.ProcessHandler; import de.neemann.digital.core.extern.ProcessHandler;
import java.io.*; import java.io.*;
import java.util.StringTokenizer;
/** /**
* The generic process description * The generic process description
*/ */
public abstract class StdIOProcess implements ProcessHandler { public class StdIOProcess implements ProcessHandler {
private static final String PREFIX = "digital:";
private Process process;
private BufferedWriter writer; private BufferedWriter writer;
private BufferedReader reader; private Thread thread;
private final Object lock = new Object();
private String dataFound;
/** /**
* Sets the reader and writer * Set the already started process
* *
* @param reader the reader * @param process the process to use
* @param writer the writer
*/ */
public void setReaderWriter(BufferedReader reader, BufferedWriter writer) { public void setProcess(Process process) {
this.reader = reader; this.process = process;
this.writer = writer; setInputOutputStream(process.getInputStream(), process.getOutputStream());
} }
/** /**
@ -36,58 +41,142 @@ public abstract class StdIOProcess implements ProcessHandler {
* @param in the input stream * @param in the input stream
* @param out the output stream * @param out the output stream
*/ */
public void setInputOutputStream(InputStream in, OutputStream out) { private void setInputOutputStream(InputStream in, OutputStream out) {
setReaderWriter( setReaderWriter(
new BufferedReader(new InputStreamReader(in)), new BufferedReader(new InputStreamReader(in)),
new BufferedWriter(new OutputStreamWriter(out))); new BufferedWriter(new OutputStreamWriter(out)));
} }
/**
* Sets the reader and writer
*
* @param reader the reader
* @param writer the writer
*/
private void setReaderWriter(BufferedReader reader, BufferedWriter writer) {
this.writer = writer;
thread = new Thread(() -> {
try {
String line;
while ((line = reader.readLine()) != null) {
if (line.startsWith(PREFIX)) {
synchronized (lock) {
while (dataFound != null)
lock.wait();
dataFound = line;
lock.notify();
}
}
}
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
});
thread.setDaemon(true);
thread.start();
}
private String readLine() throws IOException {
synchronized (lock) {
try {
long startTime = System.currentTimeMillis();
while (dataFound == null && (System.currentTimeMillis() - startTime) < 5000)
lock.wait(1000);
if (dataFound == null)
throw new IOException("timeout");
String line = dataFound;
dataFound = null;
lock.notify();
return line;
} catch (InterruptedException e) {
return null;
}
}
}
@Override @Override
public void writeValues(ObservableValues values) throws IOException { public void writeValues(ObservableValues values) throws IOException {
boolean first = true;
for (ObservableValue v : values) { for (ObservableValue v : values) {
if (first) final int bits = v.getBits();
first = false; final long value = v.getValue();
final long highZ = v.getHighZ();
long mask = 1;
for (int i = 0; i < bits; i++) {
if ((highZ & mask) != 0)
writer.write('Z');
else {
if ((value & mask) != 0)
writer.write('1');
else else
writer.write(","); writer.write('0');
writer.write(v.getName()); }
writer.write("="); mask <<= 1;
writer.write(Long.toHexString(v.getValue())); }
} }
writer.write("\n"); writer.write("\n");
writer.flush(); writer.flush();
} }
@Override @Override
public void readValuesTo(ObservableValues values) throws IOException { public void readValues(ObservableValues values) throws IOException {
String line; String line = readLine();
while ((line = reader.readLine()) != null) { if (line != null) {
if (line.startsWith("digital:")) { int pos = PREFIX.length();
StringTokenizer st = new StringTokenizer(line.substring(8), ",=", true); int len = line.length();
for (ObservableValue v : values) { for (ObservableValue v : values) {
if (!st.hasMoreTokens()) final int bits = v.getBits();
throw new IOException("not enough values received!");
String name = st.nextToken().trim(); if (pos + bits > len)
if (name.equals(",")) throw new IOException("not enough data");
name = st.nextToken().trim();
if (!name.equals(v.getName()))
throw new IOException("values in wrong order: expected " + v.getName() + " but found " + name);
if (!st.nextToken().equals("="))
throw new IOException("= expected");
final String valStr = st.nextToken();
if (valStr.equals("Z"))
v.setToHighZ();
else
v.setValue(Long.parseLong(valStr, 16));
long value = 0;
long highZ = 0;
long mask = 1;
for (int i = 0; i < bits; i++) {
char c = line.charAt(pos);
switch (c) {
case 'z':
case 'Z':
highZ |= mask;
break;
case 'h':
case 'H':
case '1':
value |= mask;
break;
case 'l':
case 'L':
case '0':
break;
default:
throw new IOException("invalid character " + c);
} }
return; mask <<= 1;
pos++;
} }
v.set(value, highZ);
} }
} else
throw new IOException("process has stopped");
} }
@Override
public void close() throws IOException {
process.destroy();
thread.interrupt();
try {
thread.join(1000);
} catch (InterruptedException e) {
throw new IOException("thread was interrupted");
}
if (thread.isAlive())
throw new IOException("thread was not stopped");
}
} }