package uk.org.smithfamily.mslogger.ecu.simulated; import java.io.*; import java.net.ServerSocket; import java.net.Socket; import uk.org.smithfamily.mslogger.ApplicationSettings; import uk.org.smithfamily.mslogger.log.*; /** * Base class for any ECU simulators * */ public abstract class MSSimulator implements Runnable { public static final int SERVERPORT = 1052; // Approx allowing for start/stop bits etc private static final double BAUD_PER_BYTE = 10; // The log file we're going to play back FRDLogFile frdFile; Thread simThread = null; ServerSocket serverSocket; long lastWrite = System.currentTimeMillis(); private volatile boolean running = false; public boolean isRunning() { return running; } @Override public void run() { running = true; while (running) { try { final Socket client = serverSocket.accept(); // Get the streams... final InputStream is = client.getInputStream(); final OutputStream os = client.getOutputStream(); // And process them while (running) { final int b = is.read(); process(b, is, os); os.flush(); } } catch (final IOException e) { // Something borked. Drop the connection and try again DebugLogManager.INSTANCE.logException(e); } } } /** * Pull a page of firmware from a previously saved file * * @param pageNo * @return */ protected byte[] getFirmwarePage(final int pageNo) { final byte[] page = new byte[getFirmwarePageSize(pageNo)]; final File firmwareFile = new File(ApplicationSettings.INSTANCE.getDataDir(), getFirmwareFile()); try { final FileInputStream r = new FileInputStream(firmwareFile); r.skip((pageNo - 1) * getFirmwarePageSize(pageNo)); r.read(page); r.close(); } catch (final IOException e) { DebugLogManager.INSTANCE.logException(e); } return page; } /** * Pull from the previously loaded list, the next set of runtime vars * * @return * @throws IOException */ protected byte[] getNextPageOfVars() throws IOException { final FRDLogFileRecord nextRecord = frdFile.getNextRecord(); final byte[] vars = nextRecord.getOchBuffer(); return vars; } /** * Construct the server socket ready for connection */ public void init() { try { serverSocket = new ServerSocket(SERVERPORT); } catch (final IOException e) { DebugLogManager.INSTANCE.logException(e); } } /** * Load up the FRD file and launch the simulator thread * * @throws IOException */ public synchronized void startRunning() throws IOException { final File logFile = new File(ApplicationSettings.INSTANCE.getDataDir(), getFRDFilename()); final FileInputStream fis = new FileInputStream(logFile); frdFile = FRDLogManager.INSTANCE.loadFile(fis); fis.close(); simThread = new Thread(this); simThread.setDaemon(true); simThread.setName("SIM:" + getSignature()); simThread.start(); } /** * Shut down the simulator thread * * @throws IOException */ public synchronized void stopRunning() throws IOException { if (simThread != null) { running = false; simThread.interrupt(); simThread = null; } } /** * Default baud rate. Should probably be lower to simulate BT being a bit rubbish * * @return */ int getBaudRate() { return 11520; } /** * Attempt to feed data back at a rate approximating the baud rate * * @param os * @param data * @throws IOException */ void speedLimitedWrite(final OutputStream os, final byte[] data) throws IOException { final int dataSize = data.length; final double msPerByte = (1000.0 / getBaudRate()) * BAUD_PER_BYTE; final long delay = (long) (msPerByte * dataSize); try { Thread.sleep(delay); } catch (final InterruptedException e) { DebugLogManager.INSTANCE.logException(e); } os.write(data); } /** * Given a command byte, process it (and any other relevant bytes) and fire the result back up the OutputStream * * @param b * @param is * @param os * @throws IOException */ abstract void process(int b, InputStream is, OutputStream os) throws IOException; /** * The signature of this simulated ECU * * @return */ abstract String getSignature(); /** * Location of the file containing a dump of the firmware * * @return */ abstract String getFirmwareFile(); /** * Return the size of a given page of the firmware * * @param pageNo * @return */ abstract int getFirmwarePageSize(int pageNo); /** * return the name of the FRD log file to play back * * @return */ abstract String getFRDFilename(); }