diff --git a/java_console/.idea/.name b/java_console/.idea/.name new file mode 100644 index 0000000000..6d94bdd83c --- /dev/null +++ b/java_console/.idea/.name @@ -0,0 +1 @@ +java_console \ No newline at end of file diff --git a/java_console/.idea/ant.xml b/java_console/.idea/ant.xml new file mode 100644 index 0000000000..313b0c0cb8 --- /dev/null +++ b/java_console/.idea/ant.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/java_console/.idea/compiler.xml b/java_console/.idea/compiler.xml new file mode 100644 index 0000000000..b22c943f13 --- /dev/null +++ b/java_console/.idea/compiler.xml @@ -0,0 +1,25 @@ + + + + + + diff --git a/java_console/.idea/copyright/profiles_settings.xml b/java_console/.idea/copyright/profiles_settings.xml new file mode 100644 index 0000000000..3572571ad8 --- /dev/null +++ b/java_console/.idea/copyright/profiles_settings.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/java_console/.idea/dictionaries/andrey.xml b/java_console/.idea/dictionaries/andrey.xml new file mode 100644 index 0000000000..9969a687d0 --- /dev/null +++ b/java_console/.idea/dictionaries/andrey.xml @@ -0,0 +1,7 @@ + + + + belomutskiy + + + \ No newline at end of file diff --git a/java_console/.idea/encodings.xml b/java_console/.idea/encodings.xml new file mode 100644 index 0000000000..e206d70d85 --- /dev/null +++ b/java_console/.idea/encodings.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/java_console/.idea/libraries/SteelSeries.xml b/java_console/.idea/libraries/SteelSeries.xml new file mode 100644 index 0000000000..2fd81cfd41 --- /dev/null +++ b/java_console/.idea/libraries/SteelSeries.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/java_console/.idea/libraries/annotations.xml b/java_console/.idea/libraries/annotations.xml new file mode 100644 index 0000000000..9375c6d1d4 --- /dev/null +++ b/java_console/.idea/libraries/annotations.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/java_console/.idea/libraries/batik.xml b/java_console/.idea/libraries/batik.xml new file mode 100644 index 0000000000..a19ef5db2a --- /dev/null +++ b/java_console/.idea/libraries/batik.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/java_console/.idea/libraries/jssc.xml b/java_console/.idea/libraries/jssc.xml new file mode 100644 index 0000000000..bff9d4bff1 --- /dev/null +++ b/java_console/.idea/libraries/jssc.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/java_console/.idea/libraries/junit.xml b/java_console/.idea/libraries/junit.xml new file mode 100644 index 0000000000..004a5215b9 --- /dev/null +++ b/java_console/.idea/libraries/junit.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/java_console/.idea/libraries/miglayout_4_0.xml b/java_console/.idea/libraries/miglayout_4_0.xml new file mode 100644 index 0000000000..527499cd32 --- /dev/null +++ b/java_console/.idea/libraries/miglayout_4_0.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/java_console/.idea/libraries/surfaceplotter.xml b/java_console/.idea/libraries/surfaceplotter.xml new file mode 100644 index 0000000000..125a69ec07 --- /dev/null +++ b/java_console/.idea/libraries/surfaceplotter.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/java_console/.idea/misc.xml b/java_console/.idea/misc.xml new file mode 100644 index 0000000000..28737110d7 --- /dev/null +++ b/java_console/.idea/misc.xml @@ -0,0 +1,27 @@ + + + + + + + http://www.w3.org/1999/xhtml + + + + + + + + + diff --git a/java_console/.idea/modules.xml b/java_console/.idea/modules.xml new file mode 100644 index 0000000000..5d231a23f9 --- /dev/null +++ b/java_console/.idea/modules.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/java_console/.idea/runConfigurations/AutoTest.xml b/java_console/.idea/runConfigurations/AutoTest.xml new file mode 100644 index 0000000000..1a245a65be --- /dev/null +++ b/java_console/.idea/runConfigurations/AutoTest.xml @@ -0,0 +1,24 @@ + + + + \ No newline at end of file diff --git a/java_console/.idea/runConfigurations/Launcher.xml b/java_console/.idea/runConfigurations/Launcher.xml new file mode 100644 index 0000000000..7c497d7441 --- /dev/null +++ b/java_console/.idea/runConfigurations/Launcher.xml @@ -0,0 +1,27 @@ + + + + \ No newline at end of file diff --git a/java_console/.idea/runConfigurations/Launcher_29001.xml b/java_console/.idea/runConfigurations/Launcher_29001.xml new file mode 100644 index 0000000000..f5085b59cd --- /dev/null +++ b/java_console/.idea/runConfigurations/Launcher_29001.xml @@ -0,0 +1,24 @@ + + + + \ No newline at end of file diff --git a/java_console/.idea/runConfigurations/Launcher_COM10.xml b/java_console/.idea/runConfigurations/Launcher_COM10.xml new file mode 100644 index 0000000000..1b6dbbcf3b --- /dev/null +++ b/java_console/.idea/runConfigurations/Launcher_COM10.xml @@ -0,0 +1,27 @@ + + + + \ No newline at end of file diff --git a/java_console/.idea/runConfigurations/Launcher_COM11.xml b/java_console/.idea/runConfigurations/Launcher_COM11.xml new file mode 100644 index 0000000000..9dcc97a035 --- /dev/null +++ b/java_console/.idea/runConfigurations/Launcher_COM11.xml @@ -0,0 +1,27 @@ + + + + \ No newline at end of file diff --git a/java_console/.idea/runConfigurations/Launcher_COM7.xml b/java_console/.idea/runConfigurations/Launcher_COM7.xml new file mode 100644 index 0000000000..d4517f5de8 --- /dev/null +++ b/java_console/.idea/runConfigurations/Launcher_COM7.xml @@ -0,0 +1,22 @@ + + + + \ No newline at end of file diff --git a/java_console/.idea/scopes/scope_settings.xml b/java_console/.idea/scopes/scope_settings.xml new file mode 100644 index 0000000000..922003b843 --- /dev/null +++ b/java_console/.idea/scopes/scope_settings.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/java_console/.idea/uiDesigner.xml b/java_console/.idea/uiDesigner.xml new file mode 100644 index 0000000000..3b00020308 --- /dev/null +++ b/java_console/.idea/uiDesigner.xml @@ -0,0 +1,125 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/java_console/.idea/vcs.xml b/java_console/.idea/vcs.xml new file mode 100644 index 0000000000..ebabb34f16 --- /dev/null +++ b/java_console/.idea/vcs.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/java_console/.idea/workspace.xml b/java_console/.idea/workspace.xml new file mode 100644 index 0000000000..abbbae189a --- /dev/null +++ b/java_console/.idea/workspace.xml @@ -0,0 +1,940 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + localhost + 5050 + + + + + + + + + + + + + + 1356459468906 + 1356459468906 + + + 1356461237109 + 1356461237109 + + + 1356490198156 + 1356490198156 + + + 1356664975109 + 1356664975109 + + + 1357444107186 + 1357444107186 + + + 1357445392108 + 1357445392108 + + + 1357493181093 + 1357493181093 + + + 1357574412109 + 1357574412109 + + + 1357583175109 + 1357583175109 + + + 1357786563484 + 1357786563484 + + + 1357824706109 + 1357824706109 + + + 1358190320109 + 1358190320109 + + + 1358205975109 + 1358205975109 + + + 1358212757093 + 1358212757093 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + No facets are configured + + + + + + + + + + + + + + + 1.7 + + + + + + + + ui + + + + + + + + 1.7 + + + + + + + + surfaceplotter + + + + + + + + + diff --git a/java_console/autotest/autotest.iml b/java_console/autotest/autotest.iml new file mode 100644 index 0000000000..d35f6f1e18 --- /dev/null +++ b/java_console/autotest/autotest.iml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/java_console/autotest/src/com/rusefi/AutoTest.java b/java_console/autotest/src/com/rusefi/AutoTest.java new file mode 100644 index 0000000000..be9a7c5d9c --- /dev/null +++ b/java_console/autotest/src/com/rusefi/AutoTest.java @@ -0,0 +1,150 @@ +package com.rusefi; + + +import com.irnems.FileLog; +import com.irnems.core.Sensor; +import com.irnems.core.SensorCentral; +import com.rusefi.io.LinkManager; +import com.rusefi.io.tcp.TcpConnector; +import com.rusefi.waves.RevolutionLog; +import com.rusefi.waves.WaveChart; +import com.rusefi.waves.WaveChartParser; +import com.rusefi.waves.WaveReport; + +import java.util.List; +import java.util.concurrent.*; + +import static com.rusefi.IoUtil.*; +import static com.rusefi.TestingUtils.assertCloseEnough; +import static com.rusefi.TestingUtils.assertTrue; +import static com.rusefi.waves.WaveReport.isCloseEnough; + +/** + * rusEfi firmware simulator functional test suite + *

+ * java -cp rusefi_console.jar com.rusefi.AutoTest + * + * @author Andrey Belomutskiy + * 3/5/14 + */ +public class AutoTest { + public static void main(String[] args) throws InterruptedException { + FileLog.SIMULATOR_CONSOLE.start(); + FileLog.MAIN.start(); + + try { + runTest(); + } finally { + ExecHelper.destroy(); + } + FileLog.MAIN.logLine("*******************************************************************************"); + FileLog.MAIN.logLine("************************************ Looks good! *****************************"); + FileLog.MAIN.logLine("*******************************************************************************"); + System.exit(0); + } + + private static void runTest() throws InterruptedException { + if (!TcpConnector.getAvailablePorts().isEmpty()) + throw new IllegalStateException("Port already binded on startup?"); + + ExecHelper.startSimulator(); + + +// FileLog.rlog("Waiting for TCP port..."); +// for (int i = 0; i < 180; i++) { +// if (!TcpConnector.getAvailablePorts().isEmpty()) +// break; +// Thread.sleep(1000); +// } +// if (TcpConnector.getAvailablePorts().isEmpty()) +// throw new IllegalStateException("Did we start it?"); +// /** +// * If we open a connection just to validate that the process has started, we are getting +// * weird issues with the second - actual connection +// */ +// FileLog.rlog("Time for simulator to close the port..."); +// Thread.sleep(3000); +// +// FileLog.rlog("Got a TCP port! Connecting..."); + LinkManager.start("" + TcpConnector.DEFAULT_PORT); + /** + * TCP connector is blocking + */ + LinkManager.open(); + + FileLog.rlog("Let's give it some time to start..."); + + final CountDownLatch startup = new CountDownLatch(1); + SensorCentral.AdcListener listener = new SensorCentral.AdcListener() { + @Override + public void onAdcUpdate(SensorCentral model, double value) { + startup.countDown(); + } + }; + long waitStart = System.currentTimeMillis(); + SensorCentral.getInstance().addListener(Sensor.RPM, listener); + startup.await(5, TimeUnit.SECONDS); + SensorCentral.getInstance().removeListener(Sensor.RPM, listener); + FileLog.MAIN.logLine("Got first signal in " + (System.currentTimeMillis() - waitStart)); + + mainTestBody(); + } + + private static void mainTestBody() throws InterruptedException { + changeRpm(500); + changeRpm(2000); + + String chartLine = getNextWaveChart(); + + + WaveChart chart = WaveChartParser.unpackToMap(chartLine); + + StringBuilder revolutions = chart.get(RevolutionLog.TOP_DEAD_CENTER_MESSAGE); + if (revolutions.length() == 0) + throw new IllegalStateException("Empty revolutions in " + chartLine); + + RevolutionLog revolutionLog = RevolutionLog.parseRevolutions(revolutions); + assertWave(chart, revolutionLog, WaveChart.INJECTOR_1, 0.33, 238.75); + assertWave(chart, revolutionLog, WaveChart.INJECTOR_2, 0.33, 53.04); + assertWave(chart, revolutionLog, WaveChart.INJECTOR_3, 0.33, 417.04); + assertWave(chart, revolutionLog, WaveChart.INJECTOR_4, 0.33, 594.04); + + assertWave(chart, revolutionLog, WaveChart.SPARK_1, 0.41, 53.05, 238.75, 417.72, 594.84); + } + + private static void assertWave(WaveChart chart, RevolutionLog revolutionLog, String key, double width, double... expectedAngles) { + StringBuilder events = chart.get(key); + assertTrue("Events not null for " + key, events != null); + List wr = WaveReport.parse(events.toString()); + assertTrue("waves for " + key, !wr.isEmpty()); + for (WaveReport.UpDown ud : wr) { + double angleByTime = revolutionLog.getCrankAngleByTime(ud.upTime); + assertCloseEnough("angle for " + key, angleByTime, expectedAngles); + + assertCloseEnough("width for " + key, ud.getDutyCycle(revolutionLog), width); + } + } + + private static void changeRpm(final int rpm) throws InterruptedException { + sendCommand("rpm " + rpm); + + final CountDownLatch rpmLatch = new CountDownLatch(1); + SensorCentral.AdcListener listener = new SensorCentral.AdcListener() { + @Override + public void onAdcUpdate(SensorCentral model, double value) { + double actualRpm = SensorCentral.getInstance().getValue(Sensor.RPM); + if (isCloseEnough(rpm, actualRpm)) + rpmLatch.countDown(); + } + }; + SensorCentral.getInstance().addListener(Sensor.RPM, listener); + rpmLatch.await(5, TimeUnit.SECONDS); + SensorCentral.getInstance().removeListener(Sensor.RPM, listener); + + double actualRpm = SensorCentral.getInstance().getValue(Sensor.RPM); + + if (!isCloseEnough(rpm, actualRpm)) + throw new IllegalStateException("rpm change did not happen"); + } + +} diff --git a/java_console/autotest/src/com/rusefi/ExecHelper.java b/java_console/autotest/src/com/rusefi/ExecHelper.java new file mode 100644 index 0000000000..dcc5315904 --- /dev/null +++ b/java_console/autotest/src/com/rusefi/ExecHelper.java @@ -0,0 +1,81 @@ +package com.rusefi; + +import com.irnems.FileLog; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; + +/** + * 3/18/14 + * (c) Andrey Belomutskiy + */ +public class ExecHelper { + private static final String SIMULATOR_COMMAND = "../win32_functional_tests/build/rusefi_simulator.exe"; + static Process simulatorProcess; + + private static void runSimulator() { + Thread.currentThread().setName("Main simulation"); + + try { + FileLog.rlog("Binary size: " + new File(SIMULATOR_COMMAND).length()); + + FileLog.rlog("Executing " + SIMULATOR_COMMAND); + ExecHelper.simulatorProcess = Runtime.getRuntime().exec(SIMULATOR_COMMAND); + FileLog.rlog("simulatorProcess: " + ExecHelper.simulatorProcess); + + BufferedReader input = + new BufferedReader(new InputStreamReader(ExecHelper.simulatorProcess.getInputStream())); + new Thread(createErrorStreamEcho()).start(); + + String line; + while ((line = input.readLine()) != null) { + System.out.println("from console: " + line); + FileLog.SIMULATOR_CONSOLE.logLine(line); + } + + FileLog.rlog("exitValue: " + simulatorProcess.exitValue()); + + System.out.println("end of console"); + input.close(); + } catch (Exception err) { + throw new IllegalStateException(err); + } + } + + private static Runnable createErrorStreamEcho() { + return new Runnable() { + @Override + public void run() { + BufferedReader err = + new BufferedReader(new InputStreamReader(ExecHelper.simulatorProcess.getErrorStream())); + String errLine; + try { + while ((errLine = err.readLine()) != null) { + System.out.println("from err: " + errLine); + FileLog.SIMULATOR_CONSOLE.logLine(errLine); + } + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + }; + } + + static void destroy() { + if (simulatorProcess != null) { + FileLog.rlog("Destroying sub-process..."); + simulatorProcess.destroy(); + } + } + + public static void startSimulator() { + new Thread(new Runnable() { + @Override + public void run() { + runSimulator(); + } + }, "simulator process").start(); + } +} diff --git a/java_console/autotest/src/com/rusefi/IoUtil.java b/java_console/autotest/src/com/rusefi/IoUtil.java new file mode 100644 index 0000000000..7e499329bf --- /dev/null +++ b/java_console/autotest/src/com/rusefi/IoUtil.java @@ -0,0 +1,51 @@ +package com.rusefi; + +import com.irnems.core.EngineState; +import com.rusefi.io.CommandQueue; +import com.rusefi.io.InvocationConfirmationListener; +import com.rusefi.io.LinkManager; +import com.rusefi.waves.WaveReport; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; + +/** + * @author Andrey Belomutskiy + * 3/19/14. + */ +public class IoUtil { + static void sendCommand(String command) throws InterruptedException { + final CountDownLatch responseLatch = new CountDownLatch(1); + CommandQueue.getInstance().write(command, CommandQueue.DEFAULT_TIMEOUT, new InvocationConfirmationListener() { + @Override + public void onCommandConfirmation() { + responseLatch.countDown(); + } + }); + responseLatch.await(20, TimeUnit.SECONDS); + } + + static String getNextWaveChart() throws InterruptedException { + getWaveChart(); + // we want to wait for the 2nd chart to see same same RPM across the whole chart + return getWaveChart(); + } + + private static String getWaveChart() throws InterruptedException { + final CountDownLatch waveChartLatch = new CountDownLatch(1); + + final AtomicReference result = new AtomicReference(); + + LinkManager.engineState.registerStringValueAction(WaveReport.WAVE_CHART, new EngineState.ValueCallback() { + @Override + public void onUpdate(String value) { + waveChartLatch.countDown(); + result.set(value); + } + }); + waveChartLatch.await(5, TimeUnit.SECONDS); + LinkManager.engineState.removeAction(WaveReport.WAVE_CHART); + return result.get(); + } +} diff --git a/java_console/autotest/src/com/rusefi/TestingUtils.java b/java_console/autotest/src/com/rusefi/TestingUtils.java new file mode 100644 index 0000000000..bad0f0977e --- /dev/null +++ b/java_console/autotest/src/com/rusefi/TestingUtils.java @@ -0,0 +1,29 @@ +package com.rusefi; + +import java.util.Arrays; + +import static com.rusefi.waves.WaveReport.isCloseEnough; + +/** + * @author Andrey Belomutskiy + * 3/19/14. + */ +public class TestingUtils { + static void assertTrue(String msg, boolean b) { + if (!b) + throw new IllegalStateException("Not true: " + msg); + } + + static void assertCloseEnough(String msg, double current, double... expectations) { + for (double expected : expectations) { + if (isCloseEnough(expected, current)) + return; + } + throw new IllegalStateException(msg + ": Got " + current + " while expecting " + Arrays.toString(expectations)); + } + + static void assertTrue(boolean b) { + if (!b) + throw new IllegalStateException("Not true"); + } +} diff --git a/java_console/build.xml b/java_console/build.xml new file mode 100644 index 0000000000..89bd64d030 --- /dev/null +++ b/java_console/build.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/java_console/io/io.iml b/java_console/io/io.iml new file mode 100644 index 0000000000..a498a0b204 --- /dev/null +++ b/java_console/io/io.iml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/java_console/io/src/com/irnems/FileLog.java b/java_console/io/src/com/irnems/FileLog.java new file mode 100644 index 0000000000..697789a13a --- /dev/null +++ b/java_console/io/src/com/irnems/FileLog.java @@ -0,0 +1,85 @@ +package com.irnems; + +import com.rusefi.io.LinkManager; +import org.jetbrains.annotations.Nullable; + +import java.io.*; +import java.text.SimpleDateFormat; +import java.util.Date; + +/** + * 6/30/13 + * (c) Andrey Belomutskiy + */ +public enum FileLog { + MAIN, + SIMULATOR_CONSOLE; + + private static final String DIR = "out/"; + + @Nullable + private OutputStream fileLog; // null if not opened yet or already closed + + private FileLog() { + } + + public void start() { + try { + fileLog = openLog(); + } catch (FileNotFoundException e) { + throw new IllegalStateException(e); + } + } + + private FileOutputStream openLog() throws FileNotFoundException { + if (LinkManager.onlyUI) + return null; + String date = getDate(); + createFolderIfNeeded(); + String fileName = DIR + name() + "_rfi_report_" + date + ".csv"; + rlog("Writing to " + fileName); + return new FileOutputStream(fileName, true); + } + + private static void createFolderIfNeeded() { + File dir = new File(DIR); + if (dir.exists()) + return; + boolean created = dir.mkdirs(); + if (!created) + throw new IllegalStateException("Failed to create " + DIR + " folder"); + } + + public static String getDate() { + return new SimpleDateFormat("yyyy-MM-dd HH_mm").format(new Date()); + } + + public synchronized void logLine(String fullLine) { + if (fileLog == null) + return; + try { + fileLog.write((fullLine + "\r\n").getBytes()); + fileLog.flush(); + System.out.println(fullLine); + } catch (IOException e) { + throw new IllegalStateException(e); + } + + } + + public synchronized void close() { + if (fileLog == null) + return; // already closed + try { + rlog("Closing file..."); + fileLog.close(); + fileLog = null; + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + public static void rlog(String msg) { + System.out.println("r " + msg); + } +} \ No newline at end of file diff --git a/java_console/io/src/com/irnems/file/BaseMap.java b/java_console/io/src/com/irnems/file/BaseMap.java new file mode 100644 index 0000000000..6a66e7f5ca --- /dev/null +++ b/java_console/io/src/com/irnems/file/BaseMap.java @@ -0,0 +1,62 @@ +package com.irnems.file; + +import com.irnems.FileLog; +import com.irnems.core.EngineState; +import com.irnems.models.Point3D; +import com.irnems.models.XYData; + +import java.util.HashMap; +import java.util.Map; + +/** + * 7/18/13 + * (c) Andrey Belomutskiy + */ +public class BaseMap { + public static void main(String[] args) { + loadData("a.csv", "maf", "af"); + } + + public static XYData loadData(String filename, final String key, final String value) { + FileLog.rlog("Loading from " + filename); + FileLog.rlog("Loading " + key + ">" + value); + final XYData data = new XYData(); + + EngineState.EngineStateListener listener = new EngineState.EngineStateListenerImpl() { + Map values = new HashMap(); + + @Override + public void onKeyValue(String key, String value) { + values.put(key, value); + } + + @Override + public void afterLine(String fullLine) { + if (values.containsKey("rpm") && values.containsKey(key)) { + process(values); + } + values.clear(); + } + + private void process(Map values) { + int rpm = (int) Double.parseDouble(values.get("rpm")); + double k = Double.parseDouble(values.get(key)); + String valueString = values.get(value); + if (valueString == null) + throw new NullPointerException("No value for: " + value); + float v = Float.parseFloat(valueString); + + data.addPoint(new Point3D(rpm, k, v)); + } + }; + EngineState engineState = new EngineState(listener); + engineState.registerStringValueAction("wave_chart", new EngineState.ValueCallback() { + @Override + public void onUpdate(String value) { + } + }); + FileUtils.readFile2(filename, engineState); + //return AverageData.average(data, 8); + return data; + } +} diff --git a/java_console/io/src/com/irnems/file/FileUtils.java b/java_console/io/src/com/irnems/file/FileUtils.java new file mode 100644 index 0000000000..dd57155c6f --- /dev/null +++ b/java_console/io/src/com/irnems/file/FileUtils.java @@ -0,0 +1,45 @@ +package com.irnems.file; + +import com.irnems.core.EngineState; + +import java.io.*; +import java.util.List; + +/** + * Date: 3/8/13 + * (c) Andrey Belomutskiy + */ +public class FileUtils { + static void readFile(String filename, EngineState.EngineStateListener listener) { + readFile2(filename, new EngineState(listener)); + } + + public static void readFile2(String filename, EngineState engineState) { + + BufferedReader reader; + try { + reader = new BufferedReader(new FileReader(filename)); + String line; + while ((line = reader.readLine()) != null) { + String packed = EngineState.packString(line); + engineState.append(packed + "\r\n"); + } + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + public static void saveList(String filename, List values) { + try { + BufferedWriter w = new BufferedWriter(new FileWriter(filename)); + + + for (Double v : values) + w.write(v + "\r\n"); + + w.close(); + } catch (IOException e) { + e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + } + } +} diff --git a/java_console/io/src/com/irnems/file/TableGenerator.java b/java_console/io/src/com/irnems/file/TableGenerator.java new file mode 100644 index 0000000000..914dc97ef6 --- /dev/null +++ b/java_console/io/src/com/irnems/file/TableGenerator.java @@ -0,0 +1,91 @@ +package com.irnems.file; + +import com.irnems.models.XYData; +import com.irnems.models.XYDataReader; + +import java.io.BufferedWriter; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +/** + * @author Andrey Belomutskiy + * 3/27/13 + */ +public class TableGenerator { + public static void main(String[] args) throws IOException { + + XYData data = XYDataReader.readFile("in.csv"); + + writeAsC(data, "ad_", "advance_map.c"); + } + + public static void writeAsC(XYData data, String prefix, String fileName) { + try { + doWrite(data, prefix, fileName); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + private static void doWrite(XYData data, String prefix, String fileName) throws IOException { + List rpms = new ArrayList(data.getXSet()); + BufferedWriter out = new BufferedWriter(new FileWriter(fileName)); + out.write("#include \"thermistors.h\"\n\n\n"); + + writeArray(rpms, out, prefix + "rpm"); + + Set mafs = data.getYAxis(rpms.get(0)).getYs(); + ArrayList yArray = new ArrayList(mafs); + writeArray(yArray, out, prefix + "maf"); + + out.write("static float " + prefix + "table[" + rpms.size() + "][" + mafs.size() + "] = {\n"); + + boolean isFirstX = true; + int indexX = 0; + for (double x : data.getXSet()) { + if (!isFirstX) + out.write(",\n"); + isFirstX = false; + + out.write("{"); + + int indexY = 0; + for (double y : mafs) { + if (indexY == 0) + out.write("/*" + indexX + " rpm=" + rpms.get(indexX) + "*/"); + + if (indexY > 0) + out.write(", "); + + out.write("/*" + indexY + " " + yArray.get(indexY) + "*/" + data.getValue(x, y)); + indexY++; + } + out.write("}"); + indexX++; + } + out.write("\n};\n"); + + out.close(); + } + + private static void writeArray(List rpms, BufferedWriter out, String title) throws IOException { + out.write("#define " + title.toUpperCase() + "_COUNT " + rpms.size() + "\n"); + + outputDoubles(rpms, out, title); + } + + private static void outputDoubles(List values, BufferedWriter out, String title) throws IOException { + out.write("static float " + title + "_table[] = {"); + + for (int i = 0; i < values.size(); i++) { + if (i > 0) + out.write(", "); + out.write("/*" + i + "*/ " + values.get(i)); + + } + out.write("};\n\n"); + } +} diff --git a/java_console/io/src/com/rusefi/io/CommandQueue.java b/java_console/io/src/com/rusefi/io/CommandQueue.java new file mode 100644 index 0000000000..860eac1acf --- /dev/null +++ b/java_console/io/src/com/rusefi/io/CommandQueue.java @@ -0,0 +1,147 @@ +package com.rusefi.io; + +import com.irnems.core.MessagesCentral; +import org.jetbrains.annotations.NotNull; + +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; + +/** + * This class keeps re-sending a command till a proper confirmation is received + *

+ * Date: 1/7/13 + * (c) Andrey Belomutskiy + */ +@SuppressWarnings("FieldCanBeLocal") +public class CommandQueue { + private static final String CONFIRMATION_PREFIX = "confirmation_"; + public static final int DEFAULT_TIMEOUT = 300; + private final Object lock = new Object(); + private String latestConfirmation; + + private static final CommandQueue instance = new CommandQueue(); + private final BlockingQueue pendingCommands = new LinkedBlockingQueue(); + + private final Runnable runnable = new Runnable() { + @SuppressWarnings("InfiniteLoopStatement") + @Override + public void run() { + MessagesCentral.getInstance().postMessage(CommandQueue.class, "SerialIO started"); + while (true) { + try { + sendPendingCommand(); + } catch (InterruptedException e) { + e.printStackTrace(); + throw new IllegalStateException(e); + } + } + } + }; + + /** + * this method is always invoked on 'Commands Queue' thread {@link #runnable} + * + * @throws InterruptedException + */ + private void sendPendingCommand() throws InterruptedException { + /** + * here we block in case there is no command to send + */ + @NotNull + final MethodInvocation command = pendingCommands.take(); + // got a command? let's send it! + sendCommand(command); + } + + /** + * this method keeps retrying till a confirmation is received + */ + private void sendCommand(final MethodInvocation pair) throws InterruptedException { + int counter = 0; + latestConfirmation = null; + String command = pair.getText(); + + while (!command.equals(latestConfirmation)) { + counter++; + LinkManager.send(command); + synchronized (lock) { + lock.wait(pair.getTimeout()); + } + } + if (command.equals(latestConfirmation)) + pair.listener.onCommandConfirmation(); + + if (counter != 1) + MessagesCentral.getInstance().postMessage(CommandQueue.class, "Took " + counter + " attempts"); + } + + private CommandQueue() { + new Thread(runnable, "Commands Queue").start(); + final MessagesCentral mc = MessagesCentral.getInstance(); + mc.addListener(new MessagesCentral.MessageListener() { + @Override + public void onMessage(Class clazz, String message) { + if (message.startsWith(CONFIRMATION_PREFIX)) + handleConfirmationMessage(message, mc); + } + }); + } + + private void handleConfirmationMessage(String message, MessagesCentral mc) { + String confirmation = message.substring(CONFIRMATION_PREFIX.length()); + int index = confirmation.indexOf(":"); + if (index < 0) { + mc.postMessage(CommandQueue.class, "Broken confirmation: " + confirmation); + return; + } + int length = Integer.parseInt(confirmation.substring(index + 1)); + if (length != index) { + mc.postMessage(CommandQueue.class, "Broken confirmation length: " + confirmation); + return; + } + latestConfirmation = confirmation.substring(0, length); + mc.postMessage(CommandQueue.class, "got valid conf! " + latestConfirmation); + synchronized (lock) { + lock.notifyAll(); + } + } + + public static CommandQueue getInstance() { + return instance; + } + + public void write(String command) { + write(command, DEFAULT_TIMEOUT); + } + + public void write(String command, int timeout) { + write(command, timeout, InvocationConfirmationListener.VOID); + } + + /** + * Non-blocking command request + */ + public void write(String command, int timeout, InvocationConfirmationListener listener) { + pendingCommands.add(new MethodInvocation(command, timeout, listener)); + } + + static class MethodInvocation { + private final String text; + private final int timeout; + private final InvocationConfirmationListener listener; + + MethodInvocation(String text, int timeout, InvocationConfirmationListener listener) { + this.text = text; + this.timeout = timeout; + this.listener = listener; + } + + public String getText() { + return text; + } + + public int getTimeout() { + return timeout; + } + } +} diff --git a/java_console/io/src/com/rusefi/io/DataListener.java b/java_console/io/src/com/rusefi/io/DataListener.java new file mode 100644 index 0000000000..524831c7e4 --- /dev/null +++ b/java_console/io/src/com/rusefi/io/DataListener.java @@ -0,0 +1,9 @@ +package com.rusefi.io; + +/** +* @author Andrey Belomutskiy +* 3/3/14 +*/ +public interface DataListener { + void onStringArrived(String string); +} diff --git a/java_console/io/src/com/rusefi/io/InvocationConfirmationListener.java b/java_console/io/src/com/rusefi/io/InvocationConfirmationListener.java new file mode 100644 index 0000000000..3d6b35108d --- /dev/null +++ b/java_console/io/src/com/rusefi/io/InvocationConfirmationListener.java @@ -0,0 +1,15 @@ +package com.rusefi.io; + +/** + * 3/8/14 + * (c) Andrey Belomutskiy + */ +public interface InvocationConfirmationListener { + InvocationConfirmationListener VOID = new InvocationConfirmationListener() { + @Override + public void onCommandConfirmation() { + } + }; + + void onCommandConfirmation(); +} diff --git a/java_console/io/src/com/rusefi/io/LinkConnector.java b/java_console/io/src/com/rusefi/io/LinkConnector.java new file mode 100644 index 0000000000..60954c9939 --- /dev/null +++ b/java_console/io/src/com/rusefi/io/LinkConnector.java @@ -0,0 +1,13 @@ +package com.rusefi.io; + +/** + * @author Andrey Belomutskiy + * 3/3/14 + */ +public interface LinkConnector { + void connect(); + + void send(String command) throws InterruptedException; + + void restart(); +} diff --git a/java_console/io/src/com/rusefi/io/LinkManager.java b/java_console/io/src/com/rusefi/io/LinkManager.java new file mode 100644 index 0000000000..61c4044388 --- /dev/null +++ b/java_console/io/src/com/rusefi/io/LinkManager.java @@ -0,0 +1,67 @@ +package com.rusefi.io; + +import com.irnems.FileLog; +import com.irnems.core.EngineState; +import com.rusefi.io.serial.SerialConnector; +import com.rusefi.io.tcp.TcpConnector; + +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; + +/** + * @author Andrey Belomutskiy + * 3/3/14 + */ +public class LinkManager { + public final static Executor IO_EXECUTOR = Executors.newSingleThreadExecutor(new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread t = new Thread(r); + t.setName("IO executor thread"); + t.setDaemon(true); // need daemon thread so that COM thread is also daemon + return t; + } + }); + public static EngineState engineState = new EngineState(new EngineState.EngineStateListenerImpl() { + @Override + public void beforeLine(String fullLine) { + FileLog.rlog("SerialManager.beforeLine: " + fullLine); + FileLog.MAIN.logLine(fullLine); + } + }); + public static boolean onlyUI = false; + private static LinkConnector connector; + + public static void start(String port) { + if (TcpConnector.isTcpPort(port)) { + connector = new TcpConnector(port); + } else { + connector = new SerialConnector(port); + } + } + + public static void open() { + if (connector == null) + throw new NullPointerException("connector"); + connector.connect(); + } + + public static void stop() { +// connector.stop(); + } + + public static void send(String command) throws InterruptedException { + if (connector == null) + throw new NullPointerException("connector"); + connector.send(encodeCommand(command)); + } + + private static String encodeCommand(String command) { + return "sec!" + command.length() + "!" + command; + } + + public static void restart() { + connector.restart(); + } +} diff --git a/java_console/io/src/com/rusefi/io/serial/PortHolder.java b/java_console/io/src/com/rusefi/io/serial/PortHolder.java new file mode 100644 index 0000000000..dc4bd41728 --- /dev/null +++ b/java_console/io/src/com/rusefi/io/serial/PortHolder.java @@ -0,0 +1,119 @@ +package com.rusefi.io.serial; + +import com.irnems.FileLog; +import com.irnems.core.EngineState; +import com.irnems.core.MessagesCentral; +import com.rusefi.io.CommandQueue; +import com.rusefi.io.DataListener; +import jssc.SerialPort; +import jssc.SerialPortException; +import org.jetbrains.annotations.Nullable; + +/** + * This class holds the reference to the actual Serial port object + *

+ * 7/25/13 + * (c) Andrey Belomutskiy + */ +class PortHolder { + // private static final int BAUD_RATE = 8 * 115200;// 921600; +// private static final int BAUD_RATE = 2 * 115200; + private static final int BAUD_RATE = 115200; + private static final int SECOND = 1000; + private static final int MINUTE = 60 * SECOND; + private static PortHolder instance = new PortHolder(); + private final Object portLock = new Object(); + + public static long startedAt = System.currentTimeMillis(); + + private PortHolder() { + } + + @Nullable + private SerialPort serialPort; + + void openPort(String port, final EngineState es) { + MessagesCentral.getInstance().postMessage(SerialManager.class, "Opening port: " + port); + if (port == null) + return; + open(port, new DataListener() { + public void onStringArrived(String string) { + // jTextAreaIn.append(string); + es.append(string); + } + }); + } + + public boolean open(String port, DataListener listener) { + SerialPort serialPort = new SerialPort(port); + try { + FileLog.rlog("Opening " + port + " @ " + BAUD_RATE); + boolean opened = serialPort.openPort();//Open serial port + if (!opened) + FileLog.rlog("opened: " + opened); + serialPort.setParams(BAUD_RATE, 8, 1, 0);//Set params. + int mask = SerialPort.MASK_RXCHAR; + //Set the prepared mask + serialPort.setEventsMask(mask); + serialPort.addEventListener(new SerialPortReader(serialPort, listener)); + } catch (SerialPortException e) { + FileLog.rlog("ERROR " + e.getMessage()); + return false; + } + + try { + Thread.sleep(200); + } catch (InterruptedException e) { + throw new IllegalStateException(e); + } + + synchronized (portLock) { + this.serialPort = serialPort; + portLock.notifyAll(); + } + return true; + } + + public void close() { + synchronized (portLock) { + if (serialPort != null) { + try { + serialPort.closePort(); + serialPort = null; + } catch (SerialPortException e) { + FileLog.rlog("Error while closing: " + e); + } finally { + portLock.notifyAll(); + } + } + } + } + + /** + * this method blocks till a connection is available + */ + public void packAndSend(String command) throws InterruptedException { + FileLog.rlog("Sending [" + command + "]"); + MessagesCentral.getInstance().postMessage(CommandQueue.class, "Sending [" + command + "]"); + + long now = System.currentTimeMillis(); + + synchronized (portLock) { + while (serialPort == null) { + if (System.currentTimeMillis() - now > 3 * MINUTE) + MessagesCentral.getInstance().postMessage(PortHolder.class, "Looks like connection is gone :("); + portLock.wait(MINUTE); + } + // we are here only when serialPort!=null, that means we have a connection + try { + serialPort.writeString(command + "\r\n"); + } catch (SerialPortException e) { + throw new IllegalStateException(e); + } + } + } + + public static PortHolder getInstance() { + return instance; + } +} diff --git a/java_console/io/src/com/rusefi/io/serial/SerialConnector.java b/java_console/io/src/com/rusefi/io/serial/SerialConnector.java new file mode 100644 index 0000000000..7d5683469a --- /dev/null +++ b/java_console/io/src/com/rusefi/io/serial/SerialConnector.java @@ -0,0 +1,28 @@ +package com.rusefi.io.serial; + +import com.rusefi.io.LinkConnector; + +/** + * @author Andrey Belomutskiy + * 3/3/14 + */ +public class SerialConnector implements LinkConnector { + public SerialConnector(String serialPort) { + SerialManager.port = serialPort; + } + + @Override + public void connect() { + SerialManager.scheduleOpening(); + } + + @Override + public void restart() { + SerialManager.restart(); + } + + @Override + public void send(String command) throws InterruptedException { + PortHolder.getInstance().packAndSend(command); + } +} diff --git a/java_console/io/src/com/rusefi/io/serial/SerialManager.java b/java_console/io/src/com/rusefi/io/serial/SerialManager.java new file mode 100644 index 0000000000..e74c61b906 --- /dev/null +++ b/java_console/io/src/com/rusefi/io/serial/SerialManager.java @@ -0,0 +1,74 @@ +package com.rusefi.io.serial; + +import com.irnems.FileLog; +import com.irnems.core.MessagesCentral; +import com.rusefi.io.LinkManager; + +/** + * 7/9/13 + * (c) Andrey Belomutskiy + */ +class SerialManager { + public static String port; + + private static boolean closed; + + public static void scheduleOpening() { + FileLog.rlog("scheduleOpening"); + LinkManager.IO_EXECUTOR.execute(new Runnable() { + @Override + public void run() { + FileLog.rlog("scheduleOpening>openPort"); + PortHolder.getInstance().openPort(port, LinkManager.engineState); + } + }); + } + + public static void restart() { + LinkManager.IO_EXECUTOR.execute(new Runnable() { + @Override + public void run() { + MessagesCentral.getInstance().postMessage(SerialManager.class, "Restarting serial IO"); + if (closed) + return; + PortHolder.getInstance().close(); + PortHolder.getInstance().openPort(port, LinkManager.engineState); + } + }); + } +/* + static String[] findSerialPorts() { + List result = new ArrayList(); + + Enumeration portEnum = CommPortIdentifier.getPortIdentifiers(); + while (portEnum.hasMoreElements()) { + CommPortIdentifier portIdentifier = portEnum.nextElement(); + System.out.println(portIdentifier.getName() + " - " + getPortTypeName(portIdentifier.getPortType())); + if (portIdentifier.getPortType() == CommPortIdentifier.PORT_SERIAL) + result.add(portIdentifier.getName()); + } + return result.toArray(new String[result.size()]); + } + + static String getPortTypeName(int portType) { + switch (portType) { + case CommPortIdentifier.PORT_I2C: + return "I2C"; + case CommPortIdentifier.PORT_PARALLEL: + return "Parallel"; + case CommPortIdentifier.PORT_RAW: + return "Raw"; + case CommPortIdentifier.PORT_RS485: + return "RS485"; + case CommPortIdentifier.PORT_SERIAL: + return "Serial"; + default: + return "unknown type"; + } + } + */ + // public static void close() { +// closed = true; +// SerialIO.getInstance().stop(); +// } +} diff --git a/java_console/io/src/com/rusefi/io/serial/SerialPortReader.java b/java_console/io/src/com/rusefi/io/serial/SerialPortReader.java new file mode 100644 index 0000000000..a8017ea81e --- /dev/null +++ b/java_console/io/src/com/rusefi/io/serial/SerialPortReader.java @@ -0,0 +1,44 @@ +package com.rusefi.io.serial; + +import com.irnems.FileLog; +import com.rusefi.io.DataListener; +import jssc.SerialPort; +import jssc.SerialPortEvent; +import jssc.SerialPortEventListener; +import jssc.SerialPortException; + +/** + * Date: 12/25/12 + * (c) Andrey Belomutskiy + */ +class SerialPortReader implements SerialPortEventListener { + private SerialPort serialPort; + private DataListener listener; + + public SerialPortReader(SerialPort serialPort, DataListener listener) { + this.serialPort = serialPort; + this.listener = listener; + } + + public void serialEvent(SerialPortEvent spe) { + if (spe.isRXCHAR() || spe.isRXFLAG()) { + try { + handleRx(spe); + } catch (SerialPortException e) { + e.printStackTrace(System.err); + } + } else { + FileLog.rlog("SerialPortReader serialEvent " + spe); + } + } + + private void handleRx(SerialPortEvent spe) throws SerialPortException { + if (spe.getEventValue() > 0) { + byte[] buffer = serialPort.readBytes(spe.getEventValue()); + String str = new String(buffer); + listener.onStringArrived(str); + // System.out.println("arrived [" + str + "]"); + } + } + +} diff --git a/java_console/io/src/com/rusefi/io/tcp/TcpConnector.java b/java_console/io/src/com/rusefi/io/tcp/TcpConnector.java new file mode 100644 index 0000000000..fd4bea3bf7 --- /dev/null +++ b/java_console/io/src/com/rusefi/io/tcp/TcpConnector.java @@ -0,0 +1,102 @@ +package com.rusefi.io.tcp; + +import com.irnems.FileLog; +import com.rusefi.io.LinkConnector; +import com.rusefi.io.LinkManager; + +import java.io.*; +import java.net.Socket; +import java.util.Collection; +import java.util.Collections; + +/** + * @author Andrey Belomutskiy + * 3/3/14 + */ +public class TcpConnector implements LinkConnector { + public final static int DEFAULT_PORT = 29001; + public static final String LOCALHOST = "localhost"; + private final int port; + private BufferedWriter writer; + + public TcpConnector(String port) { + this.port = getTcpPort(port); + } + + public static boolean isTcpPort(String port) { + try { + getTcpPort(port); + return true; + } catch (NumberFormatException e) { + return false; + } + } + + public static int getTcpPort(String port) { + return Integer.parseInt(port); + } + + /** + * this implementation is blocking + */ + @Override + public void connect() { + FileLog.rlog("Connecting to " + port); + BufferedInputStream stream; + try { + Socket socket = new Socket(LOCALHOST, port); + writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); + stream = new BufferedInputStream(socket.getInputStream()); + } catch (IOException e) { + throw new IllegalStateException("Failed to connect to simulator", e); + } + + final BufferedReader reader = new BufferedReader(new InputStreamReader(stream)); + + LinkManager.IO_EXECUTOR.execute(new Runnable() { + @Override + public void run() { + Thread.currentThread().setName("TCP connector loop"); + FileLog.rlog("Running TCP connection loop"); + while (true) { + try { + String line = reader.readLine(); + LinkManager.engineState.append(line + "\r\n"); + } catch (IOException e) { + System.err.println("End of connection"); + return; + } + } + } + }); + + } + + @Override + public void restart() { +// FileLog.rlog("Restarting on " + port); + } + + @Override + public void send(String command) throws InterruptedException { + FileLog.rlog("Writing " + command); + try { + writer.write(command + "\r\n"); + writer.flush(); + } catch (IOException e) { + System.err.println("err in send"); + e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + } + } + + public static Collection getAvailablePorts() { + try { + Socket s = new Socket(LOCALHOST, DEFAULT_PORT); + s.close(); + return Collections.singletonList("" + DEFAULT_PORT); + } catch (IOException e) { + System.out.println("Connection refused in getAvailablePorts(): simulator not running"); + return Collections.emptyList(); + } + } +} diff --git a/java_console/lib/SteelSeries-3.9.30.jar b/java_console/lib/SteelSeries-3.9.30.jar new file mode 100644 index 0000000000..db9639043f Binary files /dev/null and b/java_console/lib/SteelSeries-3.9.30.jar differ diff --git a/java_console/lib/SteelSeries-Swing-master.zip b/java_console/lib/SteelSeries-Swing-master.zip new file mode 100644 index 0000000000..fb2e8736d8 Binary files /dev/null and b/java_console/lib/SteelSeries-Swing-master.zip differ diff --git a/java_console/lib/annotations.jar b/java_console/lib/annotations.jar new file mode 100644 index 0000000000..aed48f45d8 Binary files /dev/null and b/java_console/lib/annotations.jar differ diff --git a/java_console/lib/batik-src-1.7.zip b/java_console/lib/batik-src-1.7.zip new file mode 100644 index 0000000000..2ad443d997 Binary files /dev/null and b/java_console/lib/batik-src-1.7.zip differ diff --git a/java_console/lib/batik/LICENSE.dom-documentation.txt b/java_console/lib/batik/LICENSE.dom-documentation.txt new file mode 100644 index 0000000000..966651b390 --- /dev/null +++ b/java_console/lib/batik/LICENSE.dom-documentation.txt @@ -0,0 +1,86 @@ +xml-commons/java/external/LICENSE.dom-documentation.txt $Id: LICENSE.dom-documentation.txt 201084 2002-12-09 16:15:21Z vhardy $ + + +This license came from: http://www.w3.org/Consortium/Legal/copyright-documents-19990405 + + +W3C® DOCUMENT NOTICE AND LICENSE +Copyright © 1994-2001 World +Wide Web Consortium, World +Wide Web Consortium, (Massachusetts Institute of +Technology, Institut National de +Recherche en Informatique et en Automatique, Keio University). All Rights Reserved. +http://www.w3.org/Consortium/Legal/ + +Public documents on the W3C site are provided by the copyright +holders under the following license. The software or Document Type +Definitions (DTDs) associated with W3C specifications are governed +by the Software Notice. By using and/or copying this document, or the +W3C document from which this statement is linked, you (the +licensee) agree that you have read, understood, and will comply +with the following terms and conditions: + +Permission to use, copy, and distribute the contents of this +document, or the W3C document from which this statement is linked, +in any medium for any purpose and without fee or royalty is hereby +granted, provided that you include the following on ALL +copies of the document, or portions thereof, that you use: + +A link or URL to the original W3C document. + +The pre-existing copyright notice of the original author, or if +it doesn't exist, a notice of the form: "Copyright © [$date-of-document] World Wide Web +Consortium, (Massachusetts +Institute of Technology, Institut National de Recherche en Informatique et en +Automatique, Keio +University). All Rights Reserved. +http://www.w3.org/Consortium/Legal/" (Hypertext is preferred, but a +textual representation is permitted.) + +If it exists, the STATUS of the W3C document. + +When space permits, inclusion of the full text of this NOTICE +should be provided. We request that authorship +attribution be provided in any software, documents, or other items +or products that you create pursuant to the implementation of the +contents of this document, or any portion thereof. + +No right to create modifications or derivatives of W3C documents +is granted pursuant to this license. However, if additional +requirements (documented in the Copyright +FAQ) are satisfied, the right to create modifications or +derivatives is sometimes granted by the W3C to individuals +complying with those requirements. + +THIS DOCUMENT IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO +REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING, BUT +NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE, NON-INFRINGEMENT, OR TITLE; THAT THE CONTENTS +OF THE DOCUMENT ARE SUITABLE FOR ANY PURPOSE; NOR THAT THE +IMPLEMENTATION OF SUCH CONTENTS WILL NOT INFRINGE ANY THIRD PARTY +PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. +COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, +SPECIAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE +DOCUMENT OR THE PERFORMANCE OR IMPLEMENTATION OF THE CONTENTS +THEREOF. + +The name and trademarks of copyright holders may NOT be used in +advertising or publicity pertaining to this document or its +contents without specific, written prior permission. Title to +copyright in this document will at all times remain with copyright +holders. + +---------------------------------------------------------------------------- +This formulation of W3C's notice and license became active on +April 05 1999 so as to account for the treatment of DTDs, schema's and +bindings. See the older formulation for the policy prior to this date. +Please see +our Copyright FAQ for common questions +about using materials from our site, including specific terms and +conditions for packages like libwww, Amaya, and Jigsaw. +Other questions about this notice can be directed to site-policy@w3.org. + +webmaster +(last updated by reagle on 1999/04/99.) \ No newline at end of file diff --git a/java_console/lib/batik/LICENSE.dom-software.txt b/java_console/lib/batik/LICENSE.dom-software.txt new file mode 100644 index 0000000000..2b5bfebb1f --- /dev/null +++ b/java_console/lib/batik/LICENSE.dom-software.txt @@ -0,0 +1,74 @@ +xml-commons/java/external/LICENSE.dom-software.txt $Id: LICENSE.dom-software.txt 201084 2002-12-09 16:15:21Z vhardy $ + + +This license came from: http://www.w3.org/Consortium/Legal/copyright-software-19980720 + + +W3C® SOFTWARE NOTICE AND LICENSE +Copyright © 1994-2001 World +Wide Web Consortium, World +Wide Web Consortium, (Massachusetts Institute of +Technology, Institut National de +Recherche en Informatique et en Automatique, Keio University). All Rights Reserved. +http://www.w3.org/Consortium/Legal/ + +This W3C work (including software, documents, or other related +items) is being provided by the copyright holders under the +following license. By obtaining, using and/or copying this work, +you (the licensee) agree that you have read, understood, and will +comply with the following terms and conditions: +Permission to use, copy, modify, and distribute this software +and its documentation, with or without modification,  for any +purpose and without fee or royalty is hereby granted, provided that +you include the following on ALL copies of the software and +documentation or portions thereof, including modifications, that +you make: + +The full text of this NOTICE in a location viewable to users of +the redistributed or derivative work. + +Any pre-existing intellectual property disclaimers, notices, or +terms and conditions. If none exist, a short notice of the +following form (hypertext is preferred, text is permitted) should +be used within the body of any redistributed or derivative code: +"Copyright © [$date-of-software] World Wide Web Consortium, (Massachusetts Institute of +Technology, Institut National de +Recherche en Informatique et en Automatique, Keio University). All Rights Reserved. +http://www.w3.org/Consortium/Legal/" + +Notice of any changes or modifications to the W3C files, +including the date changes were made. (We recommend you provide +URIs to the location from which the code is derived.) + +THIS SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS," AND +COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO, WARRANTIES OF +MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE +USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY THIRD +PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. +COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, +SPECIAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE +SOFTWARE OR DOCUMENTATION. + +The name and trademarks of copyright holders may NOT be used in +advertising or publicity pertaining to the software without +specific, written prior permission. Title to copyright in this +software and any associated documentation will at all times remain +with copyright holders. +____________________________________ +This formulation of W3C's notice and license became active on +August 14 1998 so as to improve compatibility with GPL. This +version ensures that W3C software licensing terms are no more +restrictive than GPL and consequently W3C software may be +distributed in GPL packages. See the older formulation for the +policy prior to this date. Please see our Copyright FAQ for common +questions about using materials from +our site, including specific terms and conditions for packages like +libwww, Amaya, and Jigsaw. +Other questions about this notice can be +directed to site-policy@w3.org. + +webmaster +(last updated $Date: 2002-12-10 03:15:21 +1100 (Tue, 10 Dec 2002) $) \ No newline at end of file diff --git a/java_console/lib/batik/LICENSE.js.txt b/java_console/lib/batik/LICENSE.js.txt new file mode 100644 index 0000000000..817c87bcb6 --- /dev/null +++ b/java_console/lib/batik/LICENSE.js.txt @@ -0,0 +1,890 @@ +This distribution includes a binary distribution of Mozilla Rhino 1.6 release 5 +plus one patch. + +You can get the unpatched 1.6R5 release of Rhino from the following URL: + + ftp://ftp.mozilla.org/pub/mozilla.org/js/rhino1_6R5.zip + +To obtain the source code for the 1.6R5 release of Rhino, issue the following +commands: + + cvs -d :pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot \ + co -D2006-11-20 mozilla/js/rhino + +The patch is available here: + + https://bugzilla.mozilla.org/attachment.cgi?id=288467 + +which is attached to this bug: + + https://bugzilla.mozilla.org/show_bug.cgi?id=367627 + +Rhino is licensed under both the MPL (Mozilla Public License) 1.1 and the +GPL (GNU General Public License) 2.0, which are duplicated below. + +The Rhino jar also includes four classes: + org.mozilla.javascript.tools.debugger.downloaded.AbstractCellEditor.java + org.mozilla.javascript.tools.debugger.downloaded.JTreeTable.java + org.mozilla.javascript.tools.debugger.downloaded.TreeTableModel.java + org.mozilla.javascript.tools.debugger.downloaded.TreeTableModelAdapter.java +Which come from: + http://java.sun.com/products/jfc/tsc/articles/treetable2 + +Under the following license: + +Code sample +License +Copyright 1994-2006 Sun Microsystems, Inc. All Rights Reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + + * Redistribution of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistribution in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + +Neither the name of Sun Microsystems, Inc. or the names of +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +This software is provided "AS IS," without a warranty of any kind. ALL +EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, +INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN +MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR +ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR +DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR +ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR +DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE +DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, +ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF +SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +You acknowledge that this software is not designed, licensed or +intended for use in the design, construction, operation or maintenance +of any nuclear facility. + + + +============================================================================== + + MOZILLA PUBLIC LICENSE + Version 1.1 + + --------------- + +1. Definitions. + + 1.0.1. "Commercial Use" means distribution or otherwise making the + Covered Code available to a third party. + + 1.1. "Contributor" means each entity that creates or contributes to + the creation of Modifications. + + 1.2. "Contributor Version" means the combination of the Original + Code, prior Modifications used by a Contributor, and the Modifications + made by that particular Contributor. + + 1.3. "Covered Code" means the Original Code or Modifications or the + combination of the Original Code and Modifications, in each case + including portions thereof. + + 1.4. "Electronic Distribution Mechanism" means a mechanism generally + accepted in the software development community for the electronic + transfer of data. + + 1.5. "Executable" means Covered Code in any form other than Source + Code. + + 1.6. "Initial Developer" means the individual or entity identified + as the Initial Developer in the Source Code notice required by Exhibit + A. + + 1.7. "Larger Work" means a work which combines Covered Code or + portions thereof with code not governed by the terms of this License. + + 1.8. "License" means this document. + + 1.8.1. "Licensable" means having the right to grant, to the maximum + extent possible, whether at the time of the initial grant or + subsequently acquired, any and all of the rights conveyed herein. + + 1.9. "Modifications" means any addition to or deletion from the + substance or structure of either the Original Code or any previous + Modifications. When Covered Code is released as a series of files, a + Modification is: + A. Any addition to or deletion from the contents of a file + containing Original Code or previous Modifications. + + B. Any new file that contains any part of the Original Code or + previous Modifications. + + 1.10. "Original Code" means Source Code of computer software code + which is described in the Source Code notice required by Exhibit A as + Original Code, and which, at the time of its release under this + License is not already Covered Code governed by this License. + + 1.10.1. "Patent Claims" means any patent claim(s), now owned or + hereafter acquired, including without limitation, method, process, + and apparatus claims, in any patent Licensable by grantor. + + 1.11. "Source Code" means the preferred form of the Covered Code for + making modifications to it, including all modules it contains, plus + any associated interface definition files, scripts used to control + compilation and installation of an Executable, or source code + differential comparisons against either the Original Code or another + well known, available Covered Code of the Contributor's choice. The + Source Code can be in a compressed or archival form, provided the + appropriate decompression or de-archiving software is widely available + for no charge. + + 1.12. "You" (or "Your") means an individual or a legal entity + exercising rights under, and complying with all of the terms of, this + License or a future version of this License issued under Section 6.1. + For legal entities, "You" includes any entity which controls, is + controlled by, or is under common control with You. For purposes of + this definition, "control" means (a) the power, direct or indirect, + to cause the direction or management of such entity, whether by + contract or otherwise, or (b) ownership of more than fifty percent + (50%) of the outstanding shares or beneficial ownership of such + entity. + +2. Source Code License. + + 2.1. The Initial Developer Grant. + The Initial Developer hereby grants You a world-wide, royalty-free, + non-exclusive license, subject to third party intellectual property + claims: + (a) under intellectual property rights (other than patent or + trademark) Licensable by Initial Developer to use, reproduce, + modify, display, perform, sublicense and distribute the Original + Code (or portions thereof) with or without Modifications, and/or + as part of a Larger Work; and + + (b) under Patents Claims infringed by the making, using or + selling of Original Code, to make, have made, use, practice, + sell, and offer for sale, and/or otherwise dispose of the + Original Code (or portions thereof). + + (c) the licenses granted in this Section 2.1(a) and (b) are + effective on the date Initial Developer first distributes + Original Code under the terms of this License. + + (d) Notwithstanding Section 2.1(b) above, no patent license is + granted: 1) for code that You delete from the Original Code; 2) + separate from the Original Code; or 3) for infringements caused + by: i) the modification of the Original Code or ii) the + combination of the Original Code with other software or devices. + + 2.2. Contributor Grant. + Subject to third party intellectual property claims, each Contributor + hereby grants You a world-wide, royalty-free, non-exclusive license + + (a) under intellectual property rights (other than patent or + trademark) Licensable by Contributor, to use, reproduce, modify, + display, perform, sublicense and distribute the Modifications + created by such Contributor (or portions thereof) either on an + unmodified basis, with other Modifications, as Covered Code + and/or as part of a Larger Work; and + + (b) under Patent Claims infringed by the making, using, or + selling of Modifications made by that Contributor either alone + and/or in combination with its Contributor Version (or portions + of such combination), to make, use, sell, offer for sale, have + made, and/or otherwise dispose of: 1) Modifications made by that + Contributor (or portions thereof); and 2) the combination of + Modifications made by that Contributor with its Contributor + Version (or portions of such combination). + + (c) the licenses granted in Sections 2.2(a) and 2.2(b) are + effective on the date Contributor first makes Commercial Use of + the Covered Code. + + (d) Notwithstanding Section 2.2(b) above, no patent license is + granted: 1) for any code that Contributor has deleted from the + Contributor Version; 2) separate from the Contributor Version; + 3) for infringements caused by: i) third party modifications of + Contributor Version or ii) the combination of Modifications made + by that Contributor with other software (except as part of the + Contributor Version) or other devices; or 4) under Patent Claims + infringed by Covered Code in the absence of Modifications made by + that Contributor. + +3. Distribution Obligations. + + 3.1. Application of License. + The Modifications which You create or to which You contribute are + governed by the terms of this License, including without limitation + Section 2.2. The Source Code version of Covered Code may be + distributed only under the terms of this License or a future version + of this License released under Section 6.1, and You must include a + copy of this License with every copy of the Source Code You + distribute. You may not offer or impose any terms on any Source Code + version that alters or restricts the applicable version of this + License or the recipients' rights hereunder. However, You may include + an additional document offering the additional rights described in + Section 3.5. + + 3.2. Availability of Source Code. + Any Modification which You create or to which You contribute must be + made available in Source Code form under the terms of this License + either on the same media as an Executable version or via an accepted + Electronic Distribution Mechanism to anyone to whom you made an + Executable version available; and if made available via Electronic + Distribution Mechanism, must remain available for at least twelve (12) + months after the date it initially became available, or at least six + (6) months after a subsequent version of that particular Modification + has been made available to such recipients. You are responsible for + ensuring that the Source Code version remains available even if the + Electronic Distribution Mechanism is maintained by a third party. + + 3.3. Description of Modifications. + You must cause all Covered Code to which You contribute to contain a + file documenting the changes You made to create that Covered Code and + the date of any change. You must include a prominent statement that + the Modification is derived, directly or indirectly, from Original + Code provided by the Initial Developer and including the name of the + Initial Developer in (a) the Source Code, and (b) in any notice in an + Executable version or related documentation in which You describe the + origin or ownership of the Covered Code. + + 3.4. Intellectual Property Matters + (a) Third Party Claims. + If Contributor has knowledge that a license under a third party's + intellectual property rights is required to exercise the rights + granted by such Contributor under Sections 2.1 or 2.2, + Contributor must include a text file with the Source Code + distribution titled "LEGAL" which describes the claim and the + party making the claim in sufficient detail that a recipient will + know whom to contact. If Contributor obtains such knowledge after + the Modification is made available as described in Section 3.2, + Contributor shall promptly modify the LEGAL file in all copies + Contributor makes available thereafter and shall take other steps + (such as notifying appropriate mailing lists or newsgroups) + reasonably calculated to inform those who received the Covered + Code that new knowledge has been obtained. + + (b) Contributor APIs. + If Contributor's Modifications include an application programming + interface and Contributor has knowledge of patent licenses which + are reasonably necessary to implement that API, Contributor must + also include this information in the LEGAL file. + + (c) Representations. + Contributor represents that, except as disclosed pursuant to + Section 3.4(a) above, Contributor believes that Contributor's + Modifications are Contributor's original creation(s) and/or + Contributor has sufficient rights to grant the rights conveyed by + this License. + + 3.5. Required Notices. + You must duplicate the notice in Exhibit A in each file of the Source + Code. If it is not possible to put such notice in a particular Source + Code file due to its structure, then You must include such notice in a + location (such as a relevant directory) where a user would be likely + to look for such a notice. If You created one or more Modification(s) + You may add your name as a Contributor to the notice described in + Exhibit A. You must also duplicate this License in any documentation + for the Source Code where You describe recipients' rights or ownership + rights relating to Covered Code. You may choose to offer, and to + charge a fee for, warranty, support, indemnity or liability + obligations to one or more recipients of Covered Code. However, You + may do so only on Your own behalf, and not on behalf of the Initial + Developer or any Contributor. You must make it absolutely clear than + any such warranty, support, indemnity or liability obligation is + offered by You alone, and You hereby agree to indemnify the Initial + Developer and every Contributor for any liability incurred by the + Initial Developer or such Contributor as a result of warranty, + support, indemnity or liability terms You offer. + + 3.6. Distribution of Executable Versions. + You may distribute Covered Code in Executable form only if the + requirements of Section 3.1-3.5 have been met for that Covered Code, + and if You include a notice stating that the Source Code version of + the Covered Code is available under the terms of this License, + including a description of how and where You have fulfilled the + obligations of Section 3.2. The notice must be conspicuously included + in any notice in an Executable version, related documentation or + collateral in which You describe recipients' rights relating to the + Covered Code. You may distribute the Executable version of Covered + Code or ownership rights under a license of Your choice, which may + contain terms different from this License, provided that You are in + compliance with the terms of this License and that the license for the + Executable version does not attempt to limit or alter the recipient's + rights in the Source Code version from the rights set forth in this + License. If You distribute the Executable version under a different + license You must make it absolutely clear that any terms which differ + from this License are offered by You alone, not by the Initial + Developer or any Contributor. You hereby agree to indemnify the + Initial Developer and every Contributor for any liability incurred by + the Initial Developer or such Contributor as a result of any such + terms You offer. + + 3.7. Larger Works. + You may create a Larger Work by combining Covered Code with other code + not governed by the terms of this License and distribute the Larger + Work as a single product. In such a case, You must make sure the + requirements of this License are fulfilled for the Covered Code. + +4. Inability to Comply Due to Statute or Regulation. + + If it is impossible for You to comply with any of the terms of this + License with respect to some or all of the Covered Code due to + statute, judicial order, or regulation then You must: (a) comply with + the terms of this License to the maximum extent possible; and (b) + describe the limitations and the code they affect. Such description + must be included in the LEGAL file described in Section 3.4 and must + be included with all distributions of the Source Code. Except to the + extent prohibited by statute or regulation, such description must be + sufficiently detailed for a recipient of ordinary skill to be able to + understand it. + +5. Application of this License. + + This License applies to code to which the Initial Developer has + attached the notice in Exhibit A and to related Covered Code. + +6. Versions of the License. + + 6.1. New Versions. + Netscape Communications Corporation ("Netscape") may publish revised + and/or new versions of the License from time to time. Each version + will be given a distinguishing version number. + + 6.2. Effect of New Versions. + Once Covered Code has been published under a particular version of the + License, You may always continue to use it under the terms of that + version. You may also choose to use such Covered Code under the terms + of any subsequent version of the License published by Netscape. No one + other than Netscape has the right to modify the terms applicable to + Covered Code created under this License. + + 6.3. Derivative Works. + If You create or use a modified version of this License (which you may + only do in order to apply it to code which is not already Covered Code + governed by this License), You must (a) rename Your license so that + the phrases "Mozilla", "MOZILLAPL", "MOZPL", "Netscape", + "MPL", "NPL" or any confusingly similar phrase do not appear in your + license (except to note that your license differs from this License) + and (b) otherwise make it clear that Your version of the license + contains terms which differ from the Mozilla Public License and + Netscape Public License. (Filling in the name of the Initial + Developer, Original Code or Contributor in the notice described in + Exhibit A shall not of themselves be deemed to be modifications of + this License.) + +7. DISCLAIMER OF WARRANTY. + + COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, + WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF + DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. + THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE + IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, + YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE + COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER + OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF + ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. + +8. TERMINATION. + + 8.1. This License and the rights granted hereunder will terminate + automatically if You fail to comply with terms herein and fail to cure + such breach within 30 days of becoming aware of the breach. All + sublicenses to the Covered Code which are properly granted shall + survive any termination of this License. Provisions which, by their + nature, must remain in effect beyond the termination of this License + shall survive. + + 8.2. If You initiate litigation by asserting a patent infringement + claim (excluding declatory judgment actions) against Initial Developer + or a Contributor (the Initial Developer or Contributor against whom + You file such action is referred to as "Participant") alleging that: + + (a) such Participant's Contributor Version directly or indirectly + infringes any patent, then any and all rights granted by such + Participant to You under Sections 2.1 and/or 2.2 of this License + shall, upon 60 days notice from Participant terminate prospectively, + unless if within 60 days after receipt of notice You either: (i) + agree in writing to pay Participant a mutually agreeable reasonable + royalty for Your past and future use of Modifications made by such + Participant, or (ii) withdraw Your litigation claim with respect to + the Contributor Version against such Participant. If within 60 days + of notice, a reasonable royalty and payment arrangement are not + mutually agreed upon in writing by the parties or the litigation claim + is not withdrawn, the rights granted by Participant to You under + Sections 2.1 and/or 2.2 automatically terminate at the expiration of + the 60 day notice period specified above. + + (b) any software, hardware, or device, other than such Participant's + Contributor Version, directly or indirectly infringes any patent, then + any rights granted to You by such Participant under Sections 2.1(b) + and 2.2(b) are revoked effective as of the date You first made, used, + sold, distributed, or had made, Modifications made by that + Participant. + + 8.3. If You assert a patent infringement claim against Participant + alleging that such Participant's Contributor Version directly or + indirectly infringes any patent where such claim is resolved (such as + by license or settlement) prior to the initiation of patent + infringement litigation, then the reasonable value of the licenses + granted by such Participant under Sections 2.1 or 2.2 shall be taken + into account in determining the amount or value of any payment or + license. + + 8.4. In the event of termination under Sections 8.1 or 8.2 above, + all end user license agreements (excluding distributors and resellers) + which have been validly granted by You or any distributor hereunder + prior to termination shall survive termination. + +9. LIMITATION OF LIABILITY. + + UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT + (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL + DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE, + OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR + ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY + CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, + WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER + COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN + INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF + LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY + RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW + PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE + EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO + THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU. + +10. U.S. GOVERNMENT END USERS. + + The Covered Code is a "commercial item," as that term is defined in + 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer + software" and "commercial computer software documentation," as such + terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 + C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), + all U.S. Government End Users acquire Covered Code with only those + rights set forth herein. + +11. MISCELLANEOUS. + + This License represents the complete agreement concerning subject + matter hereof. If any provision of this License is held to be + unenforceable, such provision shall be reformed only to the extent + necessary to make it enforceable. This License shall be governed by + California law provisions (except to the extent applicable law, if + any, provides otherwise), excluding its conflict-of-law provisions. + With respect to disputes in which at least one party is a citizen of, + or an entity chartered or registered to do business in the United + States of America, any litigation relating to this License shall be + subject to the jurisdiction of the Federal Courts of the Northern + District of California, with venue lying in Santa Clara County, + California, with the losing party responsible for costs, including + without limitation, court costs and reasonable attorneys' fees and + expenses. The application of the United Nations Convention on + Contracts for the International Sale of Goods is expressly excluded. + Any law or regulation which provides that the language of a contract + shall be construed against the drafter shall not apply to this + License. + +12. RESPONSIBILITY FOR CLAIMS. + + As between Initial Developer and the Contributors, each party is + responsible for claims and damages arising, directly or indirectly, + out of its utilization of rights under this License and You agree to + work with Initial Developer and Contributors to distribute such + responsibility on an equitable basis. Nothing herein is intended or + shall be deemed to constitute any admission of liability. + +13. MULTIPLE-LICENSED CODE. + + Initial Developer may designate portions of the Covered Code as + "Multiple-Licensed". "Multiple-Licensed" means that the Initial + Developer permits you to utilize portions of the Covered Code under + Your choice of the NPL or the alternative licenses, if any, specified + by the Initial Developer in the file described in Exhibit A. + +EXHIBIT A -Mozilla Public License. + + ``The contents of this file are subject to the Mozilla Public License + Version 1.1 (the "License"); you may not use this file except in + compliance with the License. You may obtain a copy of the License at + http://www.mozilla.org/MPL/ + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the + License for the specific language governing rights and limitations + under the License. + + The Original Code is ______________________________________. + + The Initial Developer of the Original Code is ________________________. + Portions created by ______________________ are Copyright (C) ______ + _______________________. All Rights Reserved. + + Contributor(s): ______________________________________. + + Alternatively, the contents of this file may be used under the terms + of the _____ license (the "[___] License"), in which case the + provisions of [______] License are applicable instead of those + above. If you wish to allow use of your version of this file only + under the terms of the [____] License and not to allow others to use + your version of this file under the MPL, indicate your decision by + deleting the provisions above and replace them with the notice and + other provisions required by the [___] License. If you do not delete + the provisions above, a recipient may use your version of this file + under either the MPL or the [___] License." + + [NOTE: The text of this Exhibit A may differ slightly from the text of + the notices in the Source Code files of the Original Code. You should + use the text of this Exhibit A rather than the text found in the + Original Code Source Code for Your Modifications.] + +============================================================================== + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. + +============================================================================== diff --git a/java_console/lib/batik/LICENSE.pdf-transcoder.txt b/java_console/lib/batik/LICENSE.pdf-transcoder.txt new file mode 100644 index 0000000000..588d02cf10 --- /dev/null +++ b/java_console/lib/batik/LICENSE.pdf-transcoder.txt @@ -0,0 +1,2 @@ +The pdf-transcoder.jar file is licensed under the Apache License 2.0, which +can be found in the distribution root directory in the LICENSE file. diff --git a/java_console/lib/batik/LICENSE.sax.txt b/java_console/lib/batik/LICENSE.sax.txt new file mode 100644 index 0000000000..739cb51a3b --- /dev/null +++ b/java_console/lib/batik/LICENSE.sax.txt @@ -0,0 +1,23 @@ +xml-commons/java/external/LICENSE.sax.txt $Id: LICENSE.sax.txt 201084 2002-12-09 16:15:21Z vhardy $ + + +This license came from: http://www.megginson.com/SAX/copying.html + However please note future versions of SAX may be covered + under http://saxproject.org/?selected=pd + + +This page is now out of date -- see the new SAX site at +http://www.saxproject.org/ for more up-to-date +releases and other information. Please change your bookmarks. + + +SAX2 is Free! + +I hereby abandon any property rights to SAX 2.0 (the Simple API for +XML), and release all of the SAX 2.0 source code, compiled code, and +documentation contained in this distribution into the Public Domain. +SAX comes with NO WARRANTY or guarantee of fitness for any +purpose. + +David Megginson, david@megginson.com +2000-05-05 \ No newline at end of file diff --git a/java_console/lib/batik/LICENSE.xalan-2.6.0.txt b/java_console/lib/batik/LICENSE.xalan-2.6.0.txt new file mode 100644 index 0000000000..6c5ba27951 --- /dev/null +++ b/java_console/lib/batik/LICENSE.xalan-2.6.0.txt @@ -0,0 +1,2 @@ +The xalan-2.6.0.jar file is licensed under the Apache License 2.0, which +can be found in the distribution root directory in the LICENSE file. diff --git a/java_console/lib/batik/LICENSE.xerces_2_5_0.txt b/java_console/lib/batik/LICENSE.xerces_2_5_0.txt new file mode 100644 index 0000000000..b6d4a8bc9d --- /dev/null +++ b/java_console/lib/batik/LICENSE.xerces_2_5_0.txt @@ -0,0 +1,60 @@ +The xerces_2_5_0.jar file comes from the Apache Xerces project +(http://xmlapache.org/dist/xerces-j/), and is licensed under the +Apache Software License, Version 1.1, which is reproduced below. + +/* + * The Apache Software License, Version 1.1 + * + * + * Copyright (c) 1999-2002 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, + * if any, must include the following acknowledgment: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowledgment may appear in the software itself, + * if and wherever such third-party acknowledgments normally appear. + * + * 4. The names "Xerces" and "Apache Software Foundation" must + * not be used to endorse or promote products derived from this + * software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache", + * nor may "Apache" appear in their name, without prior written + * permission of the Apache Software Foundation. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation and was + * originally based on software copyright (c) 1999, International + * Business Machines, Inc., http://www.ibm.com. For more + * information on the Apache Software Foundation, please see + * . + */ diff --git a/java_console/lib/batik/README.js.txt b/java_console/lib/batik/README.js.txt new file mode 100644 index 0000000000..72df38f49d --- /dev/null +++ b/java_console/lib/batik/README.js.txt @@ -0,0 +1,23 @@ +This distribution includes a binary distribution of Mozilla Rhino 1.6 release 5 +plus one patch. + +You can get the unpatched 1.6R5 release of Rhino from the following URL: + + ftp://ftp.mozilla.org/pub/mozilla.org/js/rhino1_6R5.zip + +To obtain the source code for the 1.6R5 release of Rhino, issue the following +commands: + + cvs -d :pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot \ + co -D2006-11-20 mozilla/js/rhino + +The patch is available here: + + https://bugzilla.mozilla.org/attachment.cgi?id=288467 + +which is attached to this bug: + + https://bugzilla.mozilla.org/show_bug.cgi?id=367627 + +Rhino is licensed under both the MPL (Mozilla Public License) 1.1 and the +GPL (GNU General Public License) 2.0, which are in the LICENSE.js.txt file. diff --git a/java_console/lib/batik/README.pdf-transcoder.txt b/java_console/lib/batik/README.pdf-transcoder.txt new file mode 100644 index 0000000000..f30d98a00e --- /dev/null +++ b/java_console/lib/batik/README.pdf-transcoder.txt @@ -0,0 +1,8 @@ +The pdf-transcoder.jar file is built from the Apache FOP project +(http://xmlgraphics.apache.org/fop), version 0.94. + +This is only needed if you want to transcode to PDF, otherwise it can +be removed. + +The pdf-transcoder.jar file is licensed under the Apache License 2.0, which +can be found in the distribution root directory in the LICENSE file. diff --git a/java_console/lib/batik/README.xalan-2.6.0.txt b/java_console/lib/batik/README.xalan-2.6.0.txt new file mode 100644 index 0000000000..8e4a408530 --- /dev/null +++ b/java_console/lib/batik/README.xalan-2.6.0.txt @@ -0,0 +1,4 @@ +The xalan-2.6.0.jar file comes from the Apache Xalan project +(http://xml.apache.org/xalan-j/), and is licensed under the +Apache License 2.0, which can be found in the distribution root directory +in the LICENSE file. diff --git a/java_console/lib/batik/README.xerces_2_5_0.txt b/java_console/lib/batik/README.xerces_2_5_0.txt new file mode 100644 index 0000000000..6090b00504 --- /dev/null +++ b/java_console/lib/batik/README.xerces_2_5_0.txt @@ -0,0 +1,4 @@ +The xerces_2_5_0.jar file comes from the Apache Xerces project +(http://xml.apache.org/dist/xerces-j/), and is licensed under the +Apache Software License, Version 1.1, which is in the +LICENSE.xerces_2_5_0.txt file. diff --git a/java_console/lib/batik/README.xml-apis-ext.txt b/java_console/lib/batik/README.xml-apis-ext.txt new file mode 100644 index 0000000000..03a0a1ea4a --- /dev/null +++ b/java_console/lib/batik/README.xml-apis-ext.txt @@ -0,0 +1,19 @@ +This distribution includes xml-apis-ext.jar from the XML Commons External +1.3.04 binary distribution, which can also be obtained from: + + http://xml.apache.org/mirrors.cgi + +Source code is available from the XML Commons web site: + + http://xml.apache.org/commons/ + +xml-apis-ext.jar contains: + + - SAC 1.3 + - SMIL Java bindings + - SVG 1.1 Java bindings + +SAC 1.3, the SMIL Java bindings and the SVG 1.1 Java bindings are licensed +under the W3C Software License. Related documentation is licensed under the +W3C Document License. See LICENSE.dom-software.txt and +LICENSE.dom-documentation.txt. diff --git a/java_console/lib/batik/README.xml-apis.txt b/java_console/lib/batik/README.xml-apis.txt new file mode 100644 index 0000000000..dcfbcdfc32 --- /dev/null +++ b/java_console/lib/batik/README.xml-apis.txt @@ -0,0 +1,30 @@ +This distribution includes xml-apis.jar from the XML Commons External +1.3.04 binary distribution, which can also be obtained from: + + http://xml.apache.org/mirrors.cgi + +Source code is available from the XML Commons web site: + + http://xml.apache.org/commons/ + +xml-apis.jar contains: + + - DOM Level 2 Events + - DOM Level 2 HTML + - DOM Level 2 Style + - DOM Level 2 Traversal and Range + - DOM Level 2 Views + - DOM Level 3 Core + - DOM Level 3 Load and Save + - DOM Level 3 XPath + - JAXP 1.3 (JSR 206) + - SAX + +All DOM code is licensed under the W3C Software License, and DOM documentation +under the W3C Document License. See LICENSE.dom-software.txt and +LICENSE.dom-documentation.txt. + +The JAXP 1.3 code is licensed under the Apache Software License 2.0, which is +in the LICENSE in the root directory of this distribution. + +SAX is public domain. See LICENSE.sax.txt. diff --git a/java_console/lib/batik/Squiggle.icns b/java_console/lib/batik/Squiggle.icns new file mode 100644 index 0000000000..b4875d6c1f Binary files /dev/null and b/java_console/lib/batik/Squiggle.icns differ diff --git a/java_console/lib/batik/batik-anim.jar b/java_console/lib/batik/batik-anim.jar new file mode 100644 index 0000000000..6913e421c9 Binary files /dev/null and b/java_console/lib/batik/batik-anim.jar differ diff --git a/java_console/lib/batik/batik-awt-util.jar b/java_console/lib/batik/batik-awt-util.jar new file mode 100644 index 0000000000..e64605af88 Binary files /dev/null and b/java_console/lib/batik/batik-awt-util.jar differ diff --git a/java_console/lib/batik/batik-bridge.jar b/java_console/lib/batik/batik-bridge.jar new file mode 100644 index 0000000000..62c10bae3d Binary files /dev/null and b/java_console/lib/batik/batik-bridge.jar differ diff --git a/java_console/lib/batik/batik-codec.jar b/java_console/lib/batik/batik-codec.jar new file mode 100644 index 0000000000..0a83c6ff19 Binary files /dev/null and b/java_console/lib/batik/batik-codec.jar differ diff --git a/java_console/lib/batik/batik-css.jar b/java_console/lib/batik/batik-css.jar new file mode 100644 index 0000000000..c1f1c9a885 Binary files /dev/null and b/java_console/lib/batik/batik-css.jar differ diff --git a/java_console/lib/batik/batik-dom.jar b/java_console/lib/batik/batik-dom.jar new file mode 100644 index 0000000000..32d5b46d05 Binary files /dev/null and b/java_console/lib/batik/batik-dom.jar differ diff --git a/java_console/lib/batik/batik-ext.jar b/java_console/lib/batik/batik-ext.jar new file mode 100644 index 0000000000..8c904e1f2a Binary files /dev/null and b/java_console/lib/batik/batik-ext.jar differ diff --git a/java_console/lib/batik/batik-extension.jar b/java_console/lib/batik/batik-extension.jar new file mode 100644 index 0000000000..106e4ac3cb Binary files /dev/null and b/java_console/lib/batik/batik-extension.jar differ diff --git a/java_console/lib/batik/batik-gui-util.jar b/java_console/lib/batik/batik-gui-util.jar new file mode 100644 index 0000000000..bf8c8441af Binary files /dev/null and b/java_console/lib/batik/batik-gui-util.jar differ diff --git a/java_console/lib/batik/batik-gvt.jar b/java_console/lib/batik/batik-gvt.jar new file mode 100644 index 0000000000..ee47ec8258 Binary files /dev/null and b/java_console/lib/batik/batik-gvt.jar differ diff --git a/java_console/lib/batik/batik-parser.jar b/java_console/lib/batik/batik-parser.jar new file mode 100644 index 0000000000..286b3799c3 Binary files /dev/null and b/java_console/lib/batik/batik-parser.jar differ diff --git a/java_console/lib/batik/batik-script.jar b/java_console/lib/batik/batik-script.jar new file mode 100644 index 0000000000..433f02e67c Binary files /dev/null and b/java_console/lib/batik/batik-script.jar differ diff --git a/java_console/lib/batik/batik-svg-dom.jar b/java_console/lib/batik/batik-svg-dom.jar new file mode 100644 index 0000000000..b4c8a620bb Binary files /dev/null and b/java_console/lib/batik/batik-svg-dom.jar differ diff --git a/java_console/lib/batik/batik-svggen.jar b/java_console/lib/batik/batik-svggen.jar new file mode 100644 index 0000000000..4d6bb14417 Binary files /dev/null and b/java_console/lib/batik/batik-svggen.jar differ diff --git a/java_console/lib/batik/batik-swing.jar b/java_console/lib/batik/batik-swing.jar new file mode 100644 index 0000000000..93e5d0f335 Binary files /dev/null and b/java_console/lib/batik/batik-swing.jar differ diff --git a/java_console/lib/batik/batik-transcoder.jar b/java_console/lib/batik/batik-transcoder.jar new file mode 100644 index 0000000000..0f2f7cd358 Binary files /dev/null and b/java_console/lib/batik/batik-transcoder.jar differ diff --git a/java_console/lib/batik/batik-util.jar b/java_console/lib/batik/batik-util.jar new file mode 100644 index 0000000000..86d75e70f2 Binary files /dev/null and b/java_console/lib/batik/batik-util.jar differ diff --git a/java_console/lib/batik/batik-xml.jar b/java_console/lib/batik/batik-xml.jar new file mode 100644 index 0000000000..d05eb25f77 Binary files /dev/null and b/java_console/lib/batik/batik-xml.jar differ diff --git a/java_console/lib/batik/js.jar b/java_console/lib/batik/js.jar new file mode 100644 index 0000000000..ccad3cc1bf Binary files /dev/null and b/java_console/lib/batik/js.jar differ diff --git a/java_console/lib/batik/make-squiggle-app.sh b/java_console/lib/batik/make-squiggle-app.sh new file mode 100644 index 0000000000..76fe888025 --- /dev/null +++ b/java_console/lib/batik/make-squiggle-app.sh @@ -0,0 +1,87 @@ +#!/bin/bash +# ----------------------------------------------------------------------------- +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# make-squiggle-app.sh +# +# Creates a Mac OS X application bundle for Squiggle, then opens a Finder +# window for the current directory so that the user can drag the icon +# into their desired installation location (probably /Applications). +# +# $Id$ +# ----------------------------------------------------------------------------- + +trap 'echo Error creating application bundle.; exit 1' ERR + +cd `dirname "$0"`/.. + +APP=Squiggle.app +CON=$APP/Contents +MAC=$CON/MacOS +RES=$CON/Resources + +[ -e $APP ] && echo $APP already exists: please move it out of the way before running this script. && exit 1 + +echo Creating $APP in `pwd`... + +mkdir $APP $CON $MAC $RES + +cat >$CON/Info.plist < + + + + CFBundleExecutable + Squiggle + CFBundleVersion + 1.7+r608262 + CFBundleShortVersionString + 1.7+r608262 + CFBundleIconFile + Squiggle.icns + CFBundleIdentifier + org.apache.batik + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + Squiggle + CFBundlePackageType + APPL + CFBundleSignature + ???? + NSHumanReadableCopyright + Copyright © 2008 Apache Software Foundation. All Rights Reserved. + + +EOF + +cat >$MAC/Squiggle < + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/java_console/models/src/com/irnems/Histograms.java b/java_console/models/src/com/irnems/Histograms.java new file mode 100644 index 0000000000..806820470b --- /dev/null +++ b/java_console/models/src/com/irnems/Histograms.java @@ -0,0 +1,492 @@ +package com.irnems; + +import org.jetbrains.annotations.NotNull; + +import java.text.FieldPosition; +import java.text.NumberFormat; +import java.util.*; + +public final class Histograms { + private static final String SQL_STATEMENT = "SQL-statement"; + public static final double H_ACCURACY = 0.05; + public static final int BOUND_LENGTH = (int) (Math.log(Long.MAX_VALUE) / Math.log(1.0 + H_ACCURACY)); + + public static final long LONG_MAX_INT = 0x7fffffffffffffffL; + + public static final double H_CONFIDENCE = 0.8; + public static final int SBI_SIZE = 1000; + + // ======= Initialization ======= + + private final HashMap total_stats = new HashMap(); + private final long start_time = System.currentTimeMillis(); + public final ThreadLocal local_stats = new ThreadLocal() { + @Override + protected LocalStats initialValue() { + return new LocalStats(); + } + }; + private final HashSet all_local_stats = new HashSet(); + + private long last_dump = System.currentTimeMillis(); + + /** + * this method updates totals & resets snapshot + * todo: get rid of TOS 'Profiler' and make this method protected? + * + * @see #getCurrentStatistics() + */ + @NotNull + public List dumpStats() { + Collection values = takeAndResetSnapshot(); + List al = new ArrayList(); + al.addAll(values); + synchronized (total_stats) { + for (StatisticsGroup source : values) { + String type = source.type + ".TOTAL"; + StatisticsGroup dest = total_stats.get(type); + if (dest == null) + total_stats.put(type, dest = new StatisticsGroup(type)); + dest.add(source); + } + al.addAll(total_stats.values()); + } + return sortAndAddTimes(al); + } + + /** + * this method displays currently gathered histogram without affecting state + * + * @see #dumpStats() + */ + @NotNull + public List getCurrentStatistics() { + Collection snapshot = getCurrentSnapshot(); + return sortAndAddTimes(new ArrayList(snapshot)); + } + + private List sortAndAddTimes(List al) { + Collections.sort(al, new Comparator() { + public int compare(StatisticsGroup o1, StatisticsGroup o2) { + return o1.type.compareTo(o2.type); + } + }); + long time = System.currentTimeMillis(); + List result = new ArrayList(); + for (StatisticsGroup sg : al) + result.add(toString(sg, time - (sg.type.endsWith(".TOTAL") ? start_time : last_dump))); + last_dump = time; + return result; + } + + // ========= Internal Data Structures ======== + + /** + * Accuracy of histogram - the ratio of bar width to its value. + */ + + /** + * Confidence interval to be printed together with min/max interval. + */ + + /** + * Confidence quantity bounds for logging. + */ + private final double[] confidence_bounds; + + /** + * Confidence interval separators for logging. + */ + private final String[] confidence_separators; + + /** + * Bounds of histogram bars. Bar number 'i' covers interval + * from bounds[i] inclusive to bounds[i + 1] exclusive. + */ + private final long[] bounds; + + /** + * Index for direct access to intervals with small bounds. + */ + private final int[] small_bounds_index; + + + /** + * Construct new instance of profiler with specified parameters. + */ + public Histograms() { + + confidence_bounds = new double[]{0.5 - H_CONFIDENCE * 0.5, 0.5, 0.5 + H_CONFIDENCE * 0.5}; + confidence_separators = new String[]{"(", " [", "-", "-", "] ", ")"}; + + FileLog.rlog("BOUND_LENGTH=" + BOUND_LENGTH); + + bounds = new long[BOUND_LENGTH]; + bounds[0] = 0; + for (int i = 1; i < BOUND_LENGTH; i++) { + long prev = bounds[i - 1]; + long next = prev + (long) ((double) prev * H_ACCURACY); + if (next == prev) // Ensure minimum step for small numbers. + next = prev + 1; + if (next < prev) // Overflow over Long.MAX_VALUE occurred. + next = LONG_MAX_INT; + bounds[i] = next; + } + bounds[BOUND_LENGTH - 1] = LONG_MAX_INT; + + small_bounds_index = new int[SBI_SIZE]; + for (int i = 0, j = 0; j < SBI_SIZE; i++) + while (j < bounds[i + 1] && j < SBI_SIZE) + small_bounds_index[j++] = i; + } + + /** + * Returns histogram index for specified value. + */ + public int getIndex(long value) { + if (value < 0) + return 0; + if (value < small_bounds_index.length) + return small_bounds_index[(int) value]; + int l = small_bounds_index[small_bounds_index.length - 1]; + int r = bounds.length - 1; + while (l < r) { + int m = (l + r) >> 1; + if (bounds[m] > value) + r = m - 1; + else if (bounds[m + 1] <= value) + l = m + 1; + else + return m; + } + return l; + } + + public static final class LocalStats { + public HashMap stats; // Type -> StatisticsGroup + } + + /** + * Adds specified samples to statistics. + */ + public void addValue(ValueType t, String name, long value) { + String type = t.getName(); + if (value < 0) + value = 0; + int index = getIndex(value); + LocalStats ls = local_stats.get(); + boolean create; + create = ls.stats == null; + if (create) + ls.stats = new HashMap(); + StatisticsGroup sg = ls.stats.get(t); + if (sg == null) + ls.stats.put(t, sg = new StatisticsGroup(type)); + sg.add(name, index, value); + if (create) + synchronized (all_local_stats) { + all_local_stats.add(ls); + } + } + + /** + * returns current statistics without resetting anything + */ + private Collection getCurrentSnapshot() { + List lss = getLocalStats(false); + HashMap snapshot = new HashMap(); + for (LocalStats ls : lss) { + // in case of a snapshot without reset, we have to merge under the lock + mergeStats(snapshot, ls.stats); + } + return snapshot.values(); + } + + /** + * Returns snapshot of all gathered statistics and clears them in process. + */ + private Collection takeAndResetSnapshot() { + List lss = getLocalStats(true); + HashMap snapshot = new HashMap(); + for (LocalStats ls : lss) { + HashMap stats; + stats = ls.stats; + /** + * we will re-register this LocalStats on next #add() invocation + * @see #add(String, String, int, long) + */ + ls.stats = null; + mergeStats(snapshot, stats); + } + return snapshot.values(); + } + + private static void mergeStats(HashMap snapshot, HashMap stats) { + if (stats != null) + for (StatisticsGroup source : stats.values()) { + StatisticsGroup dest = snapshot.get(source.type); + if (dest == null) + snapshot.put(source.type, source); + else + dest.add(source); + } + } + + private List getLocalStats(boolean reset) { + List lss; + synchronized (all_local_stats) { + lss = new ArrayList(all_local_stats); + if (reset) + all_local_stats.clear(); + } + return lss; + } + + /** + * Sorts specified statistics of specified statistics group for logging. + */ + private static void sortStatistics(StatisticsGroup sg, Statistics[] sts) { + final boolean use_total = sg.type.startsWith(SQL_STATEMENT); + Arrays.sort(sts, new Comparator() { + public int compare(Statistics st1, Statistics st2) { + if (use_total && st1.total_value != st2.total_value) + return st1.total_value < st2.total_value ? -1 : 1; + return st1.name.compareTo(st2.name); + } + }); + } + + // ========== Formatting Methods ========== + + private static final double[] multipliers = {1.0, 10.0, 100.0}; + private final NumberFormat formatter = NumberFormat.getNumberInstance(); + private final FieldPosition field = new FieldPosition(0); + + /** + * Formats specified value into specified string buffer. Not thread-safe. + */ + private StringBuffer format(double d, StringBuffer sb) { + int i = Math.abs(d) < 9.995 ? 2 : Math.abs(d) < 99.95 ? 1 : 0; + formatter.setMinimumFractionDigits(0); + formatter.setMaximumFractionDigits(i); + return formatter.format(Math.floor(d * multipliers[i] + 0.5) / multipliers[i], sb, field); + } + + /** + * Appends header for specified statistics group for logging. + */ + private void appendHeader(StringBuffer sb, StatisticsGroup sg, long duration) { + double time = duration; + String time_scale = "ms"; + if (time >= 24 * 3600 * 1000 * 0.995) { + time /= 24.0 * 3600.0 * 1000.0; + time_scale = "days"; + } else if (time >= 3600 * 1000 * 0.995) { + time /= 3600.0 * 1000.0; + time_scale = "hours"; + } else if (time >= 60 * 1000 * 0.995) { + time /= 60.0 * 1000.0; + time_scale = "min"; + } else if (time >= 1000 * 0.995) { + time /= 1000.0; + time_scale = "sec"; + } + + sb.append(sg.type).append(" statistics for "); + format(time, sb).append(time_scale).append(" with "); + format(H_ACCURACY * 100.0, sb).append("% accuracy and "); + format(H_CONFIDENCE * 100.0, sb).append("% confidence:"); + } + + /** + * Appends specified statistics (main part) for logging. + */ + public void appendStatistics(StringBuffer sb, Statistics st, List report) { + format(st.total_value, sb).append(" / ").append(st.total_count).append(" = "); + format((double) st.total_value / (double) st.total_count, sb); + + if (st.total_count < 0) { + sb.append("Total count is less then ZERO!\n"); + return; + } + + if (st.total_count <= 5) { + // There are too little samples for statistics. + sb.append(" are"); + for (int j = 0; j < st.histogram.length; j++) + for (int k = 0; k < st.histogram[j]; k++) { + sb.append(' '); + format((bounds[j] + bounds[j + 1]) / 2, sb); + } + return; + } + + // Print full min/confidence/max statistics. + sb.append(" in ").append(confidence_separators[0]); + int min = 0; + while (st.histogram[min] == 0) + min++; + // 'min' is index of interval with min sample. + format(bounds[min], sb).append(confidence_separators[1]); + report.add(bounds[min]); + + + long acc = 0; + // 'acc' is accumulated number of samples in [0, min - 1]. + for (int j = 0; j < confidence_bounds.length; j++) { + long k = (long) Math.floor((double) st.total_count * confidence_bounds[j]); + // Always drop at least 1 'non-confident' sample... + if (k == 0) + k = 1; + if (k == st.total_count) + k = st.total_count - 1; + // 'k' is desired number of samples. + while (acc + st.histogram[min] < k) + acc += st.histogram[min++]; + if (k < st.total_count / 2) // Converge to median (from left). + while (acc + st.histogram[min] <= k) + acc += st.histogram[min++]; + // Now: acc <= k <= acc + st.histogram[min] + // And desired number of samples is within [min, min + 1) + double d = bounds[min]; + if (acc != k) + d += (double) (bounds[min + 1] - 1 - bounds[min]) * + (double) (k - acc) / + (double) st.histogram[min]; + format(d, sb).append(confidence_separators[j + 2]); + report.add((long)d); + } + + int max = st.histogram.length - 1; + while (st.histogram[max] == 0) + max--; + // 'max' is index of interval with max sample. + long maxValue = bounds[max + 1] - 1; + format(maxValue, sb).append(confidence_separators[5]); + report.add(maxValue); + } + + /** + * Formats specified statistics group for logging. + */ + private String toString(StatisticsGroup sg, long duration) { + StringBuffer sb = new StringBuffer(100 + sg.data.size() * 100); + appendHeader(sb, sg, duration); + Statistics[] sts = sg.data.values().toArray(new Statistics[sg.data.size()]); + sortStatistics(sg, sts); + for (Statistics st : sts) { + appendStatistics(sb, st, new ArrayList()); + } + return sb.toString(); + } + + // ========== Internal Classes ========== + + /** + * Performance statistics for single operation. + */ + public static class Statistics { + private static final long[] EMPTY_HISTOGRAM = new long[0]; + + public final String name; + + public long total_value; + public long total_count; + public long[] histogram; + + private Statistics(String name) { + this.name = name; + this.histogram = EMPTY_HISTOGRAM; + } + + private Statistics(Statistics st) { + this.name = st.name; + this.total_value = st.total_value; + this.total_count = st.total_count; + this.histogram = st.histogram.length == 0 ? EMPTY_HISTOGRAM : st.histogram.clone(); + } + + public void add(int index, int count, long value) { + total_value += value; + total_count += count; + if (index >= histogram.length) { + int new_length = Math.max(histogram.length, 10); + while (index >= new_length) + new_length = new_length << 1; + long[] new_histogram = new long[new_length]; + System.arraycopy(histogram, 0, new_histogram, 0, histogram.length); + histogram = new_histogram; + } + histogram[index] += count; + } + + public void add(Statistics st) { + total_value += st.total_value; + total_count += st.total_count; + long[] hist = st.histogram; + if (histogram.length < st.histogram.length) { + hist = histogram; + histogram = st.histogram.clone(); + } + for (int i = hist.length; --i >= 0; ) + histogram[i] += hist[i]; + } + } + + /** + * Group of statistics of the same type. + */ + @SuppressWarnings("ForLoopReplaceableByForEach") + public static class StatisticsGroup { + public final String type; + public final HashMap data; // Maps statistics name (String) to Statistics. + + private StatisticsGroup(String type) { + this.type = type; + this.data = new HashMap(); + } + + public void add(String name, int index, long value) { + Statistics st = data.get(name); + if (st == null) + data.put(name, st = new Statistics(name)); + st.add(index, 1, value); + } + + public void add(StatisticsGroup sg) { + for (Iterator it = sg.data.values().iterator(); it.hasNext(); ) { + Statistics source = it.next(); + Statistics dest = data.get(source.name); + if (dest == null) + data.put(source.name, new Statistics(source)); + else + dest.add(source); + } + } + } + + /** + * The type of a value. + */ + public enum ValueType { + INVOCATION("Invocation"); + /** + * Value type that is used to mean a method call. + */ + + private final String name; + + /** + * Gets the name of this {@link ValueType}. + * + * @return the name. + */ + public String getName() { + return name; + } + + ValueType(String name) { + this.name = name; + } + } +} diff --git a/java_console/models/src/com/irnems/ReportLine.java b/java_console/models/src/com/irnems/ReportLine.java new file mode 100644 index 0000000000..6dfd4a7a95 --- /dev/null +++ b/java_console/models/src/com/irnems/ReportLine.java @@ -0,0 +1,48 @@ +package com.irnems; + +import com.irnems.models.MafValue; +import com.irnems.models.RpmValue; + +/** + * @author Andrey Belomutskiy + * 1/29/13 + */ +public class ReportLine { + private final int time; + private final MafValue maf; + private final RpmValue rpm; + private final int wave; + + public ReportLine(int time, MafValue maf, RpmValue rpm, int wave) { + this.time = time; + this.maf = maf; + this.rpm = rpm; + this.wave = wave; + } + + public int getTime() { + return time; + } + + public MafValue getMaf() { + return maf; + } + + public RpmValue getRpm() { + return rpm; + } + + public int getWave() { + return wave; + } + + @Override + public String toString() { + return "ReportLine{" + + "time=" + time + + ", maf=" + maf + + ", rpm=" + rpm + + ", wave=" + wave + + '}'; + } +} diff --git a/java_console/models/src/com/irnems/ReportReader.java b/java_console/models/src/com/irnems/ReportReader.java new file mode 100644 index 0000000000..33507bcace --- /dev/null +++ b/java_console/models/src/com/irnems/ReportReader.java @@ -0,0 +1,157 @@ +package com.irnems; + +import com.irnems.models.Factory; +import com.irnems.models.MafValue; +import com.irnems.models.RpmValue; +import com.irnems.models.Utils; + +import java.io.*; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Date: 1/29/13 + * (c) Andrey Belomutskiy + */ +public class ReportReader { + private static final Pattern LINE_PATTERN = Pattern.compile("\\D*(\\d*);a0;(\\d*);a1;-1;rpm;(\\d*);wave;(\\d*).*"); + private static final int INVALID_RPM_DIFF = 200; + + public static void main(String[] args) { + //List lines = read("unfiltered.txt"); + readMap("200ohm.txt"); + + } + + public static TreeMap> readMap(String filename) { + if (!new File(filename).exists()) { + FileLog.rlog("Error: not found " + filename); + return new TreeMap>(); + } + List lines = read(filename); + FileLog.rlog("Got " + lines.size() + " lines"); + + lines = filter(Collections.unmodifiableList(lines)); + + findMinMax(lines); + + return asMap(lines); + } + + private static TreeMap> asMap(List lines) { + /** + * map of maps by RPM. inner map is by MAF + */ + TreeMap> rpm2mapByMaf = new TreeMap>(); + for (ReportLine cur : lines) { + int rpmKey = cur.getRpm().getValue(); + + // round to 100 + rpmKey -= (rpmKey % 100); + + TreeMap maf2line = Utils.getOrCreate(rpm2mapByMaf, rpmKey, new Factory>() { + public TreeMap create(Integer key) { + return new TreeMap(); + } + }); + maf2line.put(cur.getMaf().getValue(), cur); + } + return rpm2mapByMaf; + } + + private static void findMinMax(List lines) { + int minMaf = 100000; + int maxMaf = 0; + + int minRpm = 100000; + int maxRpm = 0; + for (ReportLine cur : lines) { + minMaf = Math.min(minMaf, cur.getMaf().getValue()); + maxMaf = Math.max(maxMaf, cur.getMaf().getValue()); + + minRpm = Math.min(minRpm, cur.getRpm().getValue()); + maxRpm = Math.max(maxRpm, cur.getRpm().getValue()); + } + FileLog.rlog("MAF range from " + minMaf + " to " + maxMaf); + FileLog.rlog("RPM range from " + minRpm + " to " + maxRpm); + } + + private static List filter(List lines) { + List result = new ArrayList(); + int maxValidDiff = 0; + int originalCount = lines.size(); + int removedCount = 0; + + result.add(lines.get(0)); + + for (int i = 1; i < lines.size(); i++) { + ReportLine prev = result.get(result.size() - 1); + ReportLine cur = lines.get(i); + + int rpmDiff = cur.getRpm().getValue() - prev.getRpm().getValue(); + int timeDiff = cur.getTime() - prev.getTime(); + + if (Math.abs(rpmDiff) > INVALID_RPM_DIFF) { + FileLog.rlog("Invalid diff: " + cur); + removedCount++; + continue; + } + result.add(cur); + // maximum valid diff + maxValidDiff = Math.max(maxValidDiff, Math.abs(rpmDiff)); +// System.out.println("current rpm: " + cur + ", rpm diff=" + rpmDiff + " td=" + timeDiff); +// System.out.println("value," + cur.getRpm().getValue() + "," + cur.getMaf().getValue() + "," + cur.getWave()); + } + double percent = 100.0 * removedCount / originalCount; + FileLog.rlog(removedCount + " out of " + originalCount + " record(s) removed. " + percent + "%"); + FileLog.rlog("Max valid diff: " + maxValidDiff); + return result; + } + + private static List read(String filename) { + List result = new LinkedList(); + try { + BufferedReader reader = new BufferedReader(new FileReader(filename)); + String line; + while ((line = reader.readLine()) != null) { + //process each line in some way + ReportLine rl = handleOneLine(line); + if (rl != null) + result.add(rl); + } + } catch (FileNotFoundException e) { + throw new IllegalStateException(e); + } catch (IOException e) { + throw new IllegalStateException(e); + } + return result; + } + + private static int prevMaf = -1; + private static int prevRpm = -1; + private static int prevWave = -1; + + private static ReportLine handleOneLine(String line) { +// System.out.println(line); + Matcher m = LINE_PATTERN.matcher(line); + if (m.matches()) { + int time = Integer.valueOf(m.group(1)); + MafValue maf = MafValue.valueOf(m.group(2)); + RpmValue rpm = RpmValue.valueOf(m.group(3)); + int wave = Integer.valueOf(m.group(4)); + + if (prevMaf == maf.getValue() && prevRpm == rpm.getValue() && prevWave == wave) { +// System.out.println("All the same..."); + return null; + } + + //System.out.println(time + " m=" + maf + " r=" + rpm + " w=" + wave); + prevMaf = maf.getValue(); + prevRpm = rpm.getValue(); + prevWave = wave; + return new ReportLine(time, maf, rpm, wave); + } + return null; + } +} diff --git a/java_console/models/src/com/irnems/core/EngineState.java b/java_console/models/src/com/irnems/core/EngineState.java new file mode 100644 index 0000000000..e76b653795 --- /dev/null +++ b/java_console/models/src/com/irnems/core/EngineState.java @@ -0,0 +1,348 @@ +package com.irnems.core; + +import com.irnems.FileLog; +import org.jetbrains.annotations.NotNull; + +import java.util.*; +import java.util.concurrent.CopyOnWriteArrayList; + +/** + * Date: 12/25/12 + * (c) Andrey Belomutskiy + * + * @see #registerStringValueAction + */ +public class EngineState { + public static final String RPM_KEY = "rpm"; + public static final String SEPARATOR = ","; + public static final int SNIFFED_ADC_COUNT = 16; + public static final ValueCallback NOTHING = new ValueCallback() { + @Override + public void onUpdate(String value) { + } + }; + public static final String PACKING_DELIMITER = ":"; + private final Object lock = new Object(); + + static class StringActionPair extends Pair> { + public final String prefix; + + StringActionPair(String key, ValueCallback second) { + super(key, second); + prefix = key.toLowerCase() + SEPARATOR; + } + + @Override + public String toString() { + return "Pair(" + first + "," + second + "}"; + } + } + + public List timeListeners = new CopyOnWriteArrayList(); + + private final ResponseBuffer buffer; + private final List actions = new ArrayList(); + private final Set keys = new TreeSet(String.CASE_INSENSITIVE_ORDER); + + public EngineState(@NotNull final EngineStateListener listener) { + buffer = new ResponseBuffer(new ResponseBuffer.ResponseListener() { + public void onResponse(String message) { + String response = unpackString(message); + if (response != null) { + String copy = response; + listener.beforeLine(response); + while (!response.isEmpty()) + response = handleResponse(response, listener); + listener.afterLine(copy); + } + } + } + ); + + + SensorStats.start(Sensor.COOLANT, Sensor.COOLANT_WIDTH); + SensorStats.start(Sensor.INTAKE_AIR, Sensor.INTAKE_AIR_WIDTH); + SensorStats.start(Sensor.VREF, Sensor.VREF_WIDTH); + + addDoubleSensor(RPM_KEY, Sensor.RPM); + addDoubleSensor("mat", Sensor.INTAKE_AIR); + addDoubleSensor("map", Sensor.MAP); + addDoubleSensor("map_r", Sensor.MAP_RAW); + addDoubleSensor("clt", Sensor.COOLANT); + addDoubleSensor("tp", Sensor.THROTTLE); + addDoubleSensor("dwell0", Sensor.DWELL0); + addDoubleSensor("dwell1", Sensor.DWELL1); + addDoubleSensor("tch", Sensor.T_CHARGE); + addDoubleSensor("afr", Sensor.AFR); + addDoubleSensor("d_fuel", Sensor.DEFAULT_FUEL); + addDoubleSensor("fuel", Sensor.FUEL); + addDoubleSensor("fuel_base", Sensor.FUEL_BASE); + addDoubleSensor("fuel_lag", Sensor.FUEL_LAG); + addDoubleSensor("fuel_clt", Sensor.FUEL_CLT); + addDoubleSensor("fuel_iat", Sensor.FUEL_IAT); + addDoubleSensor("table_spark", Sensor.TABLE_SPARK); + addDoubleSensor("advance0", Sensor.ADVANCE0); + addDoubleSensor("advance1", Sensor.ADVANCE1); + addDoubleSensor("vref", Sensor.VREF); + addDoubleSensor("vbatt", Sensor.VBATT); + addDoubleSensor("maf", Sensor.MAF); + addDoubleSensor("period0", Sensor.PERIOD0); + addDoubleSensor("period1", Sensor.PERIOD0); + addDoubleSensor("duty0", Sensor.DUTY0); + addDoubleSensor("duty1", Sensor.DUTY1); + addDoubleSensor("timing", Sensor.TIMING); + + addDoubleSensor("idl", Sensor.IDLE_SWITCH); + + addDoubleSensor("chart", Sensor.CHART_STATUS, true); + addDoubleSensor("chartsize", Sensor.CHARTSIZE, true); + addDoubleSensor("adcDebug", Sensor.ADC_STATUS, true); + addDoubleSensor("adcfast", Sensor.ADC_FAST); + addDoubleSensor("adcfastavg", Sensor.ADC_FAST_AVG); + registerStringValueAction("adcfast_co", NOTHING); + registerStringValueAction("adcfast_max", NOTHING); + registerStringValueAction("adcfast_min", NOTHING); + registerStringValueAction("key", NOTHING); + registerStringValueAction("value", NOTHING); +// addDoubleSensor("adcfast_co", Sensor.ADC_FAST_AVG); + + addDoubleSensor("injector0", Sensor.INJECTOR_0_STATUS, true); + addDoubleSensor("injector1", Sensor.INJECTOR_1_STATUS, true); + addDoubleSensor("injector2", Sensor.INJECTOR_2_STATUS, true); + addDoubleSensor("injector3", Sensor.INJECTOR_3_STATUS, true); + + registerStringValueAction("msg", new ValueCallback() { + @Override + public void onUpdate(String value) { + MessagesCentral.getInstance().postMessage(EngineState.class, value); + } + }); + + registerStringValueAction("advance", NOTHING); + registerStringValueAction("dwell", NOTHING); + + registerStringValueAction("offset0", NOTHING); + registerStringValueAction("offset1", NOTHING); + registerStringValueAction("crank_period", NOTHING); + registerStringValueAction("ckp_c", NOTHING); + registerStringValueAction("ev0", NOTHING); + registerStringValueAction("ev1", NOTHING); + registerStringValueAction("adc10", NOTHING); + + registerStringValueAction("isC", NOTHING); + registerStringValueAction("p_d", NOTHING); + registerStringValueAction("s_d", NOTHING); + registerStringValueAction("pEC", NOTHING); + registerStringValueAction("sEC", NOTHING); + registerStringValueAction("wEC", NOTHING); + registerStringValueAction("wWi", NOTHING); + registerStringValueAction("wWi2", NOTHING); + + registerStringValueAction("cid", NOTHING); + registerStringValueAction("i_d", NOTHING); + registerStringValueAction("i_p", NOTHING); + registerStringValueAction("a_time", NOTHING); + + + registerStringValueAction("time", new ValueCallback() { + public void onUpdate(String value) { + double time; + try { + time = Double.parseDouble(value); + } catch (NumberFormatException e) { + return; + } + listener.onTime(time); + for (EngineTimeListener l : timeListeners) + l.onTime(time); + } + }); + } + + private void addDoubleSensor(String key, final Sensor sensor) { + addDoubleSensor(key, sensor, false); + } + + private void addDoubleSensor(final String key, final Sensor sensor, final boolean verbose) { + registerStringValueAction(key, new ValueCallback() { + @Override + public void onUpdate(String stringValue) { + double value; + try { + value = Double.parseDouble(stringValue); + } catch (NumberFormatException e) { + // serial protocol is not safe + return; + } + SensorCentral.getInstance().setValue(value, sensor); + if (verbose) + MessagesCentral.getInstance().postMessage(EngineState.class, key + "=" + value); + } + }); + } + + /** + * @see #unpackString(String) + */ + public static String packString(String a) { + return "line" + PACKING_DELIMITER + a.length() + PACKING_DELIMITER + a; + } + + /** + * serial protocol is not error-prone, so our simple approach is to validate the length of incoming strings + * + * @see #packString(String) + */ + public static String unpackString(String message) { + String prefix = "line" + PACKING_DELIMITER; + if (!message.startsWith(prefix)) { + FileLog.rlog("EngineState: unexpected header: " + message); + return null; + } + message = message.substring(prefix.length()); + int delimiterIndex = message.indexOf(PACKING_DELIMITER); + if (delimiterIndex == -1) { + FileLog.rlog("Delimiter not found in: " + message); + return null; + } + String lengthToken = message.substring(0, delimiterIndex); + int expectedLen; + try { + expectedLen = Integer.parseInt(lengthToken); + } catch (NumberFormatException e) { + FileLog.rlog("invalid len: " + lengthToken); + return null; + } + + String response = message.substring(delimiterIndex + 1); + if (response.length() != expectedLen) { + FileLog.rlog("message len does not match header: " + message); + response = null; + } + return response; + } + + /** + * @param response input string + * @param listener obviously + * @return unused part of the response + */ + private String handleResponse(String response, EngineStateListener listener) { + String originalResponse = response; + synchronized (lock) { + for (StringActionPair pair : actions) + response = handleStringActionPair(response, pair, listener); + } + if (originalResponse.length() == response.length()) { + FileLog.rlog("EngineState.unknown: " + response); + // discarding invalid line + return ""; + } + return response; + } + + private String handleStringActionPair(String response, StringActionPair pair, EngineStateListener listener) { + if (startWithIgnoreCase(response, pair.prefix)) { + String key = pair.first; + int beginIndex = key.length() + 1; + int endIndex = response.indexOf(SEPARATOR, beginIndex); + if (endIndex == -1) + endIndex = response.length(); + + String strValue = response.substring(beginIndex, endIndex); + pair.second.onUpdate(strValue); + listener.onKeyValue(key, strValue); + + response = response.substring(endIndex); + if (!response.isEmpty()) + response = response.substring(1); // skipping the separator + return response; + } + return response; + } + + public static boolean startWithIgnoreCase(String line, String prefix) { + /** + * this implementation is faster than 'line.toLowerCase().startsWith(prefix)', that's important if processing + * huge log files + */ + int pLen = prefix.length(); + if (line.length() < pLen) + return false; + for (int i = 0; i < pLen; i++) { + char lineChar = Character.toLowerCase(line.charAt(i)); + char prefixChar = prefix.charAt(i); + if (Character.isLetter(prefixChar) && !Character.isLowerCase(prefixChar)) + throw new IllegalStateException("Not lower: " + prefix); + if (lineChar != prefixChar) + return false; + } + return true; + + } + +// private char hexChar(int value) { +// value = value & 0xF; +// if (value < 10) +// return (char) ('0' + value); +// return (char) ('A' - 10 + value); +// } +// +// private int parseChar(char c) { +// if (c <= '9') +// return c - '0'; +// return Character.toLowerCase(c) - 'a' + 10; +// } + + public void registerStringValueAction(String key, ValueCallback callback) { + synchronized (lock) { + if (keys.contains(key)) + throw new IllegalStateException("Already registered: " + key); + keys.add(key); + actions.add(new StringActionPair(key, callback)); + } + } + + public void removeAction(String key) { + synchronized (lock) { + keys.remove(key); + for (Iterator it = actions.iterator(); it.hasNext(); ) { + if (it.next().first.equals(key)) { + it.remove(); + break; + } + } + } + } + + public void append(String append) { + buffer.append(append); + } + + public interface ValueCallback { + void onUpdate(V value); + } + + public interface EngineStateListener extends EngineTimeListener { + void beforeLine(String fullLine); + + void onKeyValue(String key, String value); + + void afterLine(String fullLine); + } + + public static class EngineStateListenerImpl implements EngineStateListener { + public void beforeLine(String fullLine) { + } + + @Override + public void onKeyValue(String key, String value) { + } + + public void afterLine(String fullLine) { + } + + public void onTime(double time) { + } + } +} diff --git a/java_console/models/src/com/irnems/core/EngineTimeListener.java b/java_console/models/src/com/irnems/core/EngineTimeListener.java new file mode 100644 index 0000000000..d7cb4dd098 --- /dev/null +++ b/java_console/models/src/com/irnems/core/EngineTimeListener.java @@ -0,0 +1,9 @@ +package com.irnems.core; + +/** + * Date: 3/26/13 + * (c) Andrey Belomutskiy + */ +public interface EngineTimeListener { + void onTime(double time); +} diff --git a/java_console/models/src/com/irnems/core/MessagesCentral.java b/java_console/models/src/com/irnems/core/MessagesCentral.java new file mode 100644 index 0000000000..a29b3f0419 --- /dev/null +++ b/java_console/models/src/com/irnems/core/MessagesCentral.java @@ -0,0 +1,45 @@ +package com.irnems.core; + +import com.irnems.FileLog; + +import javax.swing.*; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +/** + * Date: 4/27/13 + * (c) Andrey Belomutskiy + */ +public class MessagesCentral { + private static final MessagesCentral INSTANCE = new MessagesCentral(); + private final List listeners = new CopyOnWriteArrayList(); + + private MessagesCentral() { + } + + public static MessagesCentral getInstance() { + return INSTANCE; + } + + public void addListener(MessageListener listener) { + listeners.add(listener); + } + + public void postMessage(final Class clazz, final String message) { + FileLog.rlog("postMessage " + clazz.getSimpleName() + ": " + message); + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + for (MessageListener listener : listeners) + listener.onMessage(clazz, message); + } + }); + } + + public interface MessageListener { + /** + * all listeners are invoked on AWT thread + */ + void onMessage(Class clazz, String message); + } +} diff --git a/java_console/models/src/com/irnems/core/Pair.java b/java_console/models/src/com/irnems/core/Pair.java new file mode 100644 index 0000000000..f21c18b0a0 --- /dev/null +++ b/java_console/models/src/com/irnems/core/Pair.java @@ -0,0 +1,23 @@ +package com.irnems.core; + +/** + * @author Andrey Belomutskiy + * 12/26/12 + */ +public class Pair { + public final K first; + public final V second; + + public Pair(K first, V second) { + this.first = first; + this.second = second; + } + + @Override + public String toString() { + return "Pair{" + + "first=" + first + + ", second=" + second + + '}'; + } +} diff --git a/java_console/models/src/com/irnems/core/ResponseBuffer.java b/java_console/models/src/com/irnems/core/ResponseBuffer.java new file mode 100644 index 0000000000..3be30fe8ad --- /dev/null +++ b/java_console/models/src/com/irnems/core/ResponseBuffer.java @@ -0,0 +1,61 @@ +package com.irnems.core; + +import com.irnems.core.test.ResponseBufferTest; +import org.jetbrains.annotations.NotNull; + +/** + * this class takes buffers input characters and separates them into full response lines + *

+ * Date: 12/25/12 + * (c) Andrey Belomutskiy + * + * @see ResponseBufferTest + */ +public class ResponseBuffer { + private final ResponseListener listener; + private final StringBuffer pending = new StringBuffer(); + + public ResponseBuffer(@NotNull ResponseListener listener) { + this.listener = listener; + } + + public void append(String append) { + pending.append(append); + + /** + * we might have anything between one unterminated line and numerous terminated lines + */ + while (hasCRLF(pending)) { + int cr = pending.indexOf("\r"); + int lf = pending.indexOf("\n"); + + if (cr != -1 && lf == -1) + lf = cr; + + if (cr == -1 && lf != -1) + cr = lf; + + if (cr != -1) { + int endOfLine = Math.min(cr, lf); + if (endOfLine > 0) { + String fullLine = pending.substring(0, endOfLine); + listener.onResponse(fullLine); + } + while (pending.length() > endOfLine && (pending.charAt(endOfLine) == '\r' || pending.charAt(endOfLine) == '\n')) + endOfLine++; + + pending.delete(0, endOfLine); + } + } + } + + private static boolean hasCRLF(StringBuffer str) { + int cr = str.indexOf("\r"); + int lf = str.indexOf("\n"); + return cr != -1 || lf != -1; + } + + public interface ResponseListener { + void onResponse(String response); + } +} diff --git a/java_console/models/src/com/irnems/core/Sensor.java b/java_console/models/src/com/irnems/core/Sensor.java new file mode 100644 index 0000000000..645f372d3e --- /dev/null +++ b/java_console/models/src/com/irnems/core/Sensor.java @@ -0,0 +1,104 @@ +package com.irnems.core; + +import eu.hansolo.steelseries.tools.BackgroundColor; + +/** + * @author Andrey Belomutskiy + * 2/11/13 + */ +public enum Sensor { + RPM("RPM"), + MAP("MAP"), + MAP_RAW("MAP_RAW"), + TIMING("Timing"), + THROTTLE("throttle", "%", 100), + COOLANT("coolant", "F", 300), + COOLANT_WIDTH("c w", "", 30), + + INTAKE_AIR("air temp", "F", 150), + INTAKE_AIR_WIDTH("air w", "", 30), + + TABLE_SPARK("table spark", "ms", -40, 40), + VREF("VRef", "Volts", 6), + VBATT("VBatt", "Volts", 18), + VREF_WIDTH("VRef w", "", 1), + MAF("MAF", "Volts", 4), + DWELL0("Dwell0", "ms", 0, 30, BackgroundColor.BEIGE), + DWELL1("Dwell1", "ms", 0, 30, BackgroundColor.BEIGE), + ADVANCE0("Advance0", "dg", -40, 40, BackgroundColor.BROWN), + ADVANCE1("Advance1", "dg", -40, 40, BackgroundColor.BROWN), + PERIOD0("Period", "dg", 0, 400), + DUTY0("Duty0", "%", 0, 100, BackgroundColor.RED), + DUTY1("Duty1", "%", 0, 100, BackgroundColor.RED), + FUEL("Fuel", "ms", 0, 30), + FUEL_BASE("Fuel Base", "ms", 0, 30), + FUEL_IAT("F IAT", "", 0, 10), + FUEL_CLT("F CLT", "", 0, 10), + FUEL_LAG("F Lag", "", 0, 30), + + IDLE_SWITCH("idle switch"), + + DEFAULT_FUEL("map fuel", "ms", 0, 40), + T_CHARGE("T Charge", "f", 0, 200), + AFR("A/F ratio", "", 0, 20), + + CHARTSIZE("CHARTSIZE"), + CHART_STATUS("CHART_STATUS"), + ADC_STATUS("ADC_STATUS"), + + INJECTOR_0_STATUS("INJECTOR_0_STATUS"), + INJECTOR_1_STATUS("INJECTOR_1_STATUS"), + INJECTOR_2_STATUS("INJECTOR_2_STATUS"), + INJECTOR_3_STATUS("INJECTOR_3_STATUS"), + + ADC_FAST("ADC_FAST", "b", 4000), + ADC_FAST_AVG("ADC_FAST_AVG", "b", 4000); + + private final String name; + private final String units; + private final double minValue; + private final double maxValue; + private final BackgroundColor color; + + + + Sensor(String name) { + this(name, "", 255); + } + + Sensor(String name, String units, double maxValue) { + this(name, units, 0, maxValue); + } + + Sensor(String name, String units, double minValue, double maxValue) { + this(name, units, minValue, maxValue, BackgroundColor.LIGHT_GRAY); + } + + Sensor(String name, String units, double minValue, double maxValue, BackgroundColor color) { + this.name = name; + this.units = units; + this.minValue = minValue; + this.maxValue = maxValue; + this.color = color; + } + + public String getName() { + return name; + } + + public String getUnits() { + return units; + } + + public double getMinValue() { + return minValue; + } + + public double getMaxValue() { + return maxValue; + } + + public BackgroundColor getColor() { + return color; + } +} diff --git a/java_console/models/src/com/irnems/core/SensorCentral.java b/java_console/models/src/com/irnems/core/SensorCentral.java new file mode 100644 index 0000000000..29eebcf21f --- /dev/null +++ b/java_console/models/src/com/irnems/core/SensorCentral.java @@ -0,0 +1,88 @@ +package com.irnems.core; + +import java.util.EnumMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CopyOnWriteArrayList; + +/** + * Date: 1/6/13 + * (c) Andrey Belomutskiy + */ +public class SensorCentral { + private static final SensorCentral INSTANCE = new SensorCentral(); + + private final Map values = new EnumMap(Sensor.class); + + private final Map> allListeners = new EnumMap>(Sensor.class); + + +// private final List listeners = new CopyOnWriteArrayList(); + + public static SensorCentral getInstance() { + return INSTANCE; + } + + private SensorCentral() { + } + +// public static String getTitle(int i) { +// Sensor sensor = Sensor.findByMazdaIndex(i); +// return "adc " + i + sensor.getName(); +// } + +// public String getSniffedAdcRepresentation(Sensor channel) { +// double value = getValue(channel); +// +// double volts = 5 * value / 255; +// return "" + value + " (" + volts + ")"; +// } + + public double getValue(Sensor sensor) { + Double value = values.get(sensor); + if (value == null) + throw new NullPointerException("No value for sensor: " + sensor); + return value; + } + + public void setValue(double value, Sensor sensor) { + values.put(sensor, value); + List listeners; + synchronized (allListeners) { + listeners = allListeners.get(sensor); + } + if (listeners == null) + return; + for (AdcListener listener : listeners) + listener.onAdcUpdate(this, value); + } + + public static String getInternalAdcRepresentation(double value) { + double volts = value * 3.3 / 4096; + return String.format("%.2f (%.2fv)", value, volts); + } + + public void addListener(Sensor sensor, AdcListener listener) { + List listeners; + synchronized (allListeners) { + listeners = allListeners.get(sensor); + if (listeners == null) + listeners = new CopyOnWriteArrayList(); + allListeners.put(sensor, listeners); + } + listeners.add(listener); + } + + public void removeListener(Sensor sensor, AdcListener listener) { + List listeners; + synchronized (allListeners) { + listeners = allListeners.get(sensor); + } + if (listeners != null) + listeners.remove(listener); + } + + public interface AdcListener { + void onAdcUpdate(SensorCentral model, double value); + } +} diff --git a/java_console/models/src/com/irnems/core/SensorStats.java b/java_console/models/src/com/irnems/core/SensorStats.java new file mode 100644 index 0000000000..533247f0ac --- /dev/null +++ b/java_console/models/src/com/irnems/core/SensorStats.java @@ -0,0 +1,38 @@ +package com.irnems.core; + +/** + * 7/26/13 + * (c) Andrey Belomutskiy + */ +public class SensorStats { + public static void start(final Sensor source, final Sensor destination) { + + SensorCentral.getInstance().addListener(source, new SensorCentral.AdcListener() { + + int counter; + double min = Double.MAX_VALUE; + double max = Double.MIN_VALUE; + + @Override + public void onAdcUpdate(SensorCentral model, double value) { + counter++; + + min = Math.min(value, min); + max = Math.max(value, max); + + if (counter == 10) { + counter = 0; + double width = max - min; + + SensorCentral.getInstance().setValue(width, destination); + + min = Double.MAX_VALUE; + max = Double.MIN_VALUE; + } + } + }); + + + } + +} diff --git a/java_console/models/src/com/irnems/core/test/EngineStateTest.java b/java_console/models/src/com/irnems/core/test/EngineStateTest.java new file mode 100644 index 0000000000..a2e41a564f --- /dev/null +++ b/java_console/models/src/com/irnems/core/test/EngineStateTest.java @@ -0,0 +1,52 @@ +package com.irnems.core.test; + +import com.irnems.core.SensorCentral; +import com.irnems.core.EngineState; +import org.junit.Test; + +import java.util.concurrent.atomic.AtomicInteger; + +import static com.irnems.core.EngineState.SEPARATOR; +import static junit.framework.Assert.*; + +/** + * @author Andrey Belomutskiy + * 12/26/12 + */ +public class EngineStateTest { + @Test + public void packUnpack() { + String a = "rpm,100,"; + String packed = EngineState.packString(a); + assertEquals("line:8:rpm,100,", packed); + assertEquals(a, EngineState.unpackString(packed)); + } + + @Test + public void startsWithIgnoreCase() { + assertTrue(EngineState.startWithIgnoreCase("HELLO", "he")); + assertFalse(EngineState.startWithIgnoreCase("HELLO", "hellllll")); + assertFalse(EngineState.startWithIgnoreCase("HELLO", "ha")); + } + + @Test + public void testRpm() { + final AtomicInteger rpmResult = new AtomicInteger(); + EngineState es = new EngineState(new EngineState.EngineStateListenerImpl() { + public void onKeyValue(String key, String value) { + if ("rpm".equals(key)) + rpmResult.set(Integer.parseInt(value)); + } + }); + es.append("line:7:"); + es.append(EngineState.RPM_KEY + SEPARATOR); + assertEquals(0, rpmResult.get()); + es.append("600\r"); + assertEquals(600, rpmResult.get()); + } + + @Test + public void testAdcRepresentation() { + assertEquals("1025.00 (0.83v)", SensorCentral.getInternalAdcRepresentation(1025)); + } +} diff --git a/java_console/models/src/com/irnems/core/test/ResponseBufferTest.java b/java_console/models/src/com/irnems/core/test/ResponseBufferTest.java new file mode 100644 index 0000000000..2febc24d9d --- /dev/null +++ b/java_console/models/src/com/irnems/core/test/ResponseBufferTest.java @@ -0,0 +1,36 @@ +package com.irnems.core.test; + +import com.irnems.core.ResponseBuffer; +import org.junit.Test; + +import java.util.concurrent.atomic.AtomicReference; + +import static org.junit.Assert.*; + +/** + * @author Andrey Belomutskiy + * 12/26/12 + * @see ResponseBuffer + */ +public class ResponseBufferTest { + @Test + public void testSingleLine() { + final AtomicReference currentReference = new AtomicReference(); + + ResponseBuffer rb = new ResponseBuffer(new ResponseBuffer.ResponseListener() { + public void onResponse(String response) { + currentReference.set(response); + } + }); + rb.append("\r"); + assertNull(currentReference.get()); + rb.append("\n"); + assertNull(currentReference.get()); + + rb.append("hi\n"); + assertEquals("hi", currentReference.get()); + + rb.append("\r\n\r\n\r\nhi2\n\n\n"); + assertEquals("hi2", currentReference.get()); + } +} diff --git a/java_console/models/src/com/irnems/core/test/WaveReportTest.java b/java_console/models/src/com/irnems/core/test/WaveReportTest.java new file mode 100644 index 0000000000..8e995d35d6 --- /dev/null +++ b/java_console/models/src/com/irnems/core/test/WaveReportTest.java @@ -0,0 +1,26 @@ +package com.irnems.core.test; + +import com.rusefi.waves.WaveReport; +import com.irnems.waves.ZoomProvider; +import org.junit.Test; + +import static junit.framework.Assert.assertEquals; + +/** + * Date: 6/23/13 + * (c) Andrey Belomutskiy + */ +public class WaveReportTest { + public static final String report = "up!14679!down!15991!up!16823!down!18134!up!18965!down!20278!up!21108!down!22420!up!23251!down!24563!up!25394!down!26706!up!27536!down!28850!up!29678!down!30991!up!31822!down!33134!up!33965!down!35277!up!36108!down!37420!up!38251!down!39563!up!40394!down!41706!up!42537!down!43849!"; + + @Test + public void testParse() { + WaveReport wr = new WaveReport(report); + assertEquals(14, wr.getList().size()); + + assertEquals(14679, wr.getMinTime()); + assertEquals(43849, wr.getMaxTime()); + + assertEquals(59, wr.timeToScreen(18134, 500, ZoomProvider.DEFAULT)); + } +} diff --git a/java_console/models/src/com/irnems/models/AverageData.java b/java_console/models/src/com/irnems/models/AverageData.java new file mode 100644 index 0000000000..332c387b64 --- /dev/null +++ b/java_console/models/src/com/irnems/models/AverageData.java @@ -0,0 +1,65 @@ +package com.irnems.models; + +import com.irnems.FileLog; + +import java.util.Set; + +/** + * 7/18/13 + * (c) Andrey Belomutskiy + */ +public class AverageData { + public static XYData average(XYData data, int divider) { + + double minX = data.getMinXValue(); + double xWidth = data.getMaxXValue() - minX; + + FileLog.rlog("From x" + minX + " w=" + xWidth); + + XYData result = new XYData(); + + for (int i = 0; i < divider; i++) { + double fromX_ = minX + (xWidth * i) / divider; + double toX_ = minX + (xWidth * (i + 1)) / divider; + + FileLog.rlog("from " + fromX_ + " to " + toX_); + +// double fromX = data.findXfromSet(fromX_); +// double toX = data.findXfromSet(toX_); +// System.out.println("from internal " + fromX + " to internal " + toX); + + average(data, result, fromX_, toX_, divider); + } + return result; + } + + private static void average(XYData data, XYData result, double fromX, double toX, int divider) { + double minY = data.getMinYValue(); + double yWidth = data.getMaxYValue() - minY; + + for (int i = 0; i < divider; i++) { + double fromY_ = minY + (yWidth * i) / divider; + double toY_ = minY + (yWidth * (i + 1)) / divider; + + Set xRange = data.getXSet().tailSet(fromX).headSet(toX); + + int counter = 0; + double acc = 0; + for (Double x : xRange) { + YAxisData yData = data.getYAxis(x); + + Set yRange = yData.getYs().tailSet(fromY_).headSet(toY_); + + for (double y : yRange) { + counter++; + acc += yData.getValue(y); + } + } + + if (counter == 0) + result.addPoint(new Point3D(fromX, fromY_, Float.NaN)); + else + result.addPoint(new Point3D(fromX, fromY_, (float) (acc / counter))); + } + } +} \ No newline at end of file diff --git a/java_console/models/src/com/irnems/models/Factory.java b/java_console/models/src/com/irnems/models/Factory.java new file mode 100644 index 0000000000..0284a5fab2 --- /dev/null +++ b/java_console/models/src/com/irnems/models/Factory.java @@ -0,0 +1,9 @@ +package com.irnems.models; + +/** + * @author Andrey Belomutskiy + * 1/29/13 + */ +public interface Factory { + V create(K key); +} diff --git a/java_console/models/src/com/irnems/models/MafValue.java b/java_console/models/src/com/irnems/models/MafValue.java new file mode 100644 index 0000000000..3d52b63a9d --- /dev/null +++ b/java_console/models/src/com/irnems/models/MafValue.java @@ -0,0 +1,29 @@ +package com.irnems.models; + +/** + * @author Andrey Belomutskiy + * 1/29/13 + */ +public class MafValue { + + private final int value; + + public MafValue(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static MafValue valueOf(String value) { + return new MafValue(Integer.parseInt(value)); + } + + @Override + public String toString() { + return "Maf{" + + value + + '}'; + } +} diff --git a/java_console/models/src/com/irnems/models/Point3D.java b/java_console/models/src/com/irnems/models/Point3D.java new file mode 100644 index 0000000000..64a040b188 --- /dev/null +++ b/java_console/models/src/com/irnems/models/Point3D.java @@ -0,0 +1,50 @@ +package com.irnems.models; + +/** + * Date: 3/24/13 + * (c) Andrey Belomutskiy + */ +public class Point3D { + private final double x; + private final double y; + private final float z; + + public Point3D(int rpm, double key, float value) { + this((double) rpm, key, value); + } + + public Point3D(double x, double y, float z) { + this.x = x; + this.y = y; + this.z = z; + } + + public double getX() { + return x; + } + + public double getY() { + return y; + } + + public float getZ() { + return z; + } + + @Override + public String toString() { + return "Point3D{" + + "x=" + x + + ", y=" + y + + ", z=" + z + + '}'; + } + + public static Point3D parseLine(String line) { + String[] tokens = line.split(","); + double x = Double.parseDouble(tokens[1]); + double y = Double.parseDouble(tokens[3]); + float z = Float.parseFloat(tokens[5]); + return new Point3D(x, y, z); + } +} diff --git a/java_console/models/src/com/irnems/models/Range.java b/java_console/models/src/com/irnems/models/Range.java new file mode 100644 index 0000000000..855feb952a --- /dev/null +++ b/java_console/models/src/com/irnems/models/Range.java @@ -0,0 +1,27 @@ +package com.irnems.models; + +/** + * Date: 3/24/13 + * (c) Andrey Belomutskiy + */ +public class Range { + private final float min; + private final float max; + + public Range(float min, float max) { + this.min = min; + this.max = max; + } + + public float getMin() { + return min; + } + + public float getMax() { + return max; + } + + public float getWidth() { + return max - min; + } +} diff --git a/java_console/models/src/com/irnems/models/RpmValue.java b/java_console/models/src/com/irnems/models/RpmValue.java new file mode 100644 index 0000000000..5124a33330 --- /dev/null +++ b/java_console/models/src/com/irnems/models/RpmValue.java @@ -0,0 +1,28 @@ +package com.irnems.models; + +/** + * @author Andrey Belomutskiy + * 1/29/13 + */ +public class RpmValue { + private final int value; + + public RpmValue(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static RpmValue valueOf(String value) { + return new RpmValue(Integer.valueOf(value)); + } + + @Override + public String toString() { + return "Rpm{" + + value + + '}'; + } +} diff --git a/java_console/models/src/com/irnems/models/Utils.java b/java_console/models/src/com/irnems/models/Utils.java new file mode 100644 index 0000000000..18012e5033 --- /dev/null +++ b/java_console/models/src/com/irnems/models/Utils.java @@ -0,0 +1,43 @@ +package com.irnems.models; + +import java.util.Map; +import java.util.SortedMap; +import java.util.TreeMap; + +/** + * @author Andrey Belomutskiy + * 1/29/13 + */ +public class Utils { + // i do not want to deal with overflow so no MAX_VALUE for me + private static final int LARGE_VALUE = 10000000; + + /** + * finds a key in the map which is closest to the specified element + */ + public static int findClosest(TreeMap map, int value) { + SortedMap head = map.headMap(value, true); + SortedMap tail = map.tailMap(value, true); + if (head.isEmpty() && tail.isEmpty()) + throw new IllegalStateException("Empty map? " + value); + + int fromHead = head.isEmpty() ? LARGE_VALUE : head.lastKey(); + int fromTail = tail.isEmpty() ? LARGE_VALUE : tail.firstKey(); + + int headDiff = Math.abs(value - fromHead); + int tailDiff = Math.abs(value - fromTail); + return headDiff < tailDiff ? fromHead : fromTail; + } + + /** + * gets an element from the map. if no such element - new one is created using the factory + */ + public static V getOrCreate(Map map, K key, Factory factory) { + V result = map.get(key); + if (result != null) + return result; + result = factory.create(key); + map.put(key, result); + return result; + } +} diff --git a/java_console/models/src/com/irnems/models/XYData.java b/java_console/models/src/com/irnems/models/XYData.java new file mode 100644 index 0000000000..0c79fe8f91 --- /dev/null +++ b/java_console/models/src/com/irnems/models/XYData.java @@ -0,0 +1,157 @@ +package com.irnems.models; + +import com.irnems.FileLog; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.FileWriter; +import java.io.IOException; +import java.io.Writer; +import java.util.Map; +import java.util.NavigableSet; +import java.util.TreeMap; + +/** + * Date: 3/24/13 + * (c) Andrey Belomutskiy + * + * @see TableGenerator + */ +public class XYData { + @NotNull + private final TreeMap yDatas = new TreeMap(); + private double maxXValue; + private double minXValue; + + private double maxYValue; + private double minYValue; + + String date = FileLog.getDate(); + + + public XYData() { + clear(); + } + + public float getValue(double x, double y) { + YAxisData yAxis = findYAxis(x); + if (yAxis == null) + return Float.NaN; // empty map? + return yAxis.findZ(y); + } + + public double getMaxXValue() { + return maxXValue; + } + + public double getMinXValue() { + return minXValue; + } + + public double getMaxYValue() { + return maxYValue; + } + + public double getMinYValue() { + return minYValue; + } + + public void addPoint(int rpm, double key, float value) { + addPoint(new Point3D(rpm, key, value)); + } + + public void addPoint(Point3D xyz) { + YAxisData yAxis = getYdata(xyz); + yAxis.addValue(xyz.getY(), xyz.getZ()); + } + + public void setPoint(Point3D xyz) { + YAxisData yAxis = getYdata(xyz); + yAxis.setValue(xyz.getY(), xyz.getZ()); + } + + private YAxisData getYdata(Point3D xyz) { + minYValue = Math.min(minYValue, xyz.getY()); + maxYValue = Math.max(maxYValue, xyz.getY()); + return getYAxis(xyz.getX()); + } + + @NotNull + public NavigableSet getXSet() { + return yDatas.navigableKeySet(); + } + + @NotNull + public YAxisData getYAxis(double x) { + YAxisData result = yDatas.get(x); + if (result == null) { + result = new YAxisData(x); + maxXValue = Math.max(maxXValue, x); + minXValue = Math.min(minXValue, x); + yDatas.put(x, result); + } + return result; + } + + @Nullable + public YAxisData findYAxis(double x) { + double xfromSet = findXfromSet(x); + if (Double.isNaN(xfromSet)) + return null; + return yDatas.get(xfromSet); + } + + public double findXfromSet(double x) { + Map.Entry floorEntry = yDatas.floorEntry(x); + if (floorEntry != null) + return floorEntry.getKey(); + Map.Entry ceilingEntry = yDatas.ceilingEntry(x); + if (ceilingEntry == null) + return Double.NaN; + return ceilingEntry.getKey(); + } + + public void clear() { + maxXValue = Double.MIN_VALUE; + minXValue = Double.MAX_VALUE; + maxYValue = Double.MIN_VALUE; + minYValue = Double.MAX_VALUE; + yDatas.clear(); + } + + @Override + public String toString() { + return "XYData{" + + "yDatas.size()=" + yDatas.size() + + '}'; + } + + public void saveToFile(String filename) { + try { + String name = date + filename; + FileLog.rlog("Writing data to " + name); + Writer w = new FileWriter(name); + for (YAxisData yAxisData : yDatas.values()) + yAxisData.write(w); + + w.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public void fill(Range rpmRange, Range keyRange, int count, float value) { + clear(); + for (int i = 0; i < count; i++) + for (int j = 0; j < count; j++) { + + float rpm = rpmRange.getMin() + (rpmRange.getWidth() * i / count); + float key = keyRange.getMin() + (keyRange.getWidth() * j / count); + + + addPoint(new Point3D(rpm, key, value)); + + + } + } +} diff --git a/java_console/models/src/com/irnems/models/XYDataReader.java b/java_console/models/src/com/irnems/models/XYDataReader.java new file mode 100644 index 0000000000..1204af6419 --- /dev/null +++ b/java_console/models/src/com/irnems/models/XYDataReader.java @@ -0,0 +1,37 @@ +package com.irnems.models; + +import com.irnems.FileLog; + +import java.io.*; + +/** + * 6/30/13 + * (c) Andrey Belomutskiy + */ +public class XYDataReader { + public static XYData readFile(String fileName) { + if (!new File(fileName).exists()) + throw new IllegalArgumentException("No file: " + fileName); + try { + return doReadFile(fileName); + } catch (FileNotFoundException e) { + throw new IllegalStateException(e); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + private static XYData doReadFile(String fileName) throws IOException { + BufferedReader reader = new BufferedReader(new FileReader(fileName)); + String line; + XYData data = new XYData(); + while ((line = reader.readLine()) != null) { + //process each line in some way + Point3D xyz = Point3D.parseLine(line); + data.addPoint(xyz); + } + FileLog.rlog("x range: " + data.getMinXValue() + " to " + data.getMaxXValue()); + FileLog.rlog("y range: " + data.getMinYValue() + " to " + data.getMaxYValue()); + return data; + } +} diff --git a/java_console/models/src/com/irnems/models/YAxisData.java b/java_console/models/src/com/irnems/models/YAxisData.java new file mode 100644 index 0000000000..5896a3055b --- /dev/null +++ b/java_console/models/src/com/irnems/models/YAxisData.java @@ -0,0 +1,109 @@ +package com.irnems.models; + +import com.irnems.FileLog; + +import java.io.IOException; +import java.io.Writer; +import java.util.Map; +import java.util.NavigableSet; +import java.util.TreeMap; + +/** + * Y>Z mapping for the same X + *

+ * Date: 3/24/13 + * (c) Andrey Belomutskiy + */ +public class YAxisData { + private final TreeMap values = new TreeMap(); + private double maxYValue = Double.MIN_VALUE; + private double minYValue = Double.MAX_VALUE; + private final double x; + + public YAxisData(double x) { + this.x = x; + } + + public double getX() { + return x; + } + + public NavigableSet getYs() { + return values.navigableKeySet(); + } + + public float findZ(double y) { + Map.Entry entry = values.floorEntry(y); + if (entry != null) + return entry.getValue().get(); + return values.ceilingEntry(y).getValue().get(); + } + + public void setValue(double y, float value) { + ValuesHolder holder = getHolder(y); + holder.set(value); + } + + public void addValue(double y, float value) { + ValuesHolder holder = getHolder(y); + + holder.add(value); + + float newAvg = holder.get(); + if (newAvg != value && !Float.isNaN(newAvg)) { + FileLog.rlog("new " + value + " avg " + newAvg + " for x=" + x + "/y=" + y); + } + } + + private ValuesHolder getHolder(double y) { + minYValue = Math.min(minYValue, y); + maxYValue = Math.max(maxYValue, y); + + ValuesHolder holder = values.get(y); + if (holder == null) + values.put(y, holder = new ValuesHolder()); + return holder; + } + + public double getValue(double y) { + return values.get(y).get(); + } + + @Override + public String toString() { + return "YAxisData{" + + "size=" + values.size() + + ", maxYValue=" + maxYValue + + ", minYValue=" + minYValue + + ", x=" + x + + '}'; + } + + public void write(Writer w) throws IOException { + for (Map.Entry e : values.entrySet()) + w.write("rpm," + x + ",key," + e.getKey() + ",value," + e.getValue().get() + "\r\n"); + } + + private static class ValuesHolder { + + private float total; + private int count; + + private ValuesHolder() { + } + + public float get() { + return total / count; + } + + public void add(float value) { + total += value; + count++; + } + + public void set(float value) { + total = value; + count = 1; + } + } +} diff --git a/java_console/models/src/com/irnems/models/test/UtilTest.java b/java_console/models/src/com/irnems/models/test/UtilTest.java new file mode 100644 index 0000000000..479413a901 --- /dev/null +++ b/java_console/models/src/com/irnems/models/test/UtilTest.java @@ -0,0 +1,30 @@ +package com.irnems.models.test; + +import com.irnems.models.Utils; +import org.junit.Test; + +import java.util.TreeMap; + +import static com.irnems.models.Utils.findClosest; +import static junit.framework.Assert.assertEquals; + +/** + * @author Andrey Belomutskiy + * 1/29/13 + */ +public class UtilTest { + @Test + public void testClosest() { + TreeMap map = new TreeMap(); + map.put(0, "0"); + map.put(1, "1"); + map.put(10, "10"); + map.put(11, "11"); + + assertEquals(0, findClosest(map, -1)); + assertEquals(0, findClosest(map, 0)); + assertEquals(1, findClosest(map, 1)); + assertEquals(1, findClosest(map, 3)); + assertEquals(10, findClosest(map, 10)); } + +} diff --git a/java_console/models/src/com/irnems/models/test/XYDataSandbox.java b/java_console/models/src/com/irnems/models/test/XYDataSandbox.java new file mode 100644 index 0000000000..114b017f9a --- /dev/null +++ b/java_console/models/src/com/irnems/models/test/XYDataSandbox.java @@ -0,0 +1,20 @@ +package com.irnems.models.test; + +import com.irnems.models.XYData; + +/** + * 7/24/13 + * (c) Andrey Belomutskiy + */ +public class XYDataSandbox { + public static void main(String[] args) { + XYData d = new XYData(); + + d.addPoint(600, 3, 11); + d.addPoint(600, 3.1, 11); + + d.addPoint(1600, 3.1, 11); + + d.saveToFile("_mult.dat"); + } +} diff --git a/java_console/models/src/com/irnems/test/HistogramsTest.java b/java_console/models/src/com/irnems/test/HistogramsTest.java new file mode 100644 index 0000000000..6c2c959e18 --- /dev/null +++ b/java_console/models/src/com/irnems/test/HistogramsTest.java @@ -0,0 +1,51 @@ +package com.irnems.test; + +import com.irnems.Histograms; +import org.junit.Test; + +import java.util.Random; + +import static junit.framework.Assert.assertEquals; + +/** + * Created by pc on 12/18/13. + */ +public class HistogramsTest { + + @Test + public void testHistogram() { + Histograms h = new Histograms(); + + + String key = "hello"; + Histograms.ValueType type = Histograms.ValueType.INVOCATION; + h.addValue(type, key, 30); + + + Histograms.LocalStats ls = h.local_stats.get(); + + Histograms.StatisticsGroup sg = ls.stats.get(type); + + Histograms.Statistics data = sg.data.get(key); + + assertEquals(40, data.histogram.length); + + + assertEquals(80, h.getIndex(239)); + assertEquals(223, h.getIndex(239239)); + assertEquals(364, h.getIndex(239239239)); + + + Random r = new Random(); + for (int i = 0; i < 2342334; i++) + h.addValue(type, key, r.nextInt()); + + + assertEquals(640, data.histogram.length); + + + System.out.println(h.dumpStats()); + + } + +} diff --git a/java_console/models/src/com/irnems/waves/TimeAxisTranslator.java b/java_console/models/src/com/irnems/waves/TimeAxisTranslator.java new file mode 100644 index 0000000000..5501c003d2 --- /dev/null +++ b/java_console/models/src/com/irnems/waves/TimeAxisTranslator.java @@ -0,0 +1,15 @@ +package com.irnems.waves; + +/** + * Date: 6/25/13 + * (c) Andrey Belomutskiy + */ +public interface TimeAxisTranslator { + int timeToScreen(int time, int width, ZoomProvider zoomProvider); + + double screenToTime(int screen, int width, ZoomProvider zoomProvider); + + int getMaxTime(); + + int getMinTime(); +} diff --git a/java_console/models/src/com/irnems/waves/ZoomProvider.java b/java_console/models/src/com/irnems/waves/ZoomProvider.java new file mode 100644 index 0000000000..17ecf5b31b --- /dev/null +++ b/java_console/models/src/com/irnems/waves/ZoomProvider.java @@ -0,0 +1,16 @@ +package com.irnems.waves; + +/** + * 7/7/13 + * (c) Andrey Belomutskiy + */ +public interface ZoomProvider { + ZoomProvider DEFAULT = new ZoomProvider() { + @Override + public double getZoomValue() { + return 1; + } + }; + + double getZoomValue(); +} diff --git a/java_console/models/src/com/rusefi/waves/RevolutionLog.java b/java_console/models/src/com/rusefi/waves/RevolutionLog.java new file mode 100644 index 0000000000..142fc61022 --- /dev/null +++ b/java_console/models/src/com/rusefi/waves/RevolutionLog.java @@ -0,0 +1,71 @@ +package com.rusefi.waves; + +import java.util.*; + +/** + * 1/11/14. + * (c) Andrey Belomutskiy + */ +public class RevolutionLog { + public static final String TOP_DEAD_CENTER_MESSAGE = "r"; + private final TreeMap time2rpm; + + public RevolutionLog(TreeMap time2rpm) { + this.time2rpm = time2rpm; + } + + public static RevolutionLog parseRevolutions(CharSequence revolutions) { + TreeMap time2rpm = new TreeMap(); + if (revolutions == null) + return new RevolutionLog(time2rpm); + + String[] r = revolutions.toString().split("!"); + for (int i = 0; i < r.length - 1; i += 2) { + int rpm = Integer.parseInt(r[i]); + int time = Integer.parseInt(r[i + 1]); + time2rpm.put(time, rpm); + } + return new RevolutionLog(time2rpm); + } + + public String getCrankAngleByTimeString(double time) { + double result = getCrankAngleByTime(time); + return Double.isNaN(result) ? "n/a" : String.format("%.2f", result); + } + + public double getCrankAngleByTime(double time) { + return doGetAngle(time, true); + } + + private double doGetAngle(double time, boolean tryNextRevolution) { + Map.Entry entry = getTimeAndRpm(time); + if (entry == null) { + if (tryNextRevolution && time2rpm.size() >= 2) { + // we are here if the value is below the first revolution point + List> element = new ArrayList>(time2rpm.entrySet()); + Map.Entry first = element.get(0); + Map.Entry second = element.get(1); + + int oneRevolutionDuration = second.getKey() - first.getKey(); + return doGetAngle(time + oneRevolutionDuration, false); + } else { + return Double.NaN; + } + } else { + double diff = time - entry.getKey(); + + Integer rpm = entry.getValue(); + double timeForRevolution = 60000 * WaveReport.SYS_TICKS_PER_MS / rpm; + + return 360.0 * diff / timeForRevolution; + } + } + + public Map.Entry getTimeAndRpm(double time) { + return time2rpm.floorEntry((int) time); + } + + public Set keySet() { + return time2rpm.keySet(); + } +} diff --git a/java_console/models/src/com/rusefi/waves/WaveChart.java b/java_console/models/src/com/rusefi/waves/WaveChart.java new file mode 100644 index 0000000000..0dea8b1415 --- /dev/null +++ b/java_console/models/src/com/rusefi/waves/WaveChart.java @@ -0,0 +1,34 @@ +package com.rusefi.waves; + +import java.util.Map; + +/** + * A collection of {@link WaveReport} + *

+ *

+ * + * @see WaveChartParser + *

+ * Date: 3/17/14 + * (c) Andrey Belomutskiy + */ +public class WaveChart { + public static final String INJECTOR_1 = "Injector 1"; + public static final String INJECTOR_2 = "Injector 2"; + public static final String INJECTOR_3 = "Injector 3"; + public static final String INJECTOR_4 = "Injector 4"; + public static final String SPARK_1 = "Spark 1"; + public static final String SPARK_2 = "Spark 2"; + public static final String SPARK_3 = "Spark 3"; + public static final String SPARK_4 = "Spark 4"; + + public final Map map; + + public WaveChart(Map map) { + this.map = map; + } + + public StringBuilder get(String key) { + return map.get(key); + } +} diff --git a/java_console/models/src/com/rusefi/waves/WaveChartParser.java b/java_console/models/src/com/rusefi/waves/WaveChartParser.java new file mode 100644 index 0000000000..3011eaf41a --- /dev/null +++ b/java_console/models/src/com/rusefi/waves/WaveChartParser.java @@ -0,0 +1,43 @@ +package com.rusefi.waves; + +import com.irnems.FileLog; + +import java.util.HashMap; +import java.util.Map; + +/** + * 1/26/14 + * Andrey Belomutskiy (c) 2012-2014 + */ +public class WaveChartParser { + private static final String DELI = "!"; + + /** + * This method unpacks a mixed-key message into a Map of messages by key + */ + public static WaveChart unpackToMap(String value) { + FileLog.rlog(": " + value); + + String[] array = value.split(DELI); + + Map map = new HashMap(); + + int index = 0; + while (index + 2 < array.length) { + String name = array[index]; + + StringBuilder sb = map.get(name); + if (sb == null) { + sb = new StringBuilder(); + map.put(name, sb); + } + + String signal = array[index + 1]; + String val = array[index + 2]; + + sb.append(signal).append(DELI).append(val).append(DELI); + index += 3; + } + return new WaveChart(map); + } +} diff --git a/java_console/models/src/com/rusefi/waves/WaveReport.java b/java_console/models/src/com/rusefi/waves/WaveReport.java new file mode 100644 index 0000000000..ecab46ce4a --- /dev/null +++ b/java_console/models/src/com/rusefi/waves/WaveReport.java @@ -0,0 +1,166 @@ +package com.rusefi.waves; + +import com.irnems.waves.TimeAxisTranslator; +import com.irnems.waves.ZoomProvider; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Date: 6/23/13 + * (c) Andrey Belomutskiy + */ +public class WaveReport implements TimeAxisTranslator { + public static final String WAVE_CHART = "wave_chart"; + public static final WaveReport MOCK = new WaveReport(Collections.singletonList(new UpDown(0, -1, 1, -1))); + /** + * number of ChibiOS systicks per ms + */ + public static final double SYS_TICKS_PER_MS = 100; + public static final int mult = (int) (100 * SYS_TICKS_PER_MS); // 100ms + + List list; + private int maxTime; + /** + * min timestamp on this chart, in systicks + */ + private int minTime; + + public WaveReport(String report) { + this(parse(report)); + } + + public WaveReport(List list) { + if (list.isEmpty()) + throw new IllegalStateException("empty"); + this.list = list; + minTime = list.get(0).upTime; + maxTime = list.get(list.size() - 1).downTime; + } + + public static boolean isCloseEnough(double v1, double v2) { + if (v2 == 0) + return v1 == 0; + double ratio = v1 / v2; + return Math.abs(1 - ratio) < 0.05; + } + + public List getList() { + return list; + } + + @Override + public int getMaxTime() { + return maxTime; + } + + @Override + public int getMinTime() { + return minTime; + } + + public static List parse(String report) { + String[] array = report.split("!"); +// if (array.length % 4 != 0) +// throw new IllegalArgumentException("Unexpected length " + array.length); + + List times = new ArrayList(); + + int index = 0; + if (array[0].equals("down")) + index += 2; + + while (index + 3 < array.length) { + if (!array[index].equals("up")) { + index += 2; + continue; + } + if (!array[index + 2].equals("down")) { + index += 2; + continue; + } + + String upString[] = array[index + 1].split("_"); + String downString[] = array[index + 3].split("_"); + try { + int upTime = Integer.parseInt(upString[0]); + int downTime = Integer.parseInt(downString[0]); + + int upIndex = upString.length > 1 ? Integer.parseInt(upString[1]) : -1; + int downIndex = downString.length > 1 ? Integer.parseInt(downString[1]) : -1; + + + times.add(new UpDown(upTime, upIndex, downTime, downIndex)); + } catch (NumberFormatException e) { + System.err.println("Invalid? [" + upString + "][" + downString + "]"); + } + + index += 4; + } + return times; + } + + @Override + public int timeToScreen(int time, int width, ZoomProvider zoomProvider) { + double translated = (time - minTime) * zoomProvider.getZoomValue() / getDuration(); + return (int) (width * translated); + } + + @Override + public double screenToTime(int screen, int width, ZoomProvider zoomProvider) { + // / SYS_TICKS_PER_MS / 1000 + double time = 1.0 * screen * getDuration() / width / zoomProvider.getZoomValue() + minTime; + int x2 = timeToScreen((int) time, width, zoomProvider); +// FileLog.rlog("screenToTime " + (screen - x2)); + return (int) time; + } + + /** + * @return Length of this chart ini systicks + */ + public int getDuration() { + return maxTime - minTime; + } + + @Override + public String toString() { + return "WaveReport{" + + "size=" + list.size() + + ", maxTime=" + maxTime + + ", minTime=" + minTime + + '}'; + } + + public static class UpDown { + // times in sys ticks + public final int upTime; + public final int upIndex; + public final int downTime; + public final int downIndex; + + UpDown(int upTime, int upIndex, int downTime, int downIndex) { + this.upTime = upTime; + this.upIndex = upIndex; + this.downTime = downTime; + this.downIndex = downIndex; + } + + public int getDuration() { + return downTime - upTime; + } + + @Override + public String toString() { + return "UpDown{" + + "upTime=" + upTime + + ", downTime=" + downTime + + '}'; + } + + public double getDutyCycle(RevolutionLog rl) { + double angleDuration = (rl.getCrankAngleByTime(downTime) + 360 - rl.getCrankAngleByTime(upTime)) % 260; + return angleDuration / 360; + } + } +} diff --git a/java_console/models/src/com/rusefi/waves/test/RevolutionLogTest.java b/java_console/models/src/com/rusefi/waves/test/RevolutionLogTest.java new file mode 100644 index 0000000000..9cbfa14cc3 --- /dev/null +++ b/java_console/models/src/com/rusefi/waves/test/RevolutionLogTest.java @@ -0,0 +1,21 @@ +package com.rusefi.waves.test; + +import com.rusefi.waves.RevolutionLog; +import junit.framework.Assert; +import org.junit.Test; + +/** + * Andrey Belomutskiy (c) 2012-2014 + * 3/19/14 + */ +public class RevolutionLogTest { + @Test + public void backTime() { + RevolutionLog r = RevolutionLog.parseRevolutions("2000!148958!2000!154958!2000!160958!2000!166958!"); + + Assert.assertEquals(594.84, r.getCrankAngleByTime(147915)); + + // too back into the past + Assert.assertEquals(Double.NaN, r.getCrankAngleByTime(140915)); + } +} diff --git a/java_console/models/src/com/rusefi/waves/test/WaveChartParserTest.java b/java_console/models/src/com/rusefi/waves/test/WaveChartParserTest.java new file mode 100644 index 0000000000..f5d1c0d6c4 --- /dev/null +++ b/java_console/models/src/com/rusefi/waves/test/WaveChartParserTest.java @@ -0,0 +1,64 @@ +package com.rusefi.waves.test; + +import com.rusefi.waves.RevolutionLog; +import com.rusefi.waves.WaveChart; +import com.rusefi.waves.WaveChartParser; +import com.rusefi.waves.WaveReport; +import org.junit.Test; + +import java.util.List; + +import static com.rusefi.waves.WaveReport.isCloseEnough; +import static junit.framework.Assert.*; + +/** + * 1/26/14 + * Andrey Belomutskiy (c) 2012-2014 + */ +public class WaveChartParserTest { + @Test + public void testMultiParser() { + WaveChart result = WaveChartParser.unpackToMap(""); + assertTrue(result.map.isEmpty()); + + result = WaveChartParser.unpackToMap("wave_chart,input1 A8!up!185080!r!0!2670996!crank!up!2670996_2!crank2!up!2674512!Injector 4!up!2674512!Spark 1!up!2674512!Injector 4!down!2674665!Spark 1!down!2674912!crank2!down!2680055!crank2!up!2687963!Injector 2!up!2687963!Spark 1!up!2687963!Injector 2!down!2688116!Spark 1!down!2688363!crank2!down!2693093!crank!down!2697428!crank2!up!2700454!Injector 1!up!2700454!Spark 1!up!2700454!Injector 1!down!2700607!Spark 1!down!2700854!crank2!down!2705329!crank2!up!2712449!Injector 3!up!2712449!Spark 1!up!2712449!Injector 3!down!2712681!Spark 1!down!2712849!crank2!down!2717385!r!0!2721629!crank!up!2721629!crank2!up!2724641!Injector 1!up!2724641!Injector 2!up!2724641!Injector 3!up!2724641!Injector 4!up!2724641!Spark 1!up!2724641!Injector 4!down!2726241!Injector 3!down!2726241!Injector 2!down!2726241!Injector 1!down!2726241!crank2!down!2729677!Spark 1!down!2730008!crank2!up!2736851!Injector 1!up!2736851!Injector 2!up!2736851!Injector 3!up!2736851!Injector 4!up!2736851!Spark 1!up!2736851!Injector 4!down!2738451!Injector 3!down!2738451!Injector 2!down!2738451!Injector 1!down!2738451!crank2!down!2741922!Spark 1!down!2742218!crank!down!2746104!crank2!up!2749010!Injector 1!up!2749010!Injector 2!up!2749010!Injector 3!up!2749010!Injector 4!up!2749010!Spark 1!up!2749010!Injector 4!down!2750601!Injector 3!down!2750601!Injector 2!down!2750601!Injector 1!down!2750601!crank2!down!2753919!Spark 1!down!2754377!crank2!up!2760922!Injector 1!up!2760922!Injector 2!up!2760922!Injector 3!up!2760922!Injector 4!up!2760922!Spark 1!up!2760922!Injector 4!down!2762522!Injector 3!down!2762522!Injector 2!down!2762522!Injector 1!down!2762522!crank2!down!2765882!Spark 1!down!2766289!r!236!2769990!crank!up!2769990!crank2!up!2773003!Injector 1!up!2773003!Injector 2!up!2773003!Injector 3!up!2773003!Injector 4!up!2773003!Spark 1!up!2773003!Injector 4!down!2774603!Injector 3!down!2774603!Injector 2!down!2774603!Injector 1!down!2774603!Spark 1!down!2778110!crank2!down!2778143!crank2!up!2785215!Injector 1!up!2785215!Injector 2!up!2785215!Injector 3!up!2785215!Injector 4!up!2785215!Spark 1!up!2785215!Injector 4!down!2786815!,"); + assertEquals(9, result.map.size()); + + String crankReport = result.get("crank").toString(); + + List list = WaveReport.parse(crankReport); + assertEquals(2, list.size()); + + WaveReport.UpDown upDown = list.get(0); + assertEquals(2670996, upDown.upTime); + assertEquals(2, upDown.upIndex); + + assertEquals(2697428, upDown.downTime); + assertEquals(-1, upDown.downIndex); + } + + @Test + public void testDutyCycle() { + WaveChart result = WaveChartParser.unpackToMap("r!1199!64224414!crank2!up!64225149_3!Injector 2!up!64225149!Spark 1!up!64225249!Injector 2!down!64225303!Spark 1!down!64225649!crank2!down!64226105_4!crank!down!64226980_5!crank2!up!64227730_6!Injector 1!up!64227730!Spark 1!up!64227830!Injector 1!down!64227884!Spark 1!down!64228230!crank2!down!64228678_7!crank2!up!64230212_8!Injector 3!up!64230212!Spark 1!up!64230312!Injector 3!down!64230366!Spark 1!down!64230712!crank2!down!64231156_9!crank!up!64231982_0!crank2!up!64232672_1!Injector 4!up!64232672!Spark 1!up!64232772!Injector 4!down!64232826!Spark 1!down!64233172!crank2!down!64233626_2!r!1200!64234412!crank2!up!64235150_3!Injector 2!up!64235150!Spark 1!up!64235250!Injector 2!down!64235304!Spark 1!down!64235650!crank2!down!64236106_4!crank!down!64236981_5!crank2!up!64237730_6!Injector 1!up!64237730!Spark 1!up!64237830!Injector 1!down!64237884!Spark 1!down!64238230!crank2!down!64238677_7!crank2!up!64240213_8!Injector 3!up!64240213!Spark 1!up!64240313!Injector 3!down!64240367!Spark 1!down!64240713!crank2!down!64241158_9!crank!up!64241982_0!crank2!up!64242674_1!Injector 4!up!64242674!Spark 1!up!64242774!Injector 4!down!64242828!Spark 1!down!64243174!crank2!down!64243625_2!r!1200!64244412!crank2!up!64245149_3!Injector 2!up!64245149!Spark 1!up!64245249!Injector 2!down!64245303!Spark 1!down!64245649!crank2!down!64246106_4!crank!down!64246980_5!crank2!up!64247728_6!Injector 1!up!64247728!Spark 1!up!64247828!Injector 1!down!64247882!Spark 1!down!64248228!crank2!down!64248679_7!crank2!up!64250212_8!Injector 3!up!64250212!Spark 1!up!64250312!Injector 3!down!64250366!Spark 1!down!64250712!crank2!down!64251158_9!crank!up!64251982_0!crank2!up!64252674_1!Injector 4!up!64252674!Spark 1!up!64252774!Injector 4!down!64252828!Spark 1!down!64253174!crank2!down!64253625_2!r!1200!64254412!crank2!up!64255150_3!Injector 2!up!64255150!Spark 1!up!64255250!Injector 2!down!64255304!Spark 1!down!64255650!crank2!down!64256106_4!crank!down!64256982_5!crank2!up!64257728_6!Injector 1!up!64257728!Spark 1!up!64257828!Injector 1!down!64257882!Spark 1!down!64258228!crank2!down!64258678_7!crank2!up!64260214_8!Injector 3!up!64260214!Spark 1!up!64260314!Injector 3!down!64260368!Spark 1!down!64260714!,"); + assertFalse(result.map.isEmpty()); + + StringBuilder revolutions = result.get(RevolutionLog.TOP_DEAD_CENTER_MESSAGE); + + RevolutionLog rl = RevolutionLog.parseRevolutions(revolutions); + + StringBuilder inj1 = result.get(WaveChart.INJECTOR_1); + WaveReport wr = new WaveReport(inj1.toString()); + + for (WaveReport.UpDown ud : wr.getList()) { + assertTrue(isCloseEnough(238.75, rl.getCrankAngleByTime(ud.upTime))); + + assertTrue(isCloseEnough(0.308, ud.getDutyCycle(rl))); + } + } + + @Test + public void testUpOnly() { + List list = WaveReport.parse("up!15500!up!25500!up!35500!up!45500!up!55500!up!65500!up!75500!up!85500!"); + assertEquals(0, list.size()); + } +} diff --git a/java_console/tools/src/com/irnems/AdcFilter.java b/java_console/tools/src/com/irnems/AdcFilter.java new file mode 100644 index 0000000000..12bb08b9ea --- /dev/null +++ b/java_console/tools/src/com/irnems/AdcFilter.java @@ -0,0 +1,60 @@ +package com.irnems; + +import com.irnems.core.EngineState; +import com.irnems.file.FileUtils; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +/** + * @author Andrey Belomutskiy + * 8/5/13 + */ +public class AdcFilter { + public static void main(String[] args) { + String filename = "a.csv"; + + final Map map = new TreeMap(); + + final List values = new ArrayList(); + + EngineState.EngineStateListener listener = new EngineState.EngineStateListenerImpl() { + @Override + public void onKeyValue(String key, String value) { + map.put(key, value); + +// boolean isOurValue = key.equals("clt"); + boolean isOurValue = key.equals("mat"); + if (isOurValue) { + double newValue = Double.valueOf(value); + if (values.isEmpty() || values.get(values.size() - 1) != newValue) + values.add(newValue); + } + } + + @Override + public void afterLine(String fullLine) { + map.clear(); + } + }; + + EngineState engineState = new EngineState(listener); + + FileUtils.readFile2(filename, engineState); + + System.out.println("Got " + values.size() + " values"); + + FileUtils.saveList("v2.csv", values); + + + final List medianValues = MedialFilter.filter(values, 5); + FileUtils.saveList("v_med.csv", medianValues); + + + final List filtered = SimpleFilter.filter(medianValues, 100); + FileUtils.saveList("v_fil.csv", filtered); + + } +} diff --git a/java_console/tools/src/com/irnems/MedialFilter.java b/java_console/tools/src/com/irnems/MedialFilter.java new file mode 100644 index 0000000000..fd259d4c50 --- /dev/null +++ b/java_console/tools/src/com/irnems/MedialFilter.java @@ -0,0 +1,48 @@ +package com.irnems; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * @author Andrey Belomutskiy + * 8/5/13 + */ +public class MedialFilter { + + + private List values; + private int size; + + + + + public MedialFilter(List values, int size) { + this.values = values; + this.size = size; + } + + public static List filter(List values, int size) { + return new MedialFilter(values, size).filter(); + } + + private List filter() { + + List result = new ArrayList(); + + for(int i=0;i copy = new ArrayList(values.subList(fromIndex, i+1)); + + Collections.sort(copy); + + result.add(copy.get(copy.size() / 2)); + + } + + + return result; + } +} diff --git a/java_console/tools/src/com/irnems/SimpleFilter.java b/java_console/tools/src/com/irnems/SimpleFilter.java new file mode 100644 index 0000000000..ab0921c24c --- /dev/null +++ b/java_console/tools/src/com/irnems/SimpleFilter.java @@ -0,0 +1,33 @@ +package com.irnems; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Andrey Belomutskiy + * 8/5/13 + */ +public class SimpleFilter { + public static List filter(List medianValues, int K) { + List result = new ArrayList(); + + Double firstValue = medianValues.get(0); + + double Dacc = firstValue * K; + double Dout = firstValue; + + +// for (int i = 0; i < 3 * K; i++) { +// Dacc = Dacc + firstValue - Dout; +// Dout = Dacc / K; +// } + + for (double Din : medianValues) { + Dacc = Dacc + Din - Dout; + Dout = Dacc / K; + result.add(Dout); + } + + return result; + } +} diff --git a/java_console/tools/src/com/irnems/Test.java b/java_console/tools/src/com/irnems/Test.java new file mode 100644 index 0000000000..2b9b98ed66 --- /dev/null +++ b/java_console/tools/src/com/irnems/Test.java @@ -0,0 +1,19 @@ +package com.irnems; + +import jssc.SerialPortList; + +/** + * Date: 12/25/12 + * (c) Andrey Belomutskiy + */ +public class Test { + + public static void main(String[] args) { + String[] portNames = SerialPortList.getPortNames(); + System.out.println("Total " + portNames.length + " serial port(s)"); + for (String portName : portNames) { + System.out.println(portName); + } + System.exit(0); + } +} diff --git a/java_console/tools/src/com/irnems/file/FrequencyDivider.java b/java_console/tools/src/com/irnems/file/FrequencyDivider.java new file mode 100644 index 0000000000..6102d0aab9 --- /dev/null +++ b/java_console/tools/src/com/irnems/file/FrequencyDivider.java @@ -0,0 +1,43 @@ +package com.irnems.file; + +import com.irnems.core.EngineState; + +import java.io.FileOutputStream; +import java.io.IOException; + +/** + * Date: 3/8/13 + * (c) Andrey Belomutskiy + */ +public class FrequencyDivider { + public static void main(String[] args) throws IOException { + String filename = args.length > 1 ? args[0] : "a.csv"; + System.out.println("Reading from " + filename); + + final int divider = 10; + + String output = divider + "_" + filename; + System.out.println("Writing to " + output); + final FileOutputStream fos = new FileOutputStream(output); + + + EngineState.EngineStateListener listener = new EngineState.EngineStateListenerImpl() { + int lineCounter = 0; + + @Override + public void beforeLine(String fullLine) { + if (lineCounter < 5 || lineCounter % divider == 0) { + String w = fullLine + "\r\n"; + try { + fos.write(w.getBytes()); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + lineCounter++; + } + }; + FileUtils.readFile(filename, listener); + fos.close(); + } +} diff --git a/java_console/tools/tools.iml b/java_console/tools/tools.iml new file mode 100644 index 0000000000..7d2eb6cc61 --- /dev/null +++ b/java_console/tools/tools.iml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/java_console/ui/src/com/irnems/ChartRepository.java b/java_console/ui/src/com/irnems/ChartRepository.java new file mode 100644 index 0000000000..1adb0507e1 --- /dev/null +++ b/java_console/ui/src/com/irnems/ChartRepository.java @@ -0,0 +1,93 @@ +package com.irnems; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * 7/27/13 + * (c) Andrey Belomutskiy + */ +public class ChartRepository { + private static final ChartRepository instance = new ChartRepository(); + + private final List charts = new ArrayList(); + + private ChartRepository() { + } + + public static ChartRepository getInstance() { + return instance; + } + + public String getChart(int index) { + return charts.get(index); + } + + public int getSize() { + return charts.size(); + } + + public void addChart(String value) { + charts.add(value); + } + + public void clear() { + charts.clear(); + } + + public interface CRListener { + public void onDigitalChart(String chart); + } + + public JComponent createControls(final CRListener listener) { + + final AtomicInteger index = new AtomicInteger(); + + JPanel result = new JPanel(new FlowLayout(FlowLayout.RIGHT, 5, 0)); + result.setBorder(BorderFactory.createLineBorder(Color.red)); + + final JLabel info = new JLabel(); + + setInfoText(index, info); + + JButton prev = new JButton("<"); + prev.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if (index.intValue() > 0) { + index.decrementAndGet(); + listener.onDigitalChart(charts.get(index.get())); + setInfoText(index, info); + } + } + }); + + + JButton next = new JButton(">"); + next.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if (index.intValue() < getSize() - 1) { + index.incrementAndGet(); + listener.onDigitalChart(charts.get(index.get())); + setInfoText(index, info); + } + } + }); + + result.add(prev); + result.add(info); + result.add(next); + + return result; + } + + private void setInfoText(AtomicInteger index, JLabel info) { + info.setText(index.get() + "/" + getSize()); + } +} diff --git a/java_console/ui/src/com/irnems/EcuStimulator.java b/java_console/ui/src/com/irnems/EcuStimulator.java new file mode 100644 index 0000000000..0c5c51cc63 --- /dev/null +++ b/java_console/ui/src/com/irnems/EcuStimulator.java @@ -0,0 +1,226 @@ +package com.irnems; + +import com.irnems.core.MessagesCentral; +import com.irnems.core.Sensor; +import com.irnems.core.EngineTimeListener; +import com.irnems.core.SensorCentral; +import com.irnems.file.TableGenerator; +import com.irnems.models.Point3D; +import com.irnems.models.Range; +import com.irnems.models.XYData; +import com.irnems.ui.ChartHelper; +import com.irnems.ui.RpmModel; +import com.irnems.ui.widgets.PotCommand; +import com.irnems.ui.widgets.RpmCommand; +import com.rusefi.io.LinkManager; +import net.ericaro.surfaceplotter.DefaultSurfaceModel; + +import javax.swing.*; +import java.io.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CountDownLatch; + +/** + * Date: 3/24/13 + * (c) Andrey Belomutskiy + */ +public class EcuStimulator { + // private static final String TITLE = "Spark Advance"; + private static final String TITLE = "Fuel Table"; + + private static final String D = ","; + private static final long SLEEP_TIME = 300; + + private static final float MAF_MIN = 1.2f; + private static final float MAF_MAX = 4.5f; + private static final double MAF_INCREMENT = 0.1; + + private static final int RPM_MIN = 400; + private static final int RPM_MAX = 6000; + private static final int RPM_INCREMENT = 250; + private static final Sensor DWELL_SENSOR = Sensor.DWELL1; + + private static final String TABLE_FILE_NAME = "table" + RPM_INCREMENT + "_" + MAF_INCREMENT + ".csv"; + + private static final int MEASURES = 7; + // private static final String C_FILE_NAME = "advance_map.c"; +// private static final String C_PREFIX = "ad_"; + private static final String C_FILE_NAME = "fuel_map.c"; + private static final String C_PREFIX = "fuel_"; + + public static Range RPM_RANGE = new Range(0, RPM_MAX); // x-coord + static Range mafRange = new Range(MAF_MIN, MAF_MAX); // y-coord + private static XYData data = new XYData(); + private static DefaultSurfaceModel model = ChartHelper.createDefaultSurfaceModel(data, RPM_RANGE, mafRange); + public static JPanel panel = ChartHelper.create3DControl(data, model, TITLE); + + public static void buildTable() { + data.clear(); + +// setPotVoltage(2.2, Sensor.MAF); +// if (1 == 1) +// return; + + final BufferedWriter bw; + try { + bw = new BufferedWriter(new FileWriter(TABLE_FILE_NAME)); + } catch (IOException e) { + throw new IllegalStateException(e); + } + + ResultListener listener = new ResultListener() { + @Override + public void onResult(int rpm, double maf, float advance, double dwell) { + data.addPoint(new Point3D(rpm, maf, (float) dwell)); + model.plot().execute(); + + String msg = putValue("rpm", rpm) + + putValue("maf", maf) + + putValue("advance", advance) + + putValue("dwell", dwell); + MessagesCentral.getInstance().postMessage(EcuStimulator.class, msg); + + try { + bw.write(msg + "\r\n"); + bw.flush(); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + }; + + buildTable(bw, listener, DWELL_SENSOR); + + try { + bw.close(); + } catch (FileNotFoundException e) { + throw new IllegalStateException(e); + } catch (IOException e) { + throw new IllegalStateException(e); + } + + TableGenerator.writeAsC(data, C_PREFIX, C_FILE_NAME); + } + + private static void buildTable(BufferedWriter bw, ResultListener listener, Sensor dwellSensor) { + for (int rpm = RPM_MIN; rpm <= RPM_MAX; rpm += RPM_INCREMENT) + runSimulation(rpm, listener, dwellSensor); + } + + private static void runSimulation(int rpm, ResultListener resultListener, final Sensor dwellSensor) { + for (double maf = MAF_MIN; maf <= MAF_MAX; maf += MAF_INCREMENT) { + setPotVoltage(maf, Sensor.MAF); + setRpm(rpm); + sleepRuntime(SLEEP_TIME); + + final List dwells = new ArrayList(MEASURES); + final List advances = new ArrayList(MEASURES); + + final CountDownLatch latch = new CountDownLatch(MEASURES); + + EngineTimeListener listener = new EngineTimeListener() { + @Override + public void onTime(double time) { + if (latch.getCount() == 0) + return; + double dwell0 = getValue(dwellSensor); + double advance = 0;//getValue(Sensor.ADVANCE0); + dwells.add(dwell0); + advances.add(advance); + latch.countDown(); + } + }; + LinkManager.engineState.timeListeners.add(listener); + try { + latch.await(); + } catch (InterruptedException e) { + throw new IllegalStateException(e); + } + LinkManager.engineState.timeListeners.remove(listener); + + // sorting measurements, taking middle value + Collections.sort(dwells); + Collections.sort(advances); + + double dwellDiff = Math.abs(dwells.get(0) - dwells.get(MEASURES - 1)); + if (dwellDiff > 1) + System.out.println("dwells " + dwells); + + + double dwell = dwells.get(MEASURES / 2); + double advance = advances.get(MEASURES / 2); + + if (dwell > 40) { + throw new UnsupportedOperationException(); + } + + log("Stimulator result: " + rpm + "@" + maf + ": " + dwell); + +// double dwell = Launcher.getAdcModel().getValue(Sensor.DWELL0); +// double advance = Launcher.getAdcModel().getValue(Sensor.ADVANCE); + + resultListener.onResult(rpm, maf, (float) advance, dwell); + } + } + + private static void sleepRuntime(long sleepTime) { + try { + Thread.sleep(sleepTime); + } catch (InterruptedException e) { + throw new IllegalStateException(e); + } + } + + private static double getValue(Sensor sensor) { + return SensorCentral.getInstance().getValue(sensor); + } + + private static void setRpm(int rpm) { + int actual; + int attempt = 0; + do { + RpmCommand.requestRpmChange(rpm); + sleepRuntime(50); + actual = RpmModel.getInstance().getValue(); + } while (attempt++ < 10 && Math.abs(rpm - actual) >= 100); + log("Result: " + actual + " while setting " + rpm); + } + + public static void setPotVoltage(double voltage, Sensor sensor) { + log("Current voltage: " + getValue(sensor) + ", setting " + voltage); + int attempt = 0; + int resistance = PotCommand.getPotResistance(voltage); + if (resistance <= 0) { + log("Invalid resistance " + resistance + ". Invalid voltage " + voltage + "?"); + return; + } + + double actual; + do { + PotCommand.requestPotChange(1, resistance); + sleepRuntime(50); + actual = getValue(sensor); + log("Got: " + actual + " on attempt=" + attempt + " while setting " + voltage + " for " + sensor); + } while (attempt++ < 10 && Math.abs(voltage - actual) > 0.2); + log("Result: " + actual + " while setting " + voltage); + } + + private static void log(String message) { + MessagesCentral.getInstance().postMessage(EcuStimulator.class, message); + FileLog.MAIN.logLine(message); + } + + private static String putValue(String msg, double value) { + return msg + D + value + D; + } + + private static String putValue(String msg, int value) { + return msg + D + value + D; + } + + interface ResultListener { + void onResult(int rpm, double maf, float advance, double dwell); + } +} diff --git a/java_console/ui/src/com/irnems/Launcher.java b/java_console/ui/src/com/irnems/Launcher.java new file mode 100644 index 0000000000..0c5aa6105b --- /dev/null +++ b/java_console/ui/src/com/irnems/Launcher.java @@ -0,0 +1,105 @@ +package com.irnems; + +import com.irnems.core.EngineState; +import com.irnems.core.MessagesCentral; +import com.irnems.ui.*; +import com.rusefi.AnalogChartPanel; +import com.rusefi.PortLookupFrame; +import com.rusefi.io.LinkManager; +import jssc.SerialPortList; + +import javax.swing.*; + +/** + * this is the main entry point of rusEfi ECU console + *

+ *

+ * Date: 12/25/12 + * (c) Andrey Belomutskiy + * + * @see WavePanel + */ +public class Launcher extends FrameHelper { + private static final Object CONSOLE_VERSION = "20140304"; + + public Launcher(String port) { + FileLog.MAIN.start(); + + LinkManager.start(port); + + JTabbedPane tabbedPane = new JTabbedPane(); + + RpmPanel rpmPanel = new RpmPanel(); + tabbedPane.addTab("Main", rpmPanel.createRpmPanel()); + tabbedPane.addTab("Gauges", new GaugePanel()); + tabbedPane.addTab("Digital Sniffer", WavePanel.getInstance()); + tabbedPane.addTab("Analog Sniffer", new AnalogChartPanel()); + +// tabbedPane.addTab("ADC", new AdcPanel(new BooleanInputsModel()).createAdcPanel()); +// tabbedPane.add("Emulation Map", EcuStimulator.panel); +// tabbedPane.addTab("live map adjustment", new Live3DReport().getControl()); + tabbedPane.add("MessagesCentral", new MsgPanel(true)); + +// tabbedPane.add("Log Viewer", new LogViewer()); + + tabbedPane.setSelectedIndex(2); +// tabbedPane.setSelectedIndex(5); + + for (String p : SerialPortList.getPortNames()) + MessagesCentral.getInstance().postMessage(Launcher.class, "Available port: " + p); + + showFrame(tabbedPane); + } + + @Override + protected void onWindowOpened() { + super.onWindowOpened(); + setTitle("N/A"); + + LinkManager.open(); + + LinkManager.engineState.registerStringValueAction("rusEfiVersion", new EngineState.ValueCallback() { + @Override + public void onUpdate(String value) { + setTitle(value); + } + }); + } + + private void setTitle(String value) { + frame.setTitle("Console " + CONSOLE_VERSION + "; firmware=" + value); + } + + @Override + protected void onWindowClosed() { + super.onWindowClosed(); + /** + * looks like reconnectTimer in {@link RpmPanel} keeps AWT alive. Simplest solution would be to 'exit' + */ + System.exit(0); + } + + public static void main(final String[] args) throws Exception { + Thread.setDefaultUncaughtExceptionHandler(new DefaultExceptionHandler()); + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + awtCode(args); + } + }); + } + + private static void awtCode(String[] args) { + try { + + boolean isPortDefined = args.length > 0; + if (isPortDefined) { + new Launcher(args[0]); + } else { + PortLookupFrame.chooseSerialPort(); + } + + } catch (Throwable e) { + throw new IllegalStateException(e); + } + } +} diff --git a/java_console/ui/src/com/irnems/LogViewer.java b/java_console/ui/src/com/irnems/LogViewer.java new file mode 100644 index 0000000000..97aebe6171 --- /dev/null +++ b/java_console/ui/src/com/irnems/LogViewer.java @@ -0,0 +1,150 @@ +package com.irnems; + +import com.irnems.core.EngineState; +import com.irnems.file.FileUtils; +import com.irnems.ui.WavePanel; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.File; +import java.io.FileFilter; + +/** + * 7/27/13 + * (c) Andrey Belomutskiy + */ +public class LogViewer extends JPanel { + public static final FileFilter FILE_FILTER = new FileFilter() { + @Override + public boolean accept(File pathname) { + return pathname.getName().startsWith("rfi_report"); + } + }; + private final JLabel folderLabel = new JLabel(); + private final DefaultListModel fileListModel = new DefaultListModel(); + private final JList fileList = new JList(fileListModel); + private String currentFolder; + + +// int currentChartIndex = 0; + + public LogViewer() { + super(new BorderLayout()); + + setBackground(Color.green); + + // todo: this is not perfect + openFolder("out"); + + JPanel folderPanel = new JPanel(new FlowLayout()); + + JButton folderButton = new JButton("Open folder"); + folderButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + JFileChooser chooser = new JFileChooser(currentFolder); + chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + int result = chooser.showSaveDialog(LogViewer.this); + if (result == JFileChooser.APPROVE_OPTION) + openFolder(chooser.getSelectedFile().getAbsolutePath()); + } + }); + + folderPanel.add(folderButton); + folderPanel.add(folderLabel); + + folderPanel.setBackground(Color.red); + + add(folderPanel, BorderLayout.NORTH); + + + JPanel descPanel = new JPanel(); + descPanel.add(new JLabel("Total digital charts: ")); + descPanel.add(new JLabel("" + ChartRepository.getInstance().getSize())); + + + JPanel boxPanel = new JPanel(); + boxPanel.setLayout(new BoxLayout(boxPanel, BoxLayout.X_AXIS)); + boxPanel.setBorder(BorderFactory.createLineBorder(Color.cyan)); + + boxPanel.add(fileList); + boxPanel.add(descPanel); + + + add(boxPanel); + } + + private void openFolder(String folderName) { + folderLabel.setText(folderName); + currentFolder = folderName; + + File folder = new File(folderName); + if (!folder.isDirectory()) + throw new IllegalStateException("Not directory: " + folder); + + File[] files = folder.listFiles(FILE_FILTER); + fileList.removeAll(); + for (File file : files) + fileListModel.addElement(getFileDesc(file)); + + if (files.length > 0) + openFile(files[0]); + + } + + private String getFileDesc(File file) { + return file.getName() + " " + file.getUsableSpace(); + } + + +// JPanel upperPanel = new JPanel(new FlowLayout()); +// +// +// JButton next = new JButton("next"); +// next.addActionListener(new ActionListener() { +// @Override +// public void actionPerformed(ActionEvent e) { +// currentChartIndex++; +// refreshChart(); +// +// } +// }); +// +// upperPanel.add(next); +// +// +// refreshChart(); + + + // private void refreshChart() { +// String chart = ChartRepository.getInstance().getChart(currentChartIndex); +// } +// +// public static void main(String[] args) { +// String filename = "a.csv"; +// +// + private void openFile(File file) { + String filename = file.getAbsolutePath(); + EngineState.EngineStateListener listener = new EngineState.EngineStateListenerImpl() { + }; + + ChartRepository.getInstance().clear(); + EngineState engineState = new EngineState(listener); + engineState.registerStringValueAction("wave_chart", new EngineState.ValueCallback() { + @Override + public void onUpdate(String value) { + FileLog.rlog("chart " + value); + + ChartRepository.getInstance().addChart(value); + + } + }); + FileUtils.readFile2(filename, engineState); + + if (ChartRepository.getInstance().getSize() > 0) + WavePanel.getInstance().displayChart(ChartRepository.getInstance().getChart(0)); + } +} \ No newline at end of file diff --git a/java_console/ui/src/com/irnems/ShowMap.java b/java_console/ui/src/com/irnems/ShowMap.java new file mode 100644 index 0000000000..ff91f636b7 --- /dev/null +++ b/java_console/ui/src/com/irnems/ShowMap.java @@ -0,0 +1,26 @@ +package com.irnems; + +import com.irnems.file.BaseMap; +import com.irnems.models.XYData; +import com.irnems.ui.FrameHelper; +import com.irnems.ui.ChartHelper; + +import javax.swing.*; + +/** + * 7/18/13 + * (c) Andrey Belomutskiy + */ +public class ShowMap extends FrameHelper { + public static void main(String[] args) { +// XYData data = BaseMap.loadData("a.csv", "maf", "af"); +// XYData data2 = BaseMap.loadData("a.csv", "maf", "table_fuel"); + + XYData data = BaseMap.loadData("200.csv", "maf", "dwell"); + XYData data2 = null; + + JPanel jsp = ChartHelper.create3DControl(data, ChartHelper.createDefaultSurfaceModel(data, data2), "MAF<>Fuel Map"); + + new ShowMap().showFrame(jsp); + } +} diff --git a/java_console/ui/src/com/irnems/ui/AdcPanel.java b/java_console/ui/src/com/irnems/ui/AdcPanel.java new file mode 100644 index 0000000000..dae2dc002d --- /dev/null +++ b/java_console/ui/src/com/irnems/ui/AdcPanel.java @@ -0,0 +1,88 @@ +package com.irnems.ui; + +import com.irnems.core.SensorCentral; +import com.irnems.core.Pair; +import net.miginfocom.swing.MigLayout; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.HashMap; +import java.util.Map; + +import static com.irnems.core.EngineState.SNIFFED_ADC_COUNT; + +/** + * Date: 1/7/13 + * (c) Andrey Belomutskiy + */ +public class AdcPanel { + protected final Map sniffedAdcLabels = new HashMap(); + public final Map inputLabels = new HashMap(); + public final Map internalAdcLabels = new HashMap(); + + public AdcPanel(BooleanInputsModel inputs) { + } + + public JComponent createAdcPanel() { + JComponent container = new JPanel(new MigLayout()); + JButton saveAdc = new JButton("save ADC"); + saveAdc.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { +// model.saveToFile(); + } + }); + container.add(saveAdc, "grow, wrap"); + + for (int channel1 = 0; channel1 < SNIFFED_ADC_COUNT / 2; channel1++) { +// { +// Pair pair1 = createAdcLabel(channel1); +// // 2nd column of channels +// int channel2 = channel1 + SNIFFED_ADC_COUNT / 2; +// Pair pair2 = createAdcLabel(channel2); +// sniffedAdcLabels.put(channel1, pair1.second); +// sniffedAdcLabels.put(channel2, pair2.second); +// +// container.add(pair1.first); +// container.add(pair2.first); +// } + + { + Pair inputPair = createLabelWithCaption(BooleanInputsModel.getTitle(channel1)); + inputLabels.put(channel1, inputPair.second); + container.add(inputPair.first); + } + { + Pair pair = createLabelWithCaption("adc " + channel1); + internalAdcLabels.put(channel1, pair.second); + container.add(pair.first, "grow, wrap"); + } + + } + return container; + } + +// private Pair createAdcLabel(final int channel) { +// final Pair result = createLabelWithCaption(SensorCentral.getTitle(channel)); +// model.addListener(new SensorCentral.AdcListener() { +// public void onAdcUpdate(SensorCentral model, Sensor sensor, double value) { +// if (sensor.getMazdaIndex() != channel) +// return; +// JLabel label = result.second; +// String representation = model.getSniffedAdcRepresentation(sensor); +// label.setText(representation); +// } +// }); +// return result; +// } + + private Pair createLabelWithCaption(String title) { + JPanel panel = new JPanel(new FlowLayout()); + panel.add(new JLabel(title)); + JLabel value = new JLabel(""); + panel.add(value); + return new Pair(panel, value); + } + +} diff --git a/java_console/ui/src/com/irnems/ui/BooleanInputsModel.java b/java_console/ui/src/com/irnems/ui/BooleanInputsModel.java new file mode 100644 index 0000000000..409aee1332 --- /dev/null +++ b/java_console/ui/src/com/irnems/ui/BooleanInputsModel.java @@ -0,0 +1,41 @@ +package com.irnems.ui; + +import java.util.HashMap; +import java.util.Map; +import java.util.TreeMap; + +/** + * Date: 1/14/13 + * (c) Andrey Belomutskiy + */ +public class BooleanInputsModel { + private static final Map inputTitle = new HashMap(); + private static final Map trueValueText = new HashMap(); + private static final Map falseValueText = new HashMap(); + private final Map values = new TreeMap(); + + static { + inputTitle.put(0, "cranking"); + int idle = 1; + inputTitle.put(idle, "idle switch"); + trueValueText.put(idle, "idle"); + falseValueText.put(idle, "not idle"); + } + + static String getTitle(int channel) { + String title = inputTitle.containsKey(channel) ? (": " + inputTitle.get(channel)) : ""; + return "adc " + channel + title; + } + + public void setValue(int channel, boolean value) { + values.put(channel, value); + } + + public static String getValueLabelText(int channel, boolean value) { + if (value && trueValueText.containsKey(channel)) + return trueValueText.get(channel); + if (!value && falseValueText.containsKey(channel)) + return falseValueText.get(channel); + return Boolean.toString(value); + } +} diff --git a/java_console/ui/src/com/irnems/ui/ChartHelper.java b/java_console/ui/src/com/irnems/ui/ChartHelper.java new file mode 100644 index 0000000000..d986eea674 --- /dev/null +++ b/java_console/ui/src/com/irnems/ui/ChartHelper.java @@ -0,0 +1,134 @@ +package com.irnems.ui; + +import com.irnems.FileLog; +import com.irnems.models.Range; +import com.irnems.models.XYData; +import com.irnems.ui.widgets.JTextFieldWithWidth; +import net.ericaro.surfaceplotter.DefaultSurfaceModel; +import net.ericaro.surfaceplotter.JSurfacePanel; +import net.ericaro.surfaceplotter.Mapper; +import net.ericaro.surfaceplotter.surface.SurfaceModel; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +/** + * Date: 1/22/13 + * (c) Andrey Belomutskiy + */ +public class ChartHelper { + private ChartHelper() { + } + + public static JPanel create3DControl(final XYData data, SurfaceModel surfaceModel, String title) { + JPanel result = new JPanel(new BorderLayout()); + + final JSurfacePanel jsp = new JSurfacePanel(surfaceModel); + jsp.setTitleText(title); + jsp.setConfigurationVisible(true); + jsp.getSurface().setXLabel("RPM"); + jsp.getSurface().setYLabel("MAF voltage"); + result.add(BorderLayout.CENTER, jsp); + + JButton saveImageButton = new JButton("save image"); + saveImageButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + UiUtils.saveImage(FileLog.getDate() + "_3d.png", jsp); + } + }); + + JPanel upperPanel = new JPanel(new FlowLayout()); + upperPanel.add(saveImageButton); + result.add(upperPanel, BorderLayout.NORTH); + + JPanel bottomPanel = new JPanel(new FlowLayout()); + bottomPanel.add(new JLabel("RPM: ")); + + final JTextField xField = new JTextFieldWithWidth("1200", 150); + final JTextField yField = new JTextFieldWithWidth("50", 150); + bottomPanel.add(xField); + bottomPanel.add(yField); + + final JLabel currentValue = new JLabel(); + bottomPanel.add(currentValue); + + result.add(bottomPanel, BorderLayout.SOUTH); + + ActionListener updateValue = new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + int rpm = Integer.parseInt(xField.getText()); + + double y = Double.parseDouble(yField.getText()); + + currentValue.setText("" + data.getValue(rpm, y)); + } + }; + xField.addActionListener(updateValue); + yField.addActionListener(updateValue); + + updateValue.actionPerformed(null); + + return result; + } + + public static DefaultSurfaceModel createDefaultSurfaceModel(final XYData data) { + return createDefaultSurfaceModel(data, null); + } + + public static DefaultSurfaceModel createDefaultSurfaceModel(final XYData data, final XYData data2) { + Range xRange = new Range((float) data.getMinXValue(), (float) data.getMaxXValue()); + Range yRange = new Range((float) data.getMinYValue(), (float) data.getMaxYValue()); + + return createDefaultSurfaceModel(data, xRange, yRange, data2); + } + + public static DefaultSurfaceModel createDefaultSurfaceModel(final XYData data, Range xRange, Range yRange) { + return createDefaultSurfaceModel(data, xRange, yRange, null); + } + + public static DefaultSurfaceModel createDefaultSurfaceModel(final XYData data, Range xRange, Range yRange, final XYData data2) { + final DefaultSurfaceModel sm = new DefaultSurfaceModel(); + + sm.setPlotFunction2(false); + + sm.setCalcDivisions(50); + sm.setDispDivisions(50); + sm.setContourLines(10); + + sm.setXMin(xRange.getMin()); + sm.setXMax(xRange.getMax()); + sm.setYMin(yRange.getMin()); + sm.setYMax(yRange.getMax()); + + sm.setBoxed(true); + sm.setDisplayXY(true); + sm.setExpectDelay(false); + sm.setAutoScaleZ(true); + sm.setDisplayZ(true); + sm.setMesh(true); + sm.setPlotType(SurfaceModel.PlotType.SURFACE); + if (data2 == null) + sm.setFirstFunctionOnly(true); + else + sm.setBothFunction(true); + + sm.setPlotColor(data2 != null ? SurfaceModel.PlotColor.FOG : SurfaceModel.PlotColor.SPECTRUM); + sm.setMapper(new Mapper() { + public float f1(float x, float y) { + return data.getValue(x, y); + } + + public float f2(float x, float y) { + if (data2 == null) + return 0; + return data2.getValue(x, y); + } + }); + sm.plot().execute(); + return sm; + } +} diff --git a/java_console/ui/src/com/irnems/ui/ChartStatusPanel.java b/java_console/ui/src/com/irnems/ui/ChartStatusPanel.java new file mode 100644 index 0000000000..b732094f36 --- /dev/null +++ b/java_console/ui/src/com/irnems/ui/ChartStatusPanel.java @@ -0,0 +1,77 @@ +package com.irnems.ui; + +import com.irnems.waves.TimeAxisTranslator; +import com.rusefi.waves.WaveReport; +import com.irnems.waves.ZoomProvider; +import com.rusefi.waves.RevolutionLog; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.MouseEvent; +import java.awt.event.MouseMotionAdapter; +import java.util.Map; + +/** + * Status bar at the bottom of Digital Sniffer - {@link WavePanel} + *

+ *

+ * Date: 12/26/13 + * Andrey Belomutskiy (c) 2012-2013 + */ +public class ChartStatusPanel { + public final JPanel infoPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)); + private final JLabel xLabel = new JLabel(); + private final JLabel timeLabel = new JLabel(); + private final JLabel angleLabel = new JLabel(); + private final JLabel rpmLabel = new JLabel(); + private TimeAxisTranslator translator = WaveReport.MOCK; + + private RevolutionLog time2rpm = RevolutionLog.parseRevolutions(null); + + final MouseMotionAdapter motionAdapter = new MouseMotionAdapter() { + @Override + public void mouseMoved(MouseEvent event) { + int x = event.getX(); + xLabel.setText("" + x); + + /** + * Time which corresponds to the mouse cursor screen location + */ + double time = translator.screenToTime(x, infoPanel.getWidth(), zoomProvider); + timeLabel.setText("" + String.format("%.5f sec", time)); + + String text = time2rpm == null ? "n/a" : time2rpm.getCrankAngleByTimeString(time); + angleLabel.setText(text); + + Map.Entry e = time2rpm.getTimeAndRpm(time); + if (e == null) + rpmLabel.setText("n/a"); + else + rpmLabel.setText("" + e.getValue()); + } + }; + + private ZoomProvider zoomProvider; + + public ChartStatusPanel(ZoomProvider zoomProvider) { + this.zoomProvider = zoomProvider; + infoPanel.add(new JLabel("X: ")); + infoPanel.add(xLabel); + infoPanel.add(new JLabel(" time: ")); + infoPanel.add(timeLabel); + + infoPanel.add(new JLabel(" angle: ")); + infoPanel.add(angleLabel); + + infoPanel.add(new JLabel(" RPM: ")); + infoPanel.add(rpmLabel); + } + + public void setWaveReport(TimeAxisTranslator translator) { + this.translator = translator; + } + + public void setRevolutions(StringBuilder revolutions) { + time2rpm = RevolutionLog.parseRevolutions(revolutions); + } +} diff --git a/java_console/ui/src/com/irnems/ui/DefaultExceptionHandler.java b/java_console/ui/src/com/irnems/ui/DefaultExceptionHandler.java new file mode 100644 index 0000000000..0f155fe750 --- /dev/null +++ b/java_console/ui/src/com/irnems/ui/DefaultExceptionHandler.java @@ -0,0 +1,31 @@ +package com.irnems.ui; + +import javax.swing.*; +import java.awt.*; + +import static javax.swing.JOptionPane.OK_OPTION; + +/** + * 6/30/13 + * (c) Andrey Belomutskiy + */ +public class DefaultExceptionHandler implements Thread.UncaughtExceptionHandler { + public void uncaughtException(Thread t, Throwable e) { + handleException(e); + } + + public static void handleException(Throwable e) { + // Here you should have a more robust, permanent record of problems + JOptionPane.showMessageDialog(findActiveFrame(), e.toString(), "Exception Occurred", OK_OPTION); + e.printStackTrace(); + } + + private static Frame findActiveFrame() { + Frame[] frames = JFrame.getFrames(); + for (Frame frame : frames) { + if (frame.isVisible()) + return frame; + } + return null; + } +} diff --git a/java_console/ui/src/com/irnems/ui/FrameHelper.java b/java_console/ui/src/com/irnems/ui/FrameHelper.java new file mode 100644 index 0000000000..be704c3c46 --- /dev/null +++ b/java_console/ui/src/com/irnems/ui/FrameHelper.java @@ -0,0 +1,43 @@ +package com.irnems.ui; + +import com.irnems.FileLog; + +import javax.swing.*; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + +/** + * Date: 3/24/13 + * (c) Andrey Belomutskiy + */ +public class FrameHelper { + protected final JFrame frame = new JFrame(); + + protected void showFrame(JComponent container) { + frame.setSize(800, 500); + frame.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); + frame.addWindowListener(new WindowAdapter() { + @Override + public void windowOpened(WindowEvent e) { + onWindowOpened(); + frame.setExtendedState(frame.getExtendedState() | JFrame.MAXIMIZED_BOTH); + } + + @Override + public void windowClosed(WindowEvent ev) { + onWindowClosed(); + } + }); + frame.add(container); + frame.setVisible(true); + } + + protected void onWindowOpened() { + FileLog.rlog("onWindowOpened"); + } + + protected void onWindowClosed() { + FileLog.rlog("onWindowClosed"); + FileLog.MAIN.close(); + } +} diff --git a/java_console/ui/src/com/irnems/ui/GaugePanel.java b/java_console/ui/src/com/irnems/ui/GaugePanel.java new file mode 100644 index 0000000000..257de9c06c --- /dev/null +++ b/java_console/ui/src/com/irnems/ui/GaugePanel.java @@ -0,0 +1,133 @@ +package com.irnems.ui; + +import com.irnems.core.Sensor; +import com.irnems.core.SensorCentral; +import com.irnems.ui.widgets.MafCommand; +import com.irnems.ui.widgets.PotCommand; +import com.irnems.ui.widgets.RpmCommand; +import eu.hansolo.steelseries.gauges.Radial; +import eu.hansolo.steelseries.tools.ColorDef; + +import javax.swing.*; +import java.awt.*; + +/** + * Date: 2/5/13 + * (c) Andrey Belomutskiy + */ +public class GaugePanel extends JComponent { + private static final int ADC_MAX_VALUE = 255; // mazda ECU +// private static final int ADC_MAX_VALUE = 4095; // discovery board + + public GaugePanel() { + setLayout(new GridLayout(1, 3)); + + +// Radial radial2 = createRadial("title"); + + JPanel box2 = new JPanel(new GridLayout(3, 5)); + + + box2.add(createControls()); + box2.add(createGauge(Sensor.T_CHARGE)); + box2.add(createGauge(Sensor.DWELL1)); + box2.add(createGauge(Sensor.DWELL0)); + box2.add(createGauge(Sensor.DUTY0)); + box2.add(createGauge(Sensor.ADVANCE0)); + box2.add(createGauge(Sensor.MAF)); + box2.add(createGauge(Sensor.FUEL)); + box2.add(createGauge(Sensor.FUEL_BASE)); + box2.add(createGauge(Sensor.FUEL_CLT)); + box2.add(createGauge(Sensor.FUEL_IAT)); + box2.add(createGauge(Sensor.FUEL_LAG)); + +// box2.add(createGauge(Sensor.TABLE_SPARK)); + + +// box2.add(createGauge(Sensor.DUTY1)); +// box2.add(createGauge(Sensor.ADVANCE1)); + box2.add(createGauge(Sensor.INTAKE_AIR)); + //box2.add(createGauge(Sensor.INTAKE_AIR_WIDTH)); + box2.add(createGauge(Sensor.COOLANT)); +// box2.add(createGauge(Sensor.COOLANT_WIDTH)); + + box2.add(createGauge(Sensor.MAP)); + box2.add(createGauge(Sensor.MAP_RAW)); + box2.add(createGauge(Sensor.THROTTLE)); +// box2.add(createGauge(Sensor.VREF, PotCommand.VOLTAGE_CORRECTION)); +// box2.add(createGauge(Sensor.VREF_WIDTH)); + +// box2.add(createGauge(Sensor.ADC_FAST)); +// box2.add(createGauge(Sensor.ADC_FAST_AVG)); + + + box2.add(createGauge(Sensor.AFR)); + box2.add(createGauge(Sensor.DEFAULT_FUEL)); + + box2.add(createGauge(Sensor.TIMING)); + + box2.add(createRpmGauge()); + + //add(rpmGauge); + add(box2); +// add(new JLabel("fd"), BorderLayout.EAST); + } + + private Component createControls() { + JPanel controls = new JPanel(new GridLayout(2, 1)); + controls.add(new RpmCommand()); + controls.add(new MafCommand()); + return controls; + } + + private Radial createRpmGauge() { + final Radial rpmGauge = createRadial("RPM", "", 8000, 0); + RpmModel.getInstance().addListener(new RpmModel.RpmListener() { + public void onRpmChange(RpmModel rpm) { + rpmGauge.setValue(rpm.getValue()); + } + }); + rpmGauge.setMaxMeasuredValueVisible(true); + return rpmGauge; + } + + public static Component createGauge(final Sensor sensor) { + return createGauge(sensor, 1); + } + + public static Component createGauge(final Sensor sensor, final double correction) { + final Radial gauge = createRadial(sensor.getName(), sensor.getUnits(), sensor.getMaxValue(), sensor.getMinValue()); + + gauge.setBackgroundColor(sensor.getColor()); + + SensorCentral.getInstance().addListener(sensor, new SensorCentral.AdcListener() { + public void onAdcUpdate(SensorCentral model, double value) { + gauge.setValue(value * correction); + } + }); + gauge.setLcdDecimals(2); + return gauge; + } + + private static Radial createRadial(String title, String units, double maxValue, double minValue) { +// final Section[] SECTIONS = +// { +// new Section(0, to, Color.red) +// }; + + Radial radial1 = new Radial(); +// radial1.setSections(SECTIONS); + radial1.setTitle(title); + radial1.setUnitString(units); + + //radial1.setTrackStop(to); + + radial1.setMinValue(minValue); + radial1.setMaxValue(maxValue); + radial1.setThresholdVisible(false); + radial1.setPointerColor(ColorDef.RED); + + radial1.setValue(0); + return radial1; + } +} diff --git a/java_console/ui/src/com/irnems/ui/Live3DReport.java b/java_console/ui/src/com/irnems/ui/Live3DReport.java new file mode 100644 index 0000000000..25dd6f2837 --- /dev/null +++ b/java_console/ui/src/com/irnems/ui/Live3DReport.java @@ -0,0 +1,64 @@ +package com.irnems.ui; + +import com.irnems.EcuStimulator; +import com.irnems.core.MessagesCentral; +import com.irnems.models.Point3D; +import com.irnems.models.Range; +import com.irnems.models.XYData; +import net.ericaro.surfaceplotter.DefaultSurfaceModel; + +import javax.swing.*; +import java.awt.*; + +/** + * 7/22/13 + * (c) Andrey Belomutskiy + */ +public class Live3DReport { + public static final Range KEY_RANGE = new Range(1.5f, 4.0f); + private final XYData primary = new XYData(); + private final XYData secondary = null;//new XYData(); + private final JPanel control; + + private static final String KEY = "map_adjusted: "; + + public Live3DReport() { + final DefaultSurfaceModel model = ChartHelper.createDefaultSurfaceModel(primary, EcuStimulator.RPM_RANGE, KEY_RANGE, secondary); + +// primary.fill(EcuStimulator.RPM_RANGE, KEY_RANGE, 16, 1); + + control = ChartHelper.create3DControl(primary, model, "Live Data"); + +// addPoint("1000 3 0.9", model); +// addPoint("1000 320 90", model); +// addPoint("1000 340 90", model); + + MessagesCentral.getInstance().addListener(new MessagesCentral.MessageListener() { + @Override + public void onMessage(Class clazz, String message) { + if (!message.startsWith(KEY)) + return; + message = message.substring(KEY.length()); + addPoint(message, model); + } + }); + } + + private void addPoint(String message, DefaultSurfaceModel model) { + String[] v = message.split(" "); + if (v.length != 3) + return; + + int rpm = Integer.valueOf(v[0]); + float key = Integer.valueOf(v[1]) / 100.0f; + float value = Integer.valueOf(v[2]) / 100.0f; + + primary.setPoint(new Point3D(rpm, key, value)); + primary.saveToFile("_mult.csv"); + model.plot().execute(); + } + + public Component getControl() { + return control; + } +} diff --git a/java_console/ui/src/com/irnems/ui/MsgPanel.java b/java_console/ui/src/com/irnems/ui/MsgPanel.java new file mode 100644 index 0000000000..8eff095d00 --- /dev/null +++ b/java_console/ui/src/com/irnems/ui/MsgPanel.java @@ -0,0 +1,104 @@ +package com.irnems.ui; + +import com.irnems.core.MessagesCentral; +import com.irnems.ui.widgets.AnyCommand; +import com.irnems.ui.widgets.IdleLabel; + +import javax.swing.*; +import javax.swing.text.BadLocationException; +import javax.swing.text.Document; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.text.SimpleDateFormat; +import java.util.Date; + +/** + * This panel displays plain-text 'msg' plain-text debug messages + *

+ *

+ * Date: 4/27/13 + * (c) Andrey Belomutskiy + * + * @see AnyCommand + */ +public class MsgPanel extends JPanel { + private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH_mm"); + + private final JTextPane msg = new JTextPane(); + private boolean isPaused; + + public MsgPanel(boolean needsRpmControl) { + super(new BorderLayout()); + setBorder(BorderFactory.createLineBorder(Color.green)); + JScrollPane pane = new JScrollPane(msg, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + add(pane, BorderLayout.CENTER); + MessagesCentral.getInstance().addListener(new MessagesCentral.MessageListener() { + @Override + public void onMessage(Class clazz, String message) { + final String date = DATE_FORMAT.format(new Date()); + if (!isPaused) + append(date + ": " + clazz.getSimpleName() + ": " + message); + } + }); + + JButton resetButton = new JButton("clear"); + resetButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + Document d = msg.getDocument(); + clearMessages(d); + } + }); + + final JButton pauseButton = new JButton("pause"); + pauseButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + isPaused = !isPaused; + UiUtils.setPauseButtonText(pauseButton, isPaused); + } + }); + + JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 5, 0)); + buttonPanel.add(resetButton); + buttonPanel.add(pauseButton); + buttonPanel.add(new AnyCommand()); + if (needsRpmControl) + buttonPanel.add(new RpmControl().getContent()); + add(buttonPanel, BorderLayout.NORTH); + + JPanel statsPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); + + statsPanel.add(new RpmControl().getContent()); + statsPanel.add(new IdleLabel()); + + add(statsPanel, BorderLayout.SOUTH); + } + + private void clearMessages(Document d) { + try { + d.remove(0, d.getLength()); + } catch (BadLocationException e) { + throw new IllegalStateException(e); + } + } + + private void append(String line) { + Document d = msg.getDocument(); + if (d.getLength() > 10000) + clearMessages(d); + try { + d.insertString(d.getLength(), line + "\r\n", null); + msg.select(d.getLength(), d.getLength()); + } catch (BadLocationException e) { + throw new IllegalStateException(e); + } + } + + @Override + public Dimension getPreferredSize() { + Dimension size = super.getPreferredSize(); + return new Dimension(250, size.height); + } +} diff --git a/java_console/ui/src/com/irnems/ui/RpmControl.java b/java_console/ui/src/com/irnems/ui/RpmControl.java new file mode 100644 index 0000000000..d1075c9d45 --- /dev/null +++ b/java_console/ui/src/com/irnems/ui/RpmControl.java @@ -0,0 +1,79 @@ +package com.irnems.ui; + +import com.rusefi.io.LinkManager; +import com.irnems.core.EngineTimeListener; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +/** + * Digital RPM gauge which stays green while rusEfi is connected + * + * 9/17/13 + * (c) Andrey Belomutskiy + */ +public class RpmControl { + private static final String NO_CONNECTION = "N/C"; + private final JPanel content = new JPanel(new FlowLayout(FlowLayout.LEFT, 5, 0)); + + private final JLabel rpmValue = new JLabel(NO_CONNECTION); + private final JLabel rpmCaption = new JLabel("RPM:"); + + public RpmControl() { + rpmCaption.setBorder(BorderFactory.createLineBorder(Color.white)); + rpmValue.setForeground(Color.red); + + content.setBorder(BorderFactory.createLineBorder(Color.white)); + content.add(rpmCaption); + content.add(rpmValue, "grow, wrap"); + + RpmModel.getInstance().addListener(new RpmModel.RpmListener() { + public void onRpmChange(RpmModel rpm) { + int value = rpm.getSmoothedValue(); + if (value == -1) + rpmValue.setText("Noise"); + else + rpmValue.setText(value + ""); + } + }); + + final Timer timer1 = new Timer(2000, new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + rpmValue.setText(NO_CONNECTION); + rpmValue.setForeground(Color.red); + } + }); + + + LinkManager.engineState.timeListeners.add(new EngineTimeListener() { + @Override + public void onTime(double time) { + rpmValue.setForeground(Color.green); + /** + * this timer will catch engine inactivity and display a warning + */ + timer1.restart(); + } + }); + } + + public JPanel getContent() { + return content; + } + + public RpmControl setSize(int size) { + Font f = rpmCaption.getFont(); + int fontSize = size * f.getSize(); + Font font = new Font(f.getName(), f.getStyle(), fontSize); + setFont(font); + return this; + } + + private void setFont(Font font) { + rpmCaption.setFont(font); + rpmValue.setFont(font); + } +} diff --git a/java_console/ui/src/com/irnems/ui/RpmModel.java b/java_console/ui/src/com/irnems/ui/RpmModel.java new file mode 100644 index 0000000000..f125850897 --- /dev/null +++ b/java_console/ui/src/com/irnems/ui/RpmModel.java @@ -0,0 +1,61 @@ +package com.irnems.ui; + +import com.irnems.core.Sensor; +import com.irnems.core.SensorCentral; + +import java.util.ArrayList; +import java.util.List; + +/** + * Model for RPM reading with a feature of smoothing the displayed value: new value is not displayed if updated + * value is within 5% range around currently displayed value. Here we rely on the fact that RPM values are coming in + * constantly + *

+ * Date: 12/27/12 + * (c) Andrey Belomutskiy + */ +public class RpmModel { + private static final RpmModel INSTANCE = new RpmModel(); + private static final double SMOOTHING_RATIO = 0.05; + private int displayedValue; + private int value; + private final List listeners = new ArrayList(); + + public static RpmModel getInstance() { + return INSTANCE; + } + + private RpmModel() { + SensorCentral.getInstance().addListener(Sensor.RPM, new SensorCentral.AdcListener() { + @Override + public void onAdcUpdate(SensorCentral model, double value) { + setValue((int) value); + } + }); + } + + public void setValue(int rpm) { + value = rpm; + for (RpmListener listener : listeners) + listener.onRpmChange(this); + } + + public int getValue() { + return value; + } + + public int getSmoothedValue() { + int diff = Math.abs(displayedValue - value); + if (diff > value * SMOOTHING_RATIO) + displayedValue = value; + return displayedValue; + } + + public void addListener(RpmListener listener) { + listeners.add(listener); + } + + interface RpmListener { + void onRpmChange(RpmModel rpm); + } +} diff --git a/java_console/ui/src/com/irnems/ui/RpmPanel.java b/java_console/ui/src/com/irnems/ui/RpmPanel.java new file mode 100644 index 0000000000..7e750de511 --- /dev/null +++ b/java_console/ui/src/com/irnems/ui/RpmPanel.java @@ -0,0 +1,153 @@ +package com.irnems.ui; + +import com.irnems.EcuStimulator; +import com.rusefi.io.LinkManager; +import com.irnems.core.EngineTimeListener; +import com.irnems.core.Sensor; +import com.irnems.ui.widgets.*; +import net.miginfocom.swing.MigLayout; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +/** + * Date: 1/7/13 + * (c) Andrey Belomutskiy + */ +public class RpmPanel { + private RpmControl rpmControl = new RpmControl(); + // this label displays real RPM received from ECU + // that's for CKP signal emulation + public final WaveInfoPanel wave0 = new WaveInfoPanel(0); + public final WaveInfoPanel wave1 = new WaveInfoPanel(1); + public final WaveInfoPanel wave2 = new WaveInfoPanel(2); + + public RpmPanel() { + rpmControl.setSize(15); + } + + private WaveInfoPanel findWavePanel(int index) { + WaveInfoPanel wave; + if (index == 0) + wave = wave0; + else if (index == 1) + wave = wave1; + else + throw new IllegalStateException("unexpected index " + index); + return wave; + } + + + public JComponent createRpmPanel() { + JPanel controls = createControls(); + + JPanel gauges = new JPanel(new GridLayout(2, 3)); + gauges.setBorder(BorderFactory.createLineBorder(Color.black)); +// gauges.add(GaugePanel.createCoolantGauge()); + gauges.add(GaugePanel.createGauge(Sensor.DWELL0)); + gauges.add(GaugePanel.createGauge(Sensor.DUTY0)); + gauges.add(GaugePanel.createGauge(Sensor.FUEL)); + //gauges.add(GaugePanel.createGauge(Sensor.ADVANCE0)); + + gauges.add(GaugePanel.createGauge(Sensor.VREF, PotCommand.VOLTAGE_CORRECTION)); + gauges.add(GaugePanel.createGauge(Sensor.MAF)); + gauges.add(GaugePanel.createGauge(Sensor.DWELL1)); +// gauges.add(GaugePanel.createGauge(Sensor.ADVANCE1)); +// gauges.add(GaugePanel.createGauge(Sensor.MAF)); + + + final Timer reconnectTimer = new Timer(10000, new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + LinkManager.restart(); + } + }); + reconnectTimer.restart(); + + LinkManager.engineState.timeListeners.add(new EngineTimeListener() { + @Override + public void onTime(double time) { + /** + * this timer will reconnect + */ + postponeReconnecting(reconnectTimer); + + } + }); + + JComponent rpmPanel = new JPanel(new BorderLayout()); + rpmPanel.setBorder(BorderFactory.createLineBorder(Color.white)); + + rpmPanel.add(rpmControl.getContent(), BorderLayout.NORTH); + rpmPanel.add(controls, BorderLayout.WEST); + rpmPanel.add(gauges, BorderLayout.CENTER); + MsgPanel msgPanel = new MsgPanel(false); + rpmPanel.add(msgPanel, BorderLayout.EAST); + + return rpmPanel; + } + + private void postponeReconnecting(Timer timer2) { + timer2.restart(); + } + + private JPanel createControls() { + JPanel controls = new JPanel(new MigLayout()); + controls.setBorder(BorderFactory.createLineBorder(Color.red)); + JButton button = createButton(); +// controls.add(button, "grow, wrap"); + + controls.add(new RpmCommand(), "grow, wrap"); +// controls.add(new PotCommand(0).panel, "grow, wrap"); +// controls.add(new PotCommand(1).panel, "grow, wrap"); + controls.add(new AnyCommand(), "grow, wrap"); + + controls.add(new MafCommand(), "grow, wrap"); + + controls.add(wave0.getControl(), "grow, wrap"); + controls.add(wave1.getControl(), "grow, wrap"); + controls.add(wave2.getControl(), "grow, wrap"); + + + controls.add(new AdcDebugControl().getControl(), "grow, wrap"); + + controls.add(new InjectorControl(0, Sensor.INJECTOR_0_STATUS).getControl(), "grow, wrap"); + controls.add(new InjectorControl(1, Sensor.INJECTOR_1_STATUS).getControl(), "grow, wrap"); + controls.add(new InjectorControl(2, Sensor.INJECTOR_2_STATUS).getControl(), "grow, wrap"); + controls.add(new InjectorControl(3, Sensor.INJECTOR_3_STATUS).getControl(), "grow, wrap"); + + controls.add(new LogModeWidget().getPanel(), "grow, wrap"); + + return controls; + } + + private JButton createButton() { + final JButton button = new JButton("run ECU stimulation"); + button.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + new Thread(new Runnable() { + @Override + public void run() { + try { + EcuStimulator.buildTable(); + } catch (Throwable e) { + e.printStackTrace(); + System.exit(-20); + } + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + button.setText("Done"); + } + }); + } + }, "Ecu Stimulator").start(); + } + }); + return button; + } +} + diff --git a/java_console/ui/src/com/irnems/ui/UiUtils.java b/java_console/ui/src/com/irnems/ui/UiUtils.java new file mode 100644 index 0000000000..6c73d5de46 --- /dev/null +++ b/java_console/ui/src/com/irnems/ui/UiUtils.java @@ -0,0 +1,36 @@ +package com.irnems.ui; + +import javax.imageio.ImageIO; +import javax.swing.*; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; + +/** + * 7/7/13 + * (c) Andrey Belomutskiy + */ +public class UiUtils { + public static void saveImage(String fileName, Component component) { + BufferedImage img = getScreenShot(component); + try { + ImageIO.write(img, "png", new File(fileName)); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + private static BufferedImage getScreenShot(Component component) { + // http://stackoverflow.com/questions/5853879/swing-obtain-image-of-jframe/5853992 + BufferedImage image = new BufferedImage(component.getWidth(), component.getHeight(), BufferedImage.TYPE_INT_RGB); + // call the Component's paint method, using + // the Graphics object of the image. + component.paint(image.getGraphics()); + return image; + } + + public static void setPauseButtonText(JButton pauseButton, boolean isPaused) { + pauseButton.setText(isPaused ? "resume" : "pause"); + } +} diff --git a/java_console/ui/src/com/irnems/ui/WavePanel.java b/java_console/ui/src/com/irnems/ui/WavePanel.java new file mode 100644 index 0000000000..5729f4f525 --- /dev/null +++ b/java_console/ui/src/com/irnems/ui/WavePanel.java @@ -0,0 +1,184 @@ +package com.irnems.ui; + +import com.irnems.ChartRepository; +import com.irnems.FileLog; +import com.irnems.core.EngineState; +import com.irnems.core.Sensor; +import com.irnems.core.SensorCentral; +import com.irnems.ui.widgets.AnyCommand; +import com.irnems.ui.widgets.UpDownImage; +import com.rusefi.io.LinkManager; +import com.rusefi.waves.RevolutionLog; +import com.rusefi.waves.WaveChart; +import com.rusefi.waves.WaveChartParser; +import com.rusefi.waves.WaveReport; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * Digital Sniffer control consists of a set of {@link UpDownImage} + *

+ *

+ * Date: 6/23/13 + * Andrey Belomutskiy (c) 2012-2013 + * + * @see ChartStatusPanel status bar + */ +public class WavePanel extends JPanel { + private static final int EFI_DEFAULT_CHART_SIZE = 180; + + private final Map images = new LinkedHashMap(); + private final JPanel imagePanel = new JPanel(); + private final ZoomControl zoomControl = new ZoomControl(); + private final ChartStatusPanel statusPanel = new ChartStatusPanel(zoomControl); + private final UpDownImage crank = register("crank"); + + private boolean isPaused; + + private static WavePanel instance = new WavePanel(); + + private WavePanel() { + setLayout(new BorderLayout()); + + statusPanel.setWaveReport(crank.createTranslator()); + + JButton resetButton = new JButton("reset"); + resetButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + for (UpDownImage image : images.values()) + image.setWaveReport(WaveReport.MOCK, null); + } + }); + + JButton saveImageButton = new JButton("save image"); + saveImageButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + saveImage(); + } + }); + + final JButton pauseButton = new JButton("pause"); + pauseButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + isPaused = !isPaused; + UiUtils.setPauseButtonText(pauseButton, isPaused); + } + }); + + JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 5, 0)); + buttonPanel.add(resetButton); + buttonPanel.add(saveImageButton); + buttonPanel.add(pauseButton); + buttonPanel.add(new RpmControl().setSize(2).getContent()); + + JTextField command = AnyCommand.createCommandControl(); + command.setText("chartsize " + EFI_DEFAULT_CHART_SIZE); + buttonPanel.add(command); + + buttonPanel.add(zoomControl); + + buttonPanel.add(ChartRepository.getInstance().createControls(new ChartRepository.CRListener() { + @Override + public void onDigitalChart(String chart) { + displayChart(chart); + } + })); + + add(buttonPanel, BorderLayout.NORTH); + add(imagePanel, BorderLayout.CENTER); + add(statusPanel.infoPanel, BorderLayout.SOUTH); + + zoomControl.listener = new ZoomControl.ZoomControlListener() { + @Override + public void onZoomChange() { + UpDownImage.trueRepaint(imagePanel); + } + }; + + crank.setZoomProvider(zoomControl); + imagePanel.add(crank); + createSecondaryImage("crank2"); + createSecondaryImage("input1 A8"); + createSecondaryImage("input2 E5"); + + createSecondaryImage(WaveChart.SPARK_1); + createSecondaryImage(WaveChart.SPARK_2); + createSecondaryImage(WaveChart.SPARK_3); + createSecondaryImage(WaveChart.SPARK_4); + + createSecondaryImage(WaveChart.INJECTOR_1); + createSecondaryImage(WaveChart.INJECTOR_2); + createSecondaryImage(WaveChart.INJECTOR_3); + createSecondaryImage(WaveChart.INJECTOR_4); + + LinkManager.engineState.registerStringValueAction(WaveReport.WAVE_CHART, new EngineState.ValueCallback() { + @Override + public void onUpdate(String value) { + if (isPaused) + return; + displayChart(value); + } + }); + +// displayChart("wave_chart,crank2!down!192811978!crank2!up!192813389!crank2!down!192813749!crank2!up!192815156!crank2!down!192815512!crank!up!192820764!crank2!up!192825818!crank2!down!192826182!crank2!up!192827610!crank2!down!192827975!crank2!up!192829399!crank2!down!192829757!crank2!up!192831154!crank2!down!192831507!r!187!192834224!crank!down!192834224!crank2!up!192836757!crank2!down!192841994!crank2!up!192843561!crank2!down!192843925!crank2!up!192845334!crank2!down!192845693!crank2!up!192847086!crank2!down!192847439!crank!up!192853135!crank2!up!192857701!crank2!down!192858065!crank2!up!192859491!crank2!down!192859858!crank2!up!192861269!crank2!down!192861626!crank2!up!192863025!crank2!down!192863382!crank2!up!192868647!crank!down!192871268!crank2!down!192872804!crank2!up!192872804!crank!down!192872804!crank!up!192872804!crank2!down!192873898!crank2!up!192875508!crank2!down!192875887!crank2!up!192877357!crank2!down!192877732!crank2!up!192879192!crank2!down!192879565!crank!up!192886293!r!0!194982088!crank!down!194982088!crank2!up!194984699!crank2!down!194990112!crank2!up!194991715!crank2!down!194992085!crank2!up!194993530!crank2!down!194993884!crank2!up!194995292!crank2!down!194995645!crank!up!195001475!crank2!up!195006153!crank2!down!195006515!crank2!up!195007968!crank2!down!195008325!crank2!up!195009773!crank2!down!195010134!crank2!up!195011549!crank2!down!195011901!crank2!up!195017256!crank!down!195019915!crank2!down!195022597!crank2!up!195024189!crank2!down!195024554!crank2!up!195025980!crank2!down!195026329!crank2!up!195027744!crank2!down!195028103!crank!up!195033418!crank2!up!195038542!crank2!down!195038911!crank2!up!195040351!crank2!down!195040722!crank2!up!195042167!crank2!down!195042529!crank2!up!195043934!crank2!down!195044294!r!187!195047060!crank!down!195047060!crank2!up!195049619!crank2!down!195054954!crank2!up!195056549!crank2!down!195056920!crank2!up!195058345!crank2!down!195058703!crank2!up!195060114!crank2!down!195060464!crank!up!195066245!crank2!up!195070882!crank2!down!195071250!crank2!up!195072689!crank2!down!195073054!crank2!up!195074479!,"); + } + + public static WavePanel getInstance() { + return instance; + } + + public void displayChart(String value) { + WaveChart map = WaveChartParser.unpackToMap(value); + + StringBuilder revolutions = map.get(RevolutionLog.TOP_DEAD_CENTER_MESSAGE); + + statusPanel.setRevolutions(revolutions); + + for (Map.Entry e : map.map.entrySet()) { + String imageName = e.getKey(); + String report = e.getValue().toString(); + + UpDownImage image = images.get(imageName); + if (image == null) + continue; + image.setRevolutions(revolutions); + List list = WaveReport.parse(report); + if (list.isEmpty()) { + image.onUpdate(); // this would reset empty image + continue; + } + WaveReport wr = new WaveReport(list); + image.setWaveReport(wr, revolutions); + } + } + + private void createSecondaryImage(String name) { + UpDownImage image = register(name).setTranslator(crank.createTranslator()); + image.setZoomProvider(zoomControl); + imagePanel.add(image); + imagePanel.setLayout(new GridLayout(images.size(), 1)); + } + + private void saveImage() { + int rpm = RpmModel.getInstance().getValue(); + double maf = SensorCentral.getInstance().getValue(Sensor.MAF); + String fileName = FileLog.getDate() + "rpm_" + rpm + "_maf_" + maf + ".png"; + UiUtils.saveImage(fileName, imagePanel); + } + + private UpDownImage register(String name) { + UpDownImage image = new UpDownImage(name); + image.addMouseMotionListener(statusPanel.motionAdapter); + images.put(name, image); + return image; + } +} diff --git a/java_console/ui/src/com/irnems/ui/ZoomControl.java b/java_console/ui/src/com/irnems/ui/ZoomControl.java new file mode 100644 index 0000000000..5203a3f79a --- /dev/null +++ b/java_console/ui/src/com/irnems/ui/ZoomControl.java @@ -0,0 +1,67 @@ +package com.irnems.ui; + +import com.irnems.waves.ZoomProvider; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +/** + * 7/7/13 + * (c) Andrey Belomutskiy + */ +public class ZoomControl extends JPanel implements ZoomProvider { + private final JLabel currentValue = new JLabel(); + private double value; + public ZoomControlListener listener = null; + + public ZoomControl() { + super(new FlowLayout()); + setValue(1); + +// final JTextField text = new JTextField() { +// @Override +// public Dimension getPreferredSize() { +// Dimension size = super.getPreferredSize(); +// return new Dimension(200, size.height); +// } +// }; + + add(currentValue); + + JButton plus = new JButton("+"); + plus.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + setValue(value * 1.1); + } + }); + add(plus); + + JButton minus = new JButton("-"); + minus.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + setValue(value / 1.1); + } + }); + add(minus); + } + + private void setValue(double value) { + this.value = value; + currentValue.setText(String.format(" %.4fms", value)); + if (listener != null) + listener.onZoomChange(); + } + + @Override + public double getZoomValue() { + return value; + } + + interface ZoomControlListener { + void onZoomChange(); + } +} diff --git a/java_console/ui/src/com/irnems/ui/test/RpmModelTest.java b/java_console/ui/src/com/irnems/ui/test/RpmModelTest.java new file mode 100644 index 0000000000..6e9ca48cdf --- /dev/null +++ b/java_console/ui/src/com/irnems/ui/test/RpmModelTest.java @@ -0,0 +1,28 @@ +package com.irnems.ui.test; + +import com.irnems.ui.RpmModel; +import org.junit.Assert; +import org.junit.Test; + +/** + * Date: 12/27/12 + * (c) Andrey Belomutskiy + */ +public class RpmModelTest { + @Test + public void testRpmSmoothing() { + RpmModel r = RpmModel.getInstance(); + r.setValue(100); + + Assert.assertEquals(100, r.getSmoothedValue()); + + r.setValue(104); + Assert.assertEquals(100, r.getSmoothedValue()); + + r.setValue(96); + Assert.assertEquals(100, r.getSmoothedValue()); + + r.setValue(200); + Assert.assertEquals(200, r.getSmoothedValue()); + } +} diff --git a/java_console/ui/src/com/irnems/ui/test/UpDownSandbox.java b/java_console/ui/src/com/irnems/ui/test/UpDownSandbox.java new file mode 100644 index 0000000000..110fb3c472 --- /dev/null +++ b/java_console/ui/src/com/irnems/ui/test/UpDownSandbox.java @@ -0,0 +1,33 @@ +package com.irnems.ui.test; + +import com.rusefi.waves.WaveReport; +import com.irnems.core.test.WaveReportTest; +import com.irnems.ui.FrameHelper; +import com.irnems.ui.widgets.UpDownImage; + +import javax.swing.*; +import java.lang.reflect.InvocationTargetException; + +/** + * Date: 6/23/13 + * (c) Andrey Belomutskiy + */ +public class UpDownSandbox extends FrameHelper { + + public UpDownSandbox() { + + WaveReport wr = new WaveReport(WaveReportTest.report); + + showFrame(new UpDownImage(wr, "test")); + + } + + public static void main(String[] args) throws InvocationTargetException, InterruptedException { + SwingUtilities.invokeAndWait(new Runnable() { + @Override + public void run() { + new UpDownSandbox(); + } + }); + } +} diff --git a/java_console/ui/src/com/irnems/ui/test/WavePanelSandbox.java b/java_console/ui/src/com/irnems/ui/test/WavePanelSandbox.java new file mode 100644 index 0000000000..f67ac9cbfb --- /dev/null +++ b/java_console/ui/src/com/irnems/ui/test/WavePanelSandbox.java @@ -0,0 +1,33 @@ +package com.irnems.ui.test; + +import com.irnems.ui.FrameHelper; +import com.irnems.ui.WavePanel; + +import javax.swing.*; +import java.lang.reflect.InvocationTargetException; + +/** + * 7/25/13 + * (c) Andrey Belomutskiy + */ +public class WavePanelSandbox extends FrameHelper { + + public WavePanelSandbox() { + + + WavePanel wp = WavePanel.getInstance(); + + wp.displayChart("Injector 1!down!55013038!crank!up!55013444!crank!down!55013781!Injector 3!up!55013789!Injector 3!down!55013969!crank2!up!55014065!crank!up!55014358!crank!down!55014706!input1 A8!up!55014730!Injector 4!up!55014734!Injector 4!down!55014933!input1 A8!down!55014941!crank!up!55015298!crank!down!55015638!Injector 2!up!55015644!Injector 2!down!55015823!crank2!down!55015932!crank!up!55016223!crank!down!55016566!Injector 1!up!55016574!Injector 1!down!55016753!crank!up!55017148!crank!down!55017494!Injector 3!up!55017499!Injector 3!down!55017679!crank2!up!55017777!crank!up!55018070!crank!down!55018406!Injector 4!input1 A8!up!55018414!up!55018416!input1 A8!down!55018439!input1 A8!up!55018439!Injector 4!down!55018593!input1 A8!down!55018600!crank!up!55018989!crank!down!55019330!Injector 2!up!55019338!Injector 2!down!55019517!crank2!down!55019618!crank!up!55019909!crank!down!55020246!Injector 1!up!55020251!Injector 1!down!55020430!crank!up!55020827!crank!down!55021171!Injector 3!up!55021179!Injector 3!down!55021358!crank2!up!55021453!crank!up!55021747!crank!down!55022084!Injector 4input1 A8!up!55022089!!up!55022093!Injector 4!down!55022270!input1 A8!down!55022276!input1 A8!up!55022276!input1 A8!down!55022276!crank!up!55022666!crank!down!55023006!Injector 2!up!55023011!Injector 2!down!55023191!crank2!down!55023294!crank!up!55023584!crank!down!55023928!Injector 1!up!55023932!Injector 1!down!55024109!crank!up!55024507!crank!down!55024846!Injector 3!up!55024871!Injector 3!down!55025072!crank2!up!55025138!crank!up!55025440!crank!down!55025780!Injector 4input1 A8!up!55025785!!up!55025791!input1 A8!down!55025923!input1 A8!up!55025923!Injector 4!down!55025970!input1 A8!down!55025976!input1 A8!up!55025976!crank!up!55026355!crank!down!55026690!Injector 2!up!55026696!Injector 2!down!55026874!crank2!down!55026979!crank!up!55027268!crank!down!55027611!Injector 1!up!55027619!Injector 1!down!55027800!crank!up!55028186!crank!down!55028520!,"); + + showFrame(wp); + } + + public static void main(String[] args) throws InvocationTargetException, InterruptedException { + SwingUtilities.invokeAndWait(new Runnable() { + @Override + public void run() { + new WavePanelSandbox(); + } + }); + } +} diff --git a/java_console/ui/src/com/irnems/ui/widgets/AdcDebugControl.java b/java_console/ui/src/com/irnems/ui/widgets/AdcDebugControl.java new file mode 100644 index 0000000000..2b03a06980 --- /dev/null +++ b/java_console/ui/src/com/irnems/ui/widgets/AdcDebugControl.java @@ -0,0 +1,15 @@ +package com.irnems.ui.widgets; + +import com.irnems.core.Sensor; + +/** + * 7/11/13 + * (c) Andrey Belomutskiy + */ +public class AdcDebugControl extends BooleanFlagControlPanel { + public AdcDebugControl() { + super("Adc Debug", ""); + installStatusReader(Sensor.ADC_STATUS); + installCommand("adcDebug "); + } +} diff --git a/java_console/ui/src/com/irnems/ui/widgets/AnyCommand.java b/java_console/ui/src/com/irnems/ui/widgets/AnyCommand.java new file mode 100644 index 0000000000..c5239d6c77 --- /dev/null +++ b/java_console/ui/src/com/irnems/ui/widgets/AnyCommand.java @@ -0,0 +1,47 @@ +package com.irnems.ui.widgets; + +import com.rusefi.io.CommandQueue; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +/** + * Date: 3/20/13 + * (c) Andrey Belomutskiy + */ +public class AnyCommand extends JPanel { + public AnyCommand() { + setBorder(BorderFactory.createLineBorder(Color.PINK)); + setLayout(new FlowLayout(FlowLayout.LEFT)); + add(new JLabel("Command: ")); + final JTextField text = createCommandControl(); + add(text); + } + + public static JTextField createCommandControl() { + final JTextField text = new JTextField() { + @Override + public Dimension getPreferredSize() { + Dimension size = super.getPreferredSize(); + return new Dimension(200, size.height); + } + }; + text.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + String cmd = text.getText(); + int timeout = isSlowCommand(cmd) ? 5000 : 300; + CommandQueue.getInstance().write(cmd, timeout); + } + }); + // todo: limit the length of text in the text field + return text; + } + + private static boolean isSlowCommand(String cmd) { + String lc = cmd.toLowerCase(); + return lc.startsWith("set_engine_type") || lc.startsWith("writeconfig") || lc.startsWith("showconfig"); + } +} diff --git a/java_console/ui/src/com/irnems/ui/widgets/BooleanFlagControlPanel.java b/java_console/ui/src/com/irnems/ui/widgets/BooleanFlagControlPanel.java new file mode 100644 index 0000000000..a46f867eab --- /dev/null +++ b/java_console/ui/src/com/irnems/ui/widgets/BooleanFlagControlPanel.java @@ -0,0 +1,50 @@ +package com.irnems.ui.widgets; + +import com.irnems.core.Sensor; +import com.irnems.core.SensorCentral; +import com.rusefi.io.CommandQueue; +import net.miginfocom.swing.MigLayout; + +import javax.swing.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +/** + * This panel turns ON/OFF some rusefi configuration property + *

+ * 7/11/13 + * (c) Andrey Belomutskiy + */ +public class BooleanFlagControlPanel { + private final JPanel content = new JPanel(new MigLayout()); + protected final JCheckBox checkBox; + + public BooleanFlagControlPanel(String labelCaption, String checkboxCaption) { + content.add(new JLabel(labelCaption)); + checkBox = new JCheckBox(checkboxCaption); + content.add(checkBox); + } + + public JComponent getControl() { + return content; + } + + protected void installCommand(final String command) { + checkBox.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + int mode = checkBox.isSelected() ? 1 : 0; + CommandQueue.getInstance().write(command + mode); + } + }); + } + + protected void installStatusReader(final Sensor statusSensor) { + SensorCentral.getInstance().addListener(statusSensor, new SensorCentral.AdcListener() { + @Override + public void onAdcUpdate(SensorCentral model, double value) { + checkBox.setSelected(value > 0); + } + }); + } +} diff --git a/java_console/ui/src/com/irnems/ui/widgets/IdleLabel.java b/java_console/ui/src/com/irnems/ui/widgets/IdleLabel.java new file mode 100644 index 0000000000..4e424b4cb2 --- /dev/null +++ b/java_console/ui/src/com/irnems/ui/widgets/IdleLabel.java @@ -0,0 +1,21 @@ +package com.irnems.ui.widgets; + +import com.irnems.core.Sensor; +import com.irnems.core.SensorCentral; + +import javax.swing.*; + +/** + * 8/2/13 + * (c) Andrey Belomutskiy + */ +public class IdleLabel extends JLabel { + public IdleLabel() { + SensorCentral.getInstance().addListener(Sensor.IDLE_SWITCH, new SensorCentral.AdcListener() { + @Override + public void onAdcUpdate(SensorCentral model, double value) { + IdleLabel.this.setText("Idle: " + (value == 0)); + } + }); + } +} diff --git a/java_console/ui/src/com/irnems/ui/widgets/InjectorControl.java b/java_console/ui/src/com/irnems/ui/widgets/InjectorControl.java new file mode 100644 index 0000000000..31ac982d9c --- /dev/null +++ b/java_console/ui/src/com/irnems/ui/widgets/InjectorControl.java @@ -0,0 +1,15 @@ +package com.irnems.ui.widgets; + +import com.irnems.core.Sensor; + +/** + * 7/13/13 + * (c) Andrey Belomutskiy + */ +public class InjectorControl extends BooleanFlagControlPanel { + public InjectorControl(int id, Sensor sensor) { + super("Injector " + id, ""); + installStatusReader(sensor); + installCommand("injector " + id + " "); + } +} diff --git a/java_console/ui/src/com/irnems/ui/widgets/JTextFieldWithWidth.java b/java_console/ui/src/com/irnems/ui/widgets/JTextFieldWithWidth.java new file mode 100644 index 0000000000..4bf6b80b27 --- /dev/null +++ b/java_console/ui/src/com/irnems/ui/widgets/JTextFieldWithWidth.java @@ -0,0 +1,31 @@ +package com.irnems.ui.widgets; + +import javax.swing.*; +import java.awt.*; + +/** + * 7/18/13 + * (c) Andrey Belomutskiy + */ +public class JTextFieldWithWidth extends JTextField { + private int width; + + public JTextFieldWithWidth() { + this("", 200); + } + + public JTextFieldWithWidth(String text) { + this(text, 200); + } + + public JTextFieldWithWidth(String text, int width) { + super(text); + this.width = width; + } + + @Override + public Dimension getPreferredSize() { + Dimension size = super.getPreferredSize(); + return new Dimension(width, size.height); + } +} diff --git a/java_console/ui/src/com/irnems/ui/widgets/LogModeWidget.java b/java_console/ui/src/com/irnems/ui/widgets/LogModeWidget.java new file mode 100644 index 0000000000..43a582865c --- /dev/null +++ b/java_console/ui/src/com/irnems/ui/widgets/LogModeWidget.java @@ -0,0 +1,35 @@ +package com.irnems.ui.widgets; + +import com.rusefi.io.CommandQueue; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +/** + * Date: 3/29/13 + * (c) Andrey Belomutskiy + */ +public class LogModeWidget { + private final JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)); + + private final JCheckBox mode = new JCheckBox("full logging"); + + public LogModeWidget() { + panel.setBorder(BorderFactory.createLineBorder(Color.black)); + panel.add(mode); + + mode.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + int code = mode.isSelected() ? 1 : 0; + CommandQueue.getInstance().write("fl " + code); + } + }); + } + + public JPanel getPanel() { + return panel; + } +} diff --git a/java_console/ui/src/com/irnems/ui/widgets/MafCommand.java b/java_console/ui/src/com/irnems/ui/widgets/MafCommand.java new file mode 100644 index 0000000000..ffeda2ff35 --- /dev/null +++ b/java_console/ui/src/com/irnems/ui/widgets/MafCommand.java @@ -0,0 +1,42 @@ +package com.irnems.ui.widgets; + +import com.irnems.EcuStimulator; +import com.irnems.core.Sensor; + +import javax.swing.*; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import java.awt.*; + +/** + * 6/30/13 + * (c) Andrey Belomutskiy + */ +public class MafCommand extends JPanel { + public MafCommand() { + setBorder(BorderFactory.createLineBorder(Color.BLACK)); + setLayout(new FlowLayout(FlowLayout.LEFT)); + add(new JLabel("MAF: ")); + final JSpinner maf = new JSpinner(new SpinnerNumberModel(Double.valueOf(1.5), null, null, Double.valueOf(0.11))) { + @Override + public Dimension getPreferredSize() { + Dimension size = super.getPreferredSize(); + return new Dimension(100, size.height); + } + }; + final SpinnerNumberModel m = (SpinnerNumberModel) maf.getModel(); +// m.setStepSize(0.1); +// maf.setValue(3); + maf.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + Double value = (Double) m.getValue(); + + EcuStimulator.setPotVoltage(value, Sensor.MAF); + } + }); + + + add(maf); + } +} diff --git a/java_console/ui/src/com/irnems/ui/widgets/PotCommand.java b/java_console/ui/src/com/irnems/ui/widgets/PotCommand.java new file mode 100644 index 0000000000..724ffcfc10 --- /dev/null +++ b/java_console/ui/src/com/irnems/ui/widgets/PotCommand.java @@ -0,0 +1,109 @@ +package com.irnems.ui.widgets; + +import com.irnems.core.MessagesCentral; +import com.irnems.core.Sensor; +import com.irnems.core.SensorCentral; +import com.rusefi.io.CommandQueue; + +import javax.swing.*; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import java.awt.*; + +/** + * Date: 3/17/13 + * (c) Andrey Belomutskiy + */ +public class PotCommand { + public static final double VOLTAGE_CORRECTION = 2.9 / 3; + private static final int MAF_CHANNEL_ECU_INTERNAL_RESISTANCE = 1000; // 1KOhm internal resistor? + public final JPanel panel; + final JSpinner potSpinner; + + public PotCommand(final int channel) { + final JLabel rValue = new JLabel(); + + final JSpinner voltageSpinner = new JSpinner(new SpinnerNumberModel(0.0, 0, 5, 0.1)) { + @Override + public Dimension getPreferredSize() { + Dimension size = super.getPreferredSize(); + return new Dimension(100, size.height); + } + }; + ((SpinnerNumberModel) voltageSpinner.getModel()).setStepSize(0.1); + voltageSpinner.setValue(1.0); + voltageSpinner.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + Double Vout = (Double) voltageSpinner.getValue(); + int d = getPotResistance(Vout); + potSpinner.setValue(d); + } + }); + + + potSpinner = new JSpinner() { + @Override + public Dimension getPreferredSize() { + Dimension size = super.getPreferredSize(); + return new Dimension(100, size.height); + } + }; + potSpinner.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + Integer value = (Integer) potSpinner.getValue(); + try { + requestPotChange(channel, value); + } catch (IllegalArgumentException ignore) { + return; + } + int r = getRbyD(value); + rValue.setText("R=" + r); + } + }); + potSpinner.setValue(10); + + + JPanel upper = new JPanel(new FlowLayout(FlowLayout.LEFT)); + upper.add(new JLabel("set pot" + channel + ": ")); + upper.add(potSpinner); + upper.add(rValue); + + JPanel center = new JPanel(new FlowLayout(FlowLayout.LEFT)); + center.add(new JLabel("volts: ")); + center.add(voltageSpinner); + + panel = new JPanel(new BorderLayout()); + panel.setBorder(BorderFactory.createLineBorder(Color.CYAN)); + panel.add(upper, BorderLayout.NORTH); + panel.add(center, BorderLayout.CENTER); + } + + public static void requestPotChange(int channel, int resistance) { + if (resistance < 0 || resistance > 10000) + throw new IllegalArgumentException("resistance: " + resistance); + CommandQueue.getInstance().write("pot" + channel + " " + resistance); + } + + public static int getPotResistance(Double vout) { + double vRef = SensorCentral.getInstance().getValue(Sensor.VREF) * VOLTAGE_CORRECTION; + double r = getR1InVoltageDividor3(vout, vRef, MAF_CHANNEL_ECU_INTERNAL_RESISTANCE); + MessagesCentral.getInstance().postMessage(PotCommand.class, "VRef=" + vRef + ", needed resistance: " + r); + // pot command accept resistance and does the conversion itself + return (int) r; + } + + private static int getRbyD(Integer value) { + return (int) (10000.0 * (256 - value) / 256) + 52; + } + +// private static int getDbyR(double Rwa) { +// return (int) (256 - (Rwa - 52) * 256 / 10000); +// } + + private static double getR1InVoltageDividor3(double Vout, double Vin, double r2) { + return r2 * Vin / Vout - r2; + } + +} diff --git a/java_console/ui/src/com/irnems/ui/widgets/RpmCommand.java b/java_console/ui/src/com/irnems/ui/widgets/RpmCommand.java new file mode 100644 index 0000000000..453cbb8778 --- /dev/null +++ b/java_console/ui/src/com/irnems/ui/widgets/RpmCommand.java @@ -0,0 +1,43 @@ +package com.irnems.ui.widgets; + +import com.rusefi.io.CommandQueue; + +import javax.swing.*; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import java.awt.*; + +/** + * Date: 3/17/13 + * (c) Andrey Belomutskiy + */ +public class RpmCommand extends JPanel { + public RpmCommand() { + setBorder(BorderFactory.createLineBorder(Color.ORANGE)); + setLayout(new FlowLayout(FlowLayout.LEFT)); + add(new JLabel("set RPM: ")); + + final JSpinner spinner = new JSpinner() { + @Override + public Dimension getPreferredSize() { + Dimension size = super.getPreferredSize(); + return new Dimension(100, size.height); + } + }; + SpinnerNumberModel m = (SpinnerNumberModel) spinner.getModel(); + m.setStepSize(100); + spinner.setValue(600); + + spinner.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + requestRpmChange((Integer) spinner.getValue()); + } + }); + add(spinner); + } + + public static void requestRpmChange(int rpm) { + CommandQueue.getInstance().write("rpm " + rpm); + } +} diff --git a/java_console/ui/src/com/irnems/ui/widgets/UpDownImage.java b/java_console/ui/src/com/irnems/ui/widgets/UpDownImage.java new file mode 100644 index 0000000000..7a0d111bcb --- /dev/null +++ b/java_console/ui/src/com/irnems/ui/widgets/UpDownImage.java @@ -0,0 +1,194 @@ +package com.irnems.ui.widgets; + +import com.irnems.waves.TimeAxisTranslator; +import com.rusefi.waves.WaveReport; +import com.irnems.waves.ZoomProvider; +import com.rusefi.waves.RevolutionLog; + +import javax.swing.*; +import java.awt.*; +import java.text.SimpleDateFormat; +import java.util.Date; + +/** + * This is a renderer of {@link WaveReport} - this makes a simple Logical Analyzer + *

+ *

+ * Date: 6/23/13 + * (c) Andrey Belomutskiy + */ +public class UpDownImage extends JPanel { + private static final SimpleDateFormat FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss"); + private static final int LINE_SIZE = 20; + + private long lastUpdateTime; + private ZoomProvider zoomProvider = ZoomProvider.DEFAULT; + private WaveReport wr; + private StringBuilder revolutions; + private final String name; + private TimeAxisTranslator translator; + private RevolutionLog time2rpm = RevolutionLog.parseRevolutions(null); + + public UpDownImage(final String name) { + this(WaveReport.MOCK, name); + } + + public void setZoomProvider(ZoomProvider zoomProvider) { + this.zoomProvider = zoomProvider; + } + + public void onUpdate() { + trueRepaint(this); + } + + public static void trueRepaint(JComponent control) { + control.invalidate(); + control.repaint(); + } + + public UpDownImage(WaveReport wr, String name) { + this.name = name; + setWaveReport(wr, null); + setOpaque(true); + translator = createTranslator(); + } + + public UpDownImage setTranslator(TimeAxisTranslator translator) { + this.translator = translator; + return this; + } + + public TimeAxisTranslator createTranslator() { + return new TimeAxisTranslator() { + @Override + public int timeToScreen(int time, int width, ZoomProvider zoomProvider) { + return UpDownImage.this.wr.timeToScreen(time, width, zoomProvider); + } + + @Override + public double screenToTime(int screen, int width, ZoomProvider zoomProvider) { + return UpDownImage.this.wr.screenToTime(screen, width, zoomProvider); + } + + @Override + public int getMaxTime() { + return UpDownImage.this.wr.getMaxTime(); + } + + @Override + public int getMinTime() { + return UpDownImage.this.wr.getMinTime(); + } + }; + } + + public void setWaveReport(WaveReport wr, StringBuilder revolutions) { + this.wr = wr; + this.revolutions = revolutions; + lastUpdateTime = System.currentTimeMillis(); + onUpdate(); + } + + @Override + public void paint(Graphics g) { + super.paint(g); + Graphics2D g2 = (Graphics2D) g; + + Dimension d = getSize(); + g.setColor(getBackground()); + g.fillRect(0, 0, d.width, d.height); + + for (WaveReport.UpDown upDown : wr.getList()) + paintUpDown(d, upDown, g); + + paintScaleLines(g2, d); + + int duration = wr.getDuration(); + g2.setColor(Color.black); + + int line = 0; + g.drawString(name, 5, ++line * LINE_SIZE); + g.drawString("Tick length: " + duration + "; count=" + wr.getList().size(), 5, ++line * LINE_SIZE); + g.drawString("Total seconds: " + (duration / WaveReport.SYS_TICKS_PER_MS / 000.0), 5, ++line * LINE_SIZE); + g.drawString(FORMAT.format(new Date(lastUpdateTime)), 5, ++line * LINE_SIZE); + + drawStartOfRevolution(g2, d); + } + + private void drawStartOfRevolution(Graphics2D g2, Dimension d) { + if (revolutions == null) + return; + + RevolutionLog time2rpm = RevolutionLog.parseRevolutions(revolutions); + + g2.setStroke(new BasicStroke()); + for (int time : time2rpm.keySet()) { + int x = translator.timeToScreen(time, d.width, zoomProvider); + g2.setColor(Color.green); + g2.drawLine(x, 0, x, d.height); + } + } + + private static final BasicStroke LONG_STROKE = new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 10.0f, + new float[]{21.0f, 7.0f}, 0.0f); + + /** + * This method draws a vertical line every millisecond + */ + private void paintScaleLines(Graphics2D g2, Dimension d) { + int fromMs = translator.getMinTime() / WaveReport.mult; + g2.setStroke(LONG_STROKE); + g2.setColor(Color.red); + + int toMs = translator.getMaxTime() / WaveReport.mult; + for (int ms = fromMs; ms <= toMs; ms++) { + int tick = ms * WaveReport.mult; + int x = translator.timeToScreen(tick, d.width, zoomProvider); + g2.drawLine(x, 0, x, d.height); + } + } + + private void paintUpDown(Dimension d, WaveReport.UpDown upDown, Graphics g) { + + int x1 = translator.timeToScreen(upDown.upTime, d.width, zoomProvider); + int x2 = translator.timeToScreen(upDown.downTime, d.width, zoomProvider); + + int y = (int) (0.2 * d.height); + +// g.setColor(Color.cyan); +// g.fillRect(x1, y, x2 - x1, d.height); + + g.setColor(Color.lightGray); + g.fillRect(x1, y, x2 - x1, d.height - y); + + + g.setColor(Color.blue); + g.drawLine(x1, y, x2, y); + g.drawLine(x1, y, x1, d.height); + g.drawLine(x2, y, x2, d.height); + + g.setColor(Color.red); + String durationString = String.format(" %.2fms", upDown.getDuration() / WaveReport.SYS_TICKS_PER_MS); + + g.drawString(durationString, x1, (int) (0.5 * d.height)); + + g.setColor(Color.darkGray); + if (upDown.upIndex != -1) + g.drawString("" + upDown.upIndex, x1, (int) (0.25 * d.height)); + if (upDown.downIndex != -1) + g.drawString("" + upDown.downIndex, x2, (int) (0.25 * d.height)); + + int offset = 3; + g.setColor(Color.black); + String fromAngle = time2rpm.getCrankAngleByTimeString(upDown.upTime); + g.drawString(fromAngle, x1 + offset, (int) (0.75 * d.height)); + + g.setColor(Color.green); + String toAngle = time2rpm.getCrankAngleByTimeString(upDown.downTime); + g.drawString(toAngle, x1 + offset, (int) (1.0 * d.height)); + } + + public void setRevolutions(StringBuilder revolutions) { + time2rpm = RevolutionLog.parseRevolutions(revolutions); + } +} diff --git a/java_console/ui/src/com/irnems/ui/widgets/WaveInfoPanel.java b/java_console/ui/src/com/irnems/ui/widgets/WaveInfoPanel.java new file mode 100644 index 0000000000..9e724df135 --- /dev/null +++ b/java_console/ui/src/com/irnems/ui/widgets/WaveInfoPanel.java @@ -0,0 +1,12 @@ +package com.irnems.ui.widgets; + +/** + * Date: 1/14/13 + * (c) Andrey Belomutskiy + */ +public class WaveInfoPanel extends BooleanFlagControlPanel { + public WaveInfoPanel(final int index) { + super("wave" + index, "active on low"); + installCommand("wm " + index + " "); + } +} diff --git a/java_console/ui/src/com/rusefi/AnalogChartPanel.java b/java_console/ui/src/com/rusefi/AnalogChartPanel.java new file mode 100644 index 0000000000..377a902f34 --- /dev/null +++ b/java_console/ui/src/com/rusefi/AnalogChartPanel.java @@ -0,0 +1,131 @@ +package com.rusefi; + +import com.irnems.FileLog; +import com.irnems.core.MessagesCentral; +import com.irnems.ui.RpmModel; +import com.irnems.ui.UiUtils; +import com.irnems.ui.widgets.UpDownImage; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.*; +import java.util.List; + +/** + * Date: 12/21/13 + * Andrey Belomutskiy (c) 2012-2013 + */ +public class AnalogChartPanel extends JPanel { + private static final String KEY = "analog_chart"; + + private final TreeMap values = new TreeMap(); + private final AnalogChart analogChart = new AnalogChart(); + + private double minX; + private double maxX; + private double minY; + private double maxY; + + private boolean paused = false; + + public AnalogChartPanel() { + super(new BorderLayout()); + + MessagesCentral.getInstance().addListener(new MessagesCentral.MessageListener() { + @Override + public void onMessage(Class clazz, String message) { + if (paused || !message.startsWith(KEY)) + return; + unpackValues(values, message); + +// MessagesCentral.getInstance().postMessage(AnalogChartPanel.class, "chart arrived, len=" + message.length()); + + processValues(); + UpDownImage.trueRepaint(analogChart); + + } + }); + + JPanel upperPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 5, 0)); + + JButton imageButton = new JButton("save image"); + upperPanel.add(imageButton); + imageButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + int rpm = RpmModel.getInstance().getValue(); + String fileName = FileLog.getDate() + "rpm_" + rpm + "_analog" + ".png"; + UiUtils.saveImage(fileName, analogChart); + } + }); + + final JButton pauseButton = new JButton("Pause"); + upperPanel.add(pauseButton); + pauseButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + paused = !paused; + pauseButton.setText(paused ? "Resume" : "Pause"); + } + }); + + add(upperPanel, BorderLayout.NORTH); + add(analogChart, BorderLayout.CENTER); + } + + private void processValues() { + List keys = new ArrayList(values.keySet()); + minX = keys.get(0); + maxX = keys.get(keys.size() - 1); + FileLog.rlog("Analog chart from " + minX + " to " + maxX); + + TreeSet sortedValues = new TreeSet(); + sortedValues.addAll(values.values()); + List values = new ArrayList(sortedValues); + + minY = values.get(0); + maxY = values.get(values.size() - 1); + } + + private class AnalogChart extends JComponent { + @Override + public void paint(Graphics g) { + super.paint(g); + //Graphics2D g2 = (Graphics2D) g; + + Dimension size = getSize(); + + g.drawString("X range from " + minX + " to " + maxX, 4, 20); + g.drawString("Y range from " + minY + " to " + maxY, 4, 40); + + int prevX = 0; + int prevY = size.height; + + double bX = size.width / (maxX - minX); + double bY = size.height / (maxY - minY); + + for (Map.Entry e : values.entrySet()) { + int x = (int) ((e.getKey() - minX) * bX); + int y = size.height - (int) ((e.getValue() - minY) * bY); + + g.drawLine(prevX, prevY, x, y); + prevX = x; + prevY = y; + } + } + } + + private void unpackValues(TreeMap values, String chart) { + values.clear(); + + String[] tokens = chart.split("\\|"); + for (int i = 1; i < tokens.length - 1; ) { + String key = tokens[i++]; + String value = tokens[i++]; + + values.put(Double.parseDouble(key), Double.parseDouble(value)); + } + } +} \ No newline at end of file diff --git a/java_console/ui/src/com/rusefi/PortLookupFrame.java b/java_console/ui/src/com/rusefi/PortLookupFrame.java new file mode 100644 index 0000000000..e239d9ddb8 --- /dev/null +++ b/java_console/ui/src/com/rusefi/PortLookupFrame.java @@ -0,0 +1,54 @@ +package com.rusefi; + +import com.irnems.Launcher; +import com.rusefi.io.tcp.TcpConnector; +import jssc.SerialPortList; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; +import java.util.Arrays; + +/** + * This frame is used on startup to select the port we would be using + * + * @author Andrey Belomutskiy + * 2/14/14 + */ +public class PortLookupFrame { + public static void chooseSerialPort() { + java.util.List ports = new ArrayList(); + ports.addAll(Arrays.asList(SerialPortList.getPortNames())); + ports.addAll(TcpConnector.getAvailablePorts()); + + + if (ports.size() == 0) { + JOptionPane.showMessageDialog(null, "No suitable ports found"); + System.exit(-1); + } + + final JFrame frame = new JFrame("Serial port selection"); + + JPanel panel = new JPanel(new FlowLayout()); + + for (final String port : ports) { + JButton button = new JButton("Use " + port); + + button.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + frame.dispose(); + new Launcher(port); + } + }); + + panel.add(button); + } + + frame.add(panel); + frame.pack(); + frame.setVisible(true); + } +} diff --git a/java_console/ui/ui.iml b/java_console/ui/ui.iml new file mode 100644 index 0000000000..756ce4e759 --- /dev/null +++ b/java_console/ui/ui.iml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/unit_tests/.cproject b/unit_tests/.cproject new file mode 100644 index 0000000000..71e7c9e65e --- /dev/null +++ b/unit_tests/.cproject @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/unit_tests/.project b/unit_tests/.project new file mode 100644 index 0000000000..3238f268dc --- /dev/null +++ b/unit_tests/.project @@ -0,0 +1,27 @@ + + + unit_tests + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.core.ccnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/unit_tests/Makefile b/unit_tests/Makefile new file mode 100644 index 0000000000..1eb54405fd --- /dev/null +++ b/unit_tests/Makefile @@ -0,0 +1,221 @@ +############################################################################## +# Build global options +# NOTE: Can be overridden externally. +# + +PROJECT_DIR = ../firmware +#CHIBIOS = $(PROJECT_DIR)/chibios + +# Compiler options here. +ifeq ($(USE_OPT),) +# -O2 is needed for mingw, without it there is a linking issue to isnanf?!?! + #USE_OPT = $(RFLAGS) -O2 -fgnu89-inline -ggdb -fomit-frame-pointer -falign-functions=16 -std=gnu99 -Werror-implicit-function-declaration -Werror -Wno-error=pointer-sign -Wno-error=unused-function -Wno-error=unused-variable -Wno-error=sign-compare -Wno-error=unused-parameter -Wno-error=missing-field-initializers + USE_OPT = -c -Wall -O2 +endif + +# C specific options here (added to USE_OPT). +ifeq ($(USE_COPT),) + USE_COPT = -std=gnu99 -fgnu89-inline +endif + +# C++ specific options here (added to USE_OPT). +ifeq ($(USE_CPPOPT),) + USE_CPPOPT = -fno-rtti -fpermissive -fno-exceptions -fno-use-cxa-atexit +endif + +# Enable this if you want the linker to remove unused code and data +ifeq ($(USE_LINK_GC),) + USE_LINK_GC = yes +endif + +# If enabled, this option allows to compile the application in THUMB mode. +ifeq ($(USE_THUMB),) + USE_THUMB = no +endif + +# Enable this if you want to see the full log while compiling. +ifeq ($(USE_VERBOSE_COMPILE),) + USE_VERBOSE_COMPILE = no +endif + +# +# Build global options +############################################################################## + +############################################################################## +# Architecture or project specific options +# + + +# List all default C defines here, like -D_DEBUG=1 +DDEFS = + +# +# Architecture or project specific options +############################################################################## + +############################################################################## +# Project, sources and paths +# + +# Define project name here +PROJECT = rusefi_test + +#PROJECT_BOARD = OLIMEX_STM32_E407 +#ifneq ($(PROJECT_BOARD),OLIMEX_STM32_E407) +# PROJECT_BOARD = ST_STM32F4_DISCOVERY +#endif +#DDEFS += -D$(PROJECT_BOARD) + +# Imported source files and paths +include $(PROJECT_DIR)/util/util.mk +include $(PROJECT_DIR)/config/engines/engines.mk +include $(PROJECT_DIR)/controllers/algo/algo.mk +include $(PROJECT_DIR)/controllers/math/math.mk +include $(PROJECT_DIR)/controllers/sensors/sensors.mk +include test.mk + +# Define linker script file here +#LDSCRIPT= config/system/STM32F407xG.ld +#LDSCRIPT= $(PORTLD)/STM32F407xG_CCM.ld + +# C sources that can be compiled in ARM or THUMB mode depending on the global +# setting. +CSRC = $(UTILSRC) \ + $(CONTROLLERS_ALGO_SRC) \ + $(CONTROLLERS_MATH_SRC) \ + $(CONTROLLERS_SENSORS_SRC) \ + $(ENGINES_SRC) \ + $(TEST_SRC_C) + +# C++ sources that can be compiled in ARM or THUMB mode depending on the global +# setting. +CPPSRC = $(UTILSRC_CPP) \ + $(CONTROLLERS_ALGO_SRC_CPP) \ + $(ENGINES_SRC_CPP) \ + $(TEST_SRC_CPP) \ + main.cpp + +# C sources to be compiled in ARM mode regardless of the global setting. +# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler +# option that results in lower performance and larger code size. +ACSRC = + +# C++ sources to be compiled in ARM mode regardless of the global setting. +# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler +# option that results in lower performance and larger code size. +ACPPSRC = + +# C sources to be compiled in THUMB mode regardless of the global setting. +# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler +# option that results in lower performance and larger code size. +TCSRC = + +# C sources to be compiled in THUMB mode regardless of the global setting. +# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler +# option that results in lower performance and larger code size. +TCPPSRC = + +# List ASM source files here +ASMSRC = $(PORTASM) + +INCDIR = . \ + $(PROJECT_DIR)/util \ + $(PROJECT_DIR)/config/engines \ + $(PROJECT_DIR)/controllers/sensors \ + $(PROJECT_DIR)/controllers/algo \ + $(PROJECT_DIR)/controllers/math \ + $(PROJECT_DIR)/ext_algo \ + test_data_structures \ + test_basic_math + + +# +# Project, sources and paths +############################################################################## + +############################################################################## +# Compiler settings +# + +#MCU = cortex-m4 + +ifeq ($(OS),Windows_NT) + TRGT = i686-pc-mingw32- +else + TRGT = +endif + +CC = $(TRGT)gcc +CPPC = $(TRGT)g++ +# Enable loading with g++ only if you need C++ runtime support. +# NOTE: You can use C++ even without C++ support if you are careful. C++ +# runtime support makes code size explode. +#LD = $(TRGT)gcc +LD = $(TRGT)g++ +CP = $(TRGT)objcopy +AS = $(TRGT)gcc -x assembler-with-cpp +OD = $(TRGT)objdump +HEX = $(CP) -O ihex +BIN = $(CP) -O binary + +# ARM-specific options here +AOPT = + +# THUMB-specific options here +TOPT = -mthumb -DTHUMB + +# Define C warning options here +CWARN = -Wall -Wextra -Wstrict-prototypes + +# Define C++ warning options here +CPPWARN = -Wall -Wextra + +# +# Compiler settings +############################################################################## + +############################################################################## +# Start of default section +# + +# List all default ASM defines here, like -D_DEBUG=1 +DADEFS = + +# List all default directories to look for include files here +DINCDIR = + +# List the default directory to look for the libraries here +DLIBDIR = + +# List all default libraries here +DLIBS = -static-libgcc -static-libstdc++ + +# +# End of default section +############################################################################## + +############################################################################## +# Start of user section +# + +# List all user C define here, like -D_DEBUG=1 +UDEFS = + +# Define ASM defines here +UADEFS = + +# List all user directories here +UINCDIR = + +# List the user directory to look for the libraries here +ULIBDIR = + +# List all user libraries here +ULIBS = -lm + +# +# End of user defines +############################################################################## + +include rules.mk diff --git a/unit_tests/adc_inputs.h b/unit_tests/adc_inputs.h new file mode 100644 index 0000000000..5c526173a7 --- /dev/null +++ b/unit_tests/adc_inputs.h @@ -0,0 +1,11 @@ +/** + * @file adc_inputs.h + * + * @date Dec 7, 2013 + * @author Andrey Belomutskiy, (c) 2012-2013 + */ + +#ifndef ADC_INPUTS_H_ +#define ADC_INPUTS_H_ + +#endif /* ADC_INPUTS_H_ */ diff --git a/unit_tests/boards.c b/unit_tests/boards.c new file mode 100644 index 0000000000..5755c03f80 --- /dev/null +++ b/unit_tests/boards.c @@ -0,0 +1,23 @@ +/** + * @file board.c + * + * @date Nov 15, 2013 + * @author Andrey Belomutskiy, (c) 2012-2013 + */ + +#include "boards.h" + +float getVoltageDivided(int channel) { + return 0; +} + +float getVoltage(int channel) { + return 0; +} + + +int getAdcValue(int channel) { + return 0; +} + + diff --git a/unit_tests/boards.h b/unit_tests/boards.h new file mode 100644 index 0000000000..bbcf9f6256 --- /dev/null +++ b/unit_tests/boards.h @@ -0,0 +1,24 @@ +/* + * boards.h + * + * Created on: Nov 15, 2013 + * Author: Andrey Belomutskiy, (c) 2012-2013 + */ + +#ifndef BOARDS_H_ +#define BOARDS_H_ + +#define ADC_LOGIC_TPS 0 +#define ADC_LOGIC_AFR 0 +#define ADC_LOGIC_MAF 0 +#define ADC_LOGIC_MAP 0 +#define ADC_CHANNEL_VREF 0 +#define ADC_CHANNEL_VBATT 0 +#define ADC_LOGIC_INTAKE_AIR 0 +#define ADC_LOGIC_COOLANT 0 + +float getVoltageDivided(int); +float getVoltage(int channel); +int getAdcValue(int channel); + +#endif /* BOARDS_H_ */ diff --git a/unit_tests/compile.bat b/unit_tests/compile.bat new file mode 100644 index 0000000000..1c9d2c2e35 --- /dev/null +++ b/unit_tests/compile.bat @@ -0,0 +1,3 @@ +rm -rf .dep/ +rm -rf build/ +make diff --git a/unit_tests/efifeatures.h b/unit_tests/efifeatures.h new file mode 100644 index 0000000000..8845e51ac7 --- /dev/null +++ b/unit_tests/efifeatures.h @@ -0,0 +1,13 @@ +/* + * efifeatures.h + * + * Created on: Mar 7, 2014 + * Author: Andrey + */ + +#ifndef EFIFEATURES_H_ +#define EFIFEATURES_H_ + +#define EFI_CLI_SUPPORT FALSE + +#endif /* EFIFEATURES_H_ */ diff --git a/unit_tests/global.h b/unit_tests/global.h new file mode 100644 index 0000000000..fd7f9e766f --- /dev/null +++ b/unit_tests/global.h @@ -0,0 +1,23 @@ +/* + * @file global.h + * + * @date Nov 28, 2013 + * @author pc + */ + +#ifndef GLOBAL_H_ +#define GLOBAL_H_ + +#include +#include + +#define EFI_SUPPORT_FORD_ASPIRE TRUE +#define EFI_SUPPORT_DODGE_NEON TRUE +#define EFI_SUPPORT_1995_FORD_INLINE_6 TRUE +#define EFI_SUPPORT_FORD_FIESTA TRUE +#define EFI_SUPPORT_NISSAN_PRIMERA TRUE + +#define TRUE 1 +#define FALSE 0 + +#endif /* GLOBAL_H_ */ diff --git a/unit_tests/jenkins.sh b/unit_tests/jenkins.sh new file mode 100644 index 0000000000..67df86f044 --- /dev/null +++ b/unit_tests/jenkins.sh @@ -0,0 +1,40 @@ +#!/bin/bash + echo "The PATH is ${PATH}" + cd "${WORKSPACE}" + echo "start in workspace ${PWD}" + + cd firmware + echo "CD to ${PWD}" + + rm -fR .dep + rm -fR build + make + + if [ ! -f build/rusefi.hex ]; then + echo "Firmware compilation failed" + exit -1 + fi + + cd "${WORKSPACE}/win32_algo_tests" + echo "CD to ${PWD}" + + rm -fR .dep + rm -fR build + make + if [ ! -f build/rusefi_test ]; then + echo "test compilation failed" + exit -1 + fi + +# we want to terminate if test fails +set -e + + # invoke the tests - hopefully error code would be propagated? + build/rusefi_test + +cd "${WORKSPACE}/java_console" +echo "CD to ${PWD}" + +#JAVA_HOME=/usr/lib/jvm/java-6-openjdk-amd64 +#ant + diff --git a/unit_tests/junction.exe b/unit_tests/junction.exe new file mode 100644 index 0000000000..77054219b3 Binary files /dev/null and b/unit_tests/junction.exe differ diff --git a/unit_tests/main.cpp b/unit_tests/main.cpp new file mode 100644 index 0000000000..3335c4cd69 --- /dev/null +++ b/unit_tests/main.cpp @@ -0,0 +1,145 @@ +/* + ============================================================================ + Name : main.c + Author : Andrey Belomutskiy + Copyright : (c) 2012-2013 + Description : First step towards unit-testing rusEfi algorithms on win32 + ============================================================================ + */ + +#include +#include +#include +#include + +#include "main.h" + +extern "C" +{ + +#include "map_resize.h" +#include "test_idle_controller.h" +#include "test_interpolation_3d.h" +#include "test_find_index.h" +#include "test_fuel_map.h" +#include "test_engine_math.h" +#include "test_event_registry.h" +#include "test_sensors.h" +#include "test_signal_executor.h" +#include "test_util.h" +#include "engine_configuration.h" +#include "test_trigger_decoder.h" + +} + + +static engine_configuration_s ec; +engine_configuration_s *engineConfiguration = &ec; + +static float absF(float value) { + return value > 0 ? value : -value; +} + +void assertEqualsM(char *msg, float expected, float actual) { + if (isnan(actual) && !isnan(expected)) { + printf("Unexpected: %s %.4f while expected %.4f\r\n", msg, actual, expected); + exit(-1); + } + + float delta = absF(actual - expected); + if (delta > 0.0001) { + printf("delta: %.7f\r\n", delta); + printf("Unexpected: %s %.4f while expected %.4f\r\n", msg, actual, expected); + exit(-1); + } + printf("Validated %s: %f\r\n", msg, expected); +} + +void assertEquals(float expected, float actual) { + assertEqualsM("", expected, actual); +} + +void assertTrueM(char *msg, float actual) { + assertEqualsM(msg, TRUE, actual); +} + +void assertTrue(float actual) { + assertTrueM("", actual); +} + +void assertFalseM(char *msg, float actual) { + assertEqualsM(msg, FALSE, actual); +} + +void assertFalse(float actual) { + assertFalseM("", actual); +} + +void chDbgAssert(int c, char *msg, void *arg) { + if (!c) { + printf("assert failed: %s\r\n", msg); + exit(-1); + } +} + +static engine_configuration2_s ec2; +engine_configuration2_s *engineConfiguration2 = &ec2; + +int main(void) { + testInterpolate3d(); + testFindIndex(); + testInterpolate2d(); + testGpsParser(); + testFuelMap(); + testEngineMath(); + testEventRegistry(); + testSensors(); + testCyclicBuffer(); + + testSignalExecutor(); + + testHistogram(); + + testTriggerDecoder(); + + testMalfunctionCentral(); + + testConsoleLogic(); + + testAngleResolver(); + + testPinHelper(); + testSetTableValue(); + + printf("Success 20130319\r\n"); + +// resizeMap(); + + return EXIT_SUCCESS; +} + +void warning(char *msg, float value) { + printf("Warning: %s %f\r\n", msg, value); +} + +void firmwareError(const char *fmt, ...) { + printf(fmt); + exit(-1); +} + +void print(const char *format, ...) { + va_list ap; + va_start(ap, format); + vprintf(format, ap); + va_end(ap); +} + +void fatal3(char *msg, char *file, int line) { + printf(msg); + exit(-1); +} + +int warning(const char *fmt, ...) { + printf(fmt); + exit(-1); +} diff --git a/unit_tests/main.h b/unit_tests/main.h new file mode 100644 index 0000000000..23f1765c86 --- /dev/null +++ b/unit_tests/main.h @@ -0,0 +1,58 @@ +/** + * @file main.h + * @brief Test version of main.h + * + * Created on: Oct 17, 2013 + * Author: Andrey Belomutskiy (C) 2012-2013 + */ + +#ifndef MAIN_H_ +#define MAIN_H_ + +#include +#include +#include "error_handling.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "efilib.h" + +#include "global.h" +#include "boards.h" +#include "engines.h" + +typedef int bool_t; + +void chDbgAssert(int c, char *msg, void *arg); + +void print(const char *fmt, ...); + +#define TICKS_IN_MS 100 + +#define DEBUG_INTERPOLATION 1 + +#define chDbgCheck(x, y) chDbgAssert(x, y, NULL) + +void assertEqualsM(char *msg, float expected, float actual); +void assertEquals(float expected, float actual); +void assertTrue(float actual); +void assertTrueM(char *msg, float actual); +void assertFalse(float actual); +void assertFalseM(char *msg, float actual); + +float getIntakeAirTemperature(void); +float getCoolantTemperature(void); +float getVBatt(void); +float getMaf(void); + +#define systicks2ms(x) (0) + +#ifdef __cplusplus +} +#endif + + +#endif /* MAIN_H_ */ diff --git a/unit_tests/makelinks.bat b/unit_tests/makelinks.bat new file mode 100644 index 0000000000..a700673682 --- /dev/null +++ b/unit_tests/makelinks.bat @@ -0,0 +1,8 @@ + +junction controllers_algo ..\firmware\controllers\algo +junction controllers_math ..\firmware\controllers\math +junction engines ..\firmware\config\engines +junction controllers_sensors ..\firmware\controllers\sensors +junction util ..\firmware\util +junction ext_algo ..\firmware\ext_algo + diff --git a/unit_tests/map_resize.c b/unit_tests/map_resize.c new file mode 100644 index 0000000000..44b7f5a6e7 --- /dev/null +++ b/unit_tests/map_resize.c @@ -0,0 +1,176 @@ +/** + * @file map_resize.c + * + * @date Jan 12, 2014 + * @author Andrey Belomutskiy, (c) 2012-2013 + */ + +#include +#include "fuel_math.h" +#include "interpolation.h" +#include "engines.h" +#include "engine_configuration.h" +#include "main.h" +#include "idle_controller.h" + +#if 1 + +#define AD_LOAD_COUNT 16 +#define AD_RPM_COUNT 16 + + +static float ad_rpm_table[] = {/*0*/ 800.000000, + /*1*/ 1213.333374, + /*2*/ 1626.666748, + /*3*/ 2040.000000, + /*4*/ 2453.333496, + /*5*/ 2866.666748, + /*6*/ 3280.000000, + /*7*/ 3693.333496, + /*8*/ 4106.666992, + /*9*/ 4520.000000, + /*10*/ 4933.333496, + /*11*/ 5346.666992, + /*12*/ 5760.000000, + /*13*/ 6173.333496, + /*14*/ 6586.666992, + /*15*/ 7000.000000, + };; + +static float ad_maf_table[] = +{/*0*/ 1.200000, +/*1*/ 1.413333, +/*2*/ 1.626667, +/*3*/ 1.840000, +/*4*/ 2.053333, +/*5*/ 2.266667, +/*6*/ 2.480000, +/*7*/ 2.693333, +/*8*/ 2.906667, +/*9*/ 3.120000, +/*10*/ 3.333333, +/*11*/ 3.546667, +/*12*/ 3.760000, +/*13*/ 3.973333, +/*14*/ 4.186667, +/*15*/ 4.400000, +}; + +static float ad_table[AD_LOAD_COUNT][AD_RPM_COUNT] = { + /* RPM 800.000000 1213.333374 1626.666748 2040.000000 2453.333496 2866.666748 3280.000000 3693.333496 4106.666992 4520.000000 4933.333496 5346.666992 5760.000000 6173.333496 6586.666992 7000.000000*/ + /* Load 1.200000 */{ 0.662000, -7.730000, -16.722000, -23.139999, -29.398001, -31.268000, -32.108002, -30.436001, -30.896000, -26.656000, -24.704000, -25.108000, -25.132000, -25.459999, -25.459999, -25.459999}, + /* Load 1.413333 */{ 0.546000, -7.662000, -16.882000, -23.482000, -29.520000, -31.323999, -32.108002, -30.656000, -30.468000, -26.879999, -24.746000, -24.742001, -29.032000, -25.562000, -25.562000, -25.562000}, + /* Load 1.626667 */{ 0.584000, -7.870000, -16.714001, -23.025999, -29.542000, -31.166000, -32.175999, -30.540001, -30.268000, -26.416000, -24.134001, -25.007999, -24.698000, -26.167999, -26.167999, -26.167999}, + /* Load 1.840000 */{ 0.584000, -7.658000, -16.714001, -23.254000, -29.351999, -30.978001, -32.141998, -30.874001, -30.896000, -26.507999, -24.558001, -24.389999, -25.761999, -35.492001, -35.492001, -35.492001}, + /* Load 2.053333 */{ 0.584000, -7.862000, -16.538000, -23.254000, -29.232000, -31.296000, -32.520000, -30.142000, -30.388000, -25.903999, -24.370001, -24.082001, -24.792000, -24.351999, -24.351999, -24.351999}, + /* Load 2.266667 */{ -1.364000, -7.726000, -16.806000, -23.254000, -29.639999, -31.006001, -32.298000, -30.912001, -29.882000, -26.392000, -24.664000, -27.233999, -25.374001, -25.417999, -25.417999, -25.417999}, + /* Load 2.480000 */{ 1.364000, -10.490000, -16.705999, -22.441999, -28.101999, -30.238001, -32.363998, -30.719999, -30.896000, -26.608000, -24.664000, -24.431999, -24.500000, -25.510000, -25.510000, -25.510000}, + /* Load 2.693333 */{ 9.864000, -10.416000, -11.680000, -19.150000, -25.754000, -27.936001, -32.554001, -30.656000, -30.153999, -27.184000, -25.252001, -22.812000, -24.452000, -25.219999, -25.219999, -25.219999}, + /* Load 2.906667 */{ 9.866000, 5.452000, 2.854000, -17.212000, -17.552000, -20.688000, -25.660000, -27.809999, -27.691999, -27.224001, -25.882000, -25.360001, -26.100000, -27.992001, -27.992001, -27.992001}, + /* Load 3.120000 */{ 9.864000, 5.452000, 2.854000, -0.342000, -12.526000, -16.218000, -21.364000, -27.590000, -25.780001, -24.170000, -24.664000, -25.584000, -26.490000, -31.968000, -31.968000, -31.968000}, + /* Load 3.333333 */{ 9.864000, 5.516000, 2.854000, -0.226000, -2.738000, -3.816000, -11.924000, -18.808001, -21.038000, -21.538000, -21.209999, -22.228001, -25.046000, -25.156000, -25.156000, -25.156000}, + /* Load 3.546667 */{ 9.866000, 5.518000, 2.854000, 0.000000, -3.022000, -3.816000, -6.428000, -7.788000, -19.426001, -20.860001, -19.966000, -21.030001, -21.396000, -21.570000, -21.570000, -21.570000}, + /* Load 3.760000 */{ 9.864000, 5.516000, 2.772000, -0.226000, -2.732000, -3.500000, -6.798000, -8.102000, -8.660000, -9.500000, -11.788000, -20.132000, -20.072001, -20.510000, -20.510000, -20.510000}, + /* Load 3.973333 */{ 9.864000, 5.518000, 2.854000, 0.000000, -2.880000, -3.816000, -6.420000, -8.320000, -8.426000, -8.532000, -11.470000, -11.442000, -13.610000, -12.022000, -12.022000, -12.022000}, + /* Load 4.186667 */{ 9.750000, 5.518000, 2.604000, 0.000000, -2.880000, -3.654000, -6.050000, -6.888000, -8.372000, -9.364000, -11.764000, -11.732000, -11.864000, -12.376000, -12.376000, -12.376000}, + /* Load 4.400000 */{ 0.350000, 5.590000, 0.502000, 0.910000, 0.864000, 0.954000, 1.324000, -7.436000, 1.170000, 1.054000, 2.058000, 2.098000, 2.636000, -12.352000, -12.352000, -12.352000} + }; + +//float getBaseAdvance(int rpm, float key) { +// // todo: use interpolation +// int rpm_index = findIndex(ad_rpm_table, AD_RPM_COUNT, rpm); +// rpm_index = max(rpm_index, 0); +// int maf_index = findIndex(ad_maf_table, AD_LOAD_COUNT, key); +// maf_index = max(maf_index, 0); +// +// return ad_table[rpm_index][maf_index]; +//} + + + +#define newRpmSize 16 +#define newKeySize 16 + +static float newRpmBin[newRpmSize]; +static float newKeyBin[newKeySize]; + +//static float *fuel_ptrs[FUEL_LOAD_COUNT]; + +//EngineConfiguration *engineConfiguration; + +extern int needInterpolationLogging; + +void resizeMap(void) { +// float keyMin = 1.2; +// float keyMax = 4.4; +// +// float rpmMin = 800; +// float rpmMax = 7000; + +// for (int k = 0; k < FUEL_LOAD_COUNT; k++) +// fuel_ptrs[k] = engineConfiguration->fuelTable[k]; + +// for (int i = 0; i < FUEL_MAF_COUNT; i++) +// engineConfiguration->fuelKeyBins[i] = default_fuel_maf_bins[i]; +// for (int i = 0; i < FUEL_RPM_COUNT; i++) +// engineConfiguration->fuelRpmBins[i] = default_fuel_rpm_bins[i]; +// for (int k = 0; k < FUEL_MAF_COUNT; k++) { +// for (int r = 0; r < FUEL_RPM_COUNT; r++) { +// // todo: this is BAD, this needs to be fixed - TS table indexes are different from default indexes +// engineConfiguration->fuelTable[k][r] = default_fuel_table[r][k]; +// } +// } + +// assertEquals(15, interpolate3d(1.2, engineConfiguration->fuelKeyBins, FUEL_MAF_COUNT, 8000, +// engineConfiguration->fuelRpmBins, +// FUEL_RPM_COUNT, fuel_ptrs)); + + needInterpolationLogging = 0; + +// printf("static float ad_maf_table[AD_LOAD_COUNT] = {"); +// for (int i = 0; i < newKeySize; i++) { +// newKeyBin[i] = interpolate(0, keyMin, newKeySize - 1, keyMax, i); +// printf("/*%d*/ %f,\r\n", i, newKeyBin[i]); +// } +// printf("};\r\n"); +// +// printf("static float ad_rpm_table[AD_RPM_COUNT] = {"); +// for (int i = 0; i < newRpmSize; i++) { +// newRpmBin[i] = interpolate(0, rpmMin, newRpmSize - 1, rpmMax, i); +// printf("/*%d*/ %f,\r\n", i, newRpmBin[i]); +// } +// printf("};\r\n"); + + printf("static float ad_table[AD_RPM_COUNT][AD_LOAD_COUNT] = {\r\n"); + + printf("/* RPM\t\t"); + for (int r = 0; r < newRpmSize; r++) { + float rpm = newRpmBin[r]; + printf("\t%f", rpm); + } + printf("*/\r\n"); + + for (int k = 0; k < newKeySize; k++) { + float load = newKeyBin[k]; + printf("/* Load %f */{", load); + + for (int r = 0; r < newRpmSize; r++) { + float rpm = newRpmBin[r]; + + float v = ad_table[k][r]; + + printf("\t%f", v); + if (r != newRpmSize - 1) + printf(","); + + } + printf("}"); + if (k != newKeySize - 1) + printf(","); + printf("\r\n"); + } + printf("};\r\n"); + +} + +#endif diff --git a/unit_tests/map_resize.h b/unit_tests/map_resize.h new file mode 100644 index 0000000000..3d105a434e --- /dev/null +++ b/unit_tests/map_resize.h @@ -0,0 +1,13 @@ +/** + * @file map_resize.h + * + * @date Jan 12, 2014 + * @author Andrey Belomutskiy, (c) 2012-2013 + */ + +#ifndef MAP_RESIZE_H_ +#define MAP_RESIZE_H_ + +void resizeMap(void); + +#endif /* MAP_RESIZE_H_ */ diff --git a/unit_tests/readme.txt b/unit_tests/readme.txt new file mode 100644 index 0000000000..621959dacd --- /dev/null +++ b/unit_tests/readme.txt @@ -0,0 +1,6 @@ +In this folder we have a naive test set. I am not sure what is the best approach to tests in C (someone can educate me), but my approach is: + +1) with a symlink linking some (only some, not all) folders of the firmware implementation, we can compile some files of the firmware as win32 code. +Please execute the 'makelinks.bat' file to get the links. On XP you might need to execute 'junction.exe' before exuting the .bat file. + +2) we then compile & run the .exe, which is expected to say SUCCESS and not fail :) \ No newline at end of file diff --git a/unit_tests/rules.mk b/unit_tests/rules.mk new file mode 100644 index 0000000000..5e93b3e311 --- /dev/null +++ b/unit_tests/rules.mk @@ -0,0 +1,168 @@ +# ARM Cortex-Mx common makefile scripts and rules. + +# Output directory and files +ifeq ($(BUILDDIR),) + BUILDDIR = build +endif +ifeq ($(BUILDDIR),.) + BUILDDIR = build +endif +OUTFILES = $(BUILDDIR)/$(PROJECT) + +# Automatic compiler options +OPT = $(USE_OPT) +COPT = $(USE_COPT) +CPPOPT = $(USE_CPPOPT) +ifeq ($(USE_LINK_GC),yes) + OPT += -ffunction-sections -fdata-sections -fno-common +endif + +# Source files groups and paths +ifeq ($(USE_THUMB),yes) + TCSRC += $(CSRC) + TCPPSRC += $(CPPSRC) +else + ACSRC += $(CSRC) + ACPPSRC += $(CPPSRC) +endif +ASRC = $(ACSRC)$(ACPPSRC) +TSRC = $(TCSRC)$(TCPPSRC) +SRCPATHS = $(sort $(dir $(ASMXSRC)) $(dir $(ASMSRC)) $(dir $(ASRC)) $(dir $(TSRC))) + +# Various directories +OBJDIR = $(BUILDDIR)/obj +LSTDIR = $(BUILDDIR)/lst + +# Object files groups +ACOBJS = $(addprefix $(OBJDIR)/, $(notdir $(ACSRC:.c=.o))) +ACPPOBJS = $(addprefix $(OBJDIR)/, $(notdir $(ACPPSRC:.cpp=.o))) +TCOBJS = $(addprefix $(OBJDIR)/, $(notdir $(TCSRC:.c=.o))) +TCPPOBJS = $(addprefix $(OBJDIR)/, $(notdir $(TCPPSRC:.cpp=.o))) +ASMOBJS = $(addprefix $(OBJDIR)/, $(notdir $(ASMSRC:.s=.o))) +ASMXOBJS = $(addprefix $(OBJDIR)/, $(notdir $(ASMXSRC:.S=.o))) +OBJS = $(ASMXOBJS) $(ASMOBJS) $(ACOBJS) $(TCOBJS) $(ACPPOBJS) $(TCPPOBJS) + +# Paths +IINCDIR = $(patsubst %,-I%,$(INCDIR) $(DINCDIR) $(UINCDIR)) +LLIBDIR = $(patsubst %,-L%,$(DLIBDIR) $(ULIBDIR)) + +# Macros +DEFS = $(DDEFS) $(UDEFS) +ADEFS = $(DADEFS) $(UADEFS) + +# Libs +LIBS = $(DLIBS) $(ULIBS) + +# Various settings +#MCFLAGS = -mcpu=$(MCU) +ODFLAGS = -x --syms +ASFLAGS = $(MCFLAGS) -Wa,-amhls=$(LSTDIR)/$(notdir $(<:.s=.lst)) $(ADEFS) +ASXFLAGS = $(MCFLAGS) -Wa,-amhls=$(LSTDIR)/$(notdir $(<:.S=.lst)) $(ADEFS) +CFLAGS = $(MCFLAGS) $(OPT) $(COPT) $(CWARN) -Wa,-alms=$(LSTDIR)/$(notdir $(<:.c=.lst)) $(DEFS) +CPPFLAGS = $(MCFLAGS) $(OPT) $(CPPOPT) $(CPPWARN) -Wa,-alms=$(LSTDIR)/$(notdir $(<:.cpp=.lst)) $(DEFS) +ifeq ($(USE_LINK_GC),yes) + LDFLAGS = $(MCFLAGS) -Wl,-Map=$(BUILDDIR)/$(PROJECT).map,--cref,--no-warn-mismatch,--gc-sections $(LLIBDIR) +else + LDFLAGS = $(MCFLAGS) -Wl,-Map=$(BUILDDIR)/$(PROJECT).map,--cref,--no-warn-mismatch $(LLIBDIR) +endif + +# Generate dependency information +CFLAGS += -MD -MP -MF .dep/$(@F).d +CPPFLAGS += -MD -MP -MF .dep/$(@F).d + +# Paths where to search for sources +VPATH = $(SRCPATHS) + +# +# Makefile rules +# + +all: $(OBJS) $(OUTFILES) MAKE_ALL_RULE_HOOK + +MAKE_ALL_RULE_HOOK: + +$(OBJS): | $(BUILDDIR) + +$(BUILDDIR) $(OBJDIR) $(LSTDIR): +ifneq ($(USE_VERBOSE_COMPILE),yes) + @echo Compiler Options + @echo $(CPPC) -c $(CPPFLAGS) -I. $(IINCDIR) main.cpp -o main.o + @echo +endif + mkdir -p $(OBJDIR) + mkdir -p $(LSTDIR) + +$(ACPPOBJS) : $(OBJDIR)/%.o : %.cpp Makefile +ifeq ($(USE_VERBOSE_COMPILE),yes) + @echo + $(CPPC) -c $(CPPFLAGS) $(AOPT) -I. $(IINCDIR) $< -o $@ +else + @echo Compiling $(/dev/null) $(wildcard .dep/*) + +# *** EOF *** diff --git a/unit_tests/settings.h b/unit_tests/settings.h new file mode 100644 index 0000000000..31327e150e --- /dev/null +++ b/unit_tests/settings.h @@ -0,0 +1,13 @@ +/** + * @file settings.h + * + * @date Feb 13, 2014 + * @author Andrey Belomutskiy, (c) 2012-2014 + */ + +#ifndef SETTINGS_H_ +#define SETTINGS_H_ + + + +#endif /* SETTINGS_H_ */ diff --git a/unit_tests/test.mk b/unit_tests/test.mk new file mode 100644 index 0000000000..8929ffc1f6 --- /dev/null +++ b/unit_tests/test.mk @@ -0,0 +1,14 @@ +TEST_SRC_C = boards.c \ + test_data_structures/test_engine_math.c \ + test_event_registry.c \ + test_basic_math/test_find_index.c \ + test_basic_math/test_interpolation_3d.c \ + test_fuel_map.c \ + test_idle_controller.c \ + test_trigger_decoder.c \ + test_sensors.c \ + test_signal_executor.c + +TEST_SRC_CPP = test_util.cpp + + diff --git a/unit_tests/test_basic_math/test_find_index.c b/unit_tests/test_basic_math/test_find_index.c new file mode 100644 index 0000000000..47d34d851a --- /dev/null +++ b/unit_tests/test_basic_math/test_find_index.c @@ -0,0 +1,120 @@ +/* + * test_find_index.c + * + * Created on: Oct 30, 2013 + * Author: Andrey Belomutskiy, (c) 2012-2013 + */ + +#include "engine_math.h" +#include "main.h" +#include "interpolation.h" +#include +#include "engine_configuration.h" + +void testFindIndex(void) { + printf("*************************************************** testFindIndex\r\n"); + + float array[] = { 1, 2, 3, 4, 5 }; + int size = 4; + int result; + + printf("To the left\r\n"); + result = findIndex(array, size, -1.0); + assertEquals(-1, result); + + printf("To the right4\r\n"); + result = findIndex(array, size, 10.0); + assertEquals(3, result); + + printf("To the right5\r\n"); + result = findIndex(array, 5, 10.0); + assertEquals(4, result); + + printf("On the edge\r\n"); + result = findIndex(array, size, 4.0); + assertEquals(3, result); + + printf("Another1\r\n"); + result = findIndex(array, size, 3.9); + assertEquals(2, result); + + printf("Another2\r\n"); + result = findIndex(array, size, 4.1); + assertEquals(3, result); + + printf("Another3\r\n"); + result = findIndex(array, size, 2); + assertEquals(1, result); + + printf("Left edge1\r\n"); + result = findIndex(array, size, 1); + assertEquals(0, result); + + printf("Left edge2\r\n"); + result = findIndex(array, size, 1.1); + assertEquals(0, result); + + printf("Middle\r\n"); + result = findIndex(array, size, 3); + assertEquals(2, result); + + size = 5; // now test with off array size + + printf("Middle2\r\n"); + result = findIndex(array, size, 4); + assertEquals(3, result); + + printf("Middle2\r\n"); + result = findIndex(array, size, 3.1); + assertEquals(2, result); +} + +//static float getValue2(float key, float maf) { +// +//} + +void testInterpolate2d(void) { + printf("*************************************************** testInterpolate2d\r\n"); + + float bins4[] = { 1, 2, 3, 4 }; + float values4[] = { 1, 20, 30, 400 }; + int size = 4; + + int result; + + printf("Left size\r\n"); + result = interpolate2d(0, bins4, values4, size); + assertEquals(1, result); + + printf("Right size\r\n"); + result = interpolate2d(10, bins4, values4, size); + assertEquals(400, result); + + printf("Middle1\r\n"); + result = interpolate2d(3, bins4, values4, size); + assertEquals(30, result); + + printf("Middle1\r\n"); + result = interpolate2d(3.5, bins4, values4, size); + assertEquals(215, result); +} + +static engine_configuration_s engineConfiguration; + +void testSetTableValue(void) { + printf("*************************************************** testSetTableValue\r\n"); + + for (int i = 0; i < CLT_CURVE_SIZE; i++) { + engineConfiguration.cltFuelCorrBins[i] = -40 + i * 10; + engineConfiguration.cltFuelCorr[i] = 1; + } + + assertEquals(1, engineConfiguration.cltFuelCorr[0]); + + setTableValue(engineConfiguration.cltFuelCorrBins, engineConfiguration.cltFuelCorr, CLT_CURVE_SIZE, -40, 1.5); + assertEquals(1.5, engineConfiguration.cltFuelCorr[0]); + + setTableValue(engineConfiguration.cltFuelCorrBins, engineConfiguration.cltFuelCorr, CLT_CURVE_SIZE, -50, 1.4); + assertEquals(1.4, engineConfiguration.cltFuelCorr[0]); + +} diff --git a/unit_tests/test_basic_math/test_find_index.h b/unit_tests/test_basic_math/test_find_index.h new file mode 100644 index 0000000000..3a611751c1 --- /dev/null +++ b/unit_tests/test_basic_math/test_find_index.h @@ -0,0 +1,15 @@ +/* + * test_find_index.h + * + * Created on: Oct 30, 2013 + * Author: Andrey Belomutskiy, (c) 2012-2013 + */ + +#ifndef TEST_FIND_INDEX_H_ +#define TEST_FIND_INDEX_H_ + +void testFindIndex(void); +void testInterpolate2d(void); +void testSetTableValue(void); + +#endif /* TEST_FIND_INDEX_H_ */ diff --git a/unit_tests/test_basic_math/test_interpolation_3d.c b/unit_tests/test_basic_math/test_interpolation_3d.c new file mode 100644 index 0000000000..6fa875b94c --- /dev/null +++ b/unit_tests/test_basic_math/test_interpolation_3d.c @@ -0,0 +1,70 @@ +/* + * Created on: Oct 17, 2013 + * Author: Andrey Belomutskiy, (c) 2012-2013 + */ + +/** + * @file test_interpolation_3d.c + */ + +#include "test_interpolation_3d.h" +#include +#include + +#include "interpolation.h" +#include "main.h" + +float rpmBins[5] = { 100, 200, 300, 400, 500 }; +float mafBins[4] = { 1, 2, 3, 4 }; + +float map0[4] = { 1, 2, 3, 4 }; +float map1[4] = { 2, 3, 4, 5 }; +float map2[4] = { 3, 4, 200, 300 }; +float map3[4] = { 4, 200, 500, 600 }; +float map4[4] = { 4, 200, 500, 600 }; + +float *map[5] = { map0, map1, map2, map3, map4 }; + + +static float getValue(float rpm, float maf) { + return interpolate3d(rpm, rpmBins, 5, maf, mafBins, 4, map); +} + +void testInterpolate3d(void) { + printf("*************************************************** testInterpolate3d\r\n"); + float dwell; + printf("*** no interpolation here 1\r\n"); + dwell = getValue(100, 2); + assertEquals(2, dwell); + + printf("*** no interpolation here 2\r\n"); + dwell = getValue(200, 4); + assertEquals(5, dwell); + + printf("*** rpm interpolated value expected1\r\n"); + dwell = getValue(150, 2); + assertEquals(2.5, dwell); + + printf("*** rpm interpolated value expected2\r\n"); + dwell = getValue(250, 3); + assertEquals(102, dwell); + + printf("*** both rpm and maf interpolated value expected\r\n"); + dwell = getValue(335.3, 3.551); + assertEquals(361, dwell); + + printf("*** both rpm and maf interpolated value expected 2\r\n"); + dwell = getValue(410.01, 2.012); + assertEquals(203.6, dwell); + + printf("*** both rpm and maf interpolated value expected 3\r\n"); + dwell = getValue(1000000, 1000); + assertEquals(600, dwell); + + printf("*** both rpm and maf interpolated value expected 4\r\n"); + dwell = getValue(410.01, -1); + assertEquals(4, dwell); + + dwell = getValue(-1, -1); + assertEquals(1, dwell); +} diff --git a/unit_tests/test_basic_math/test_interpolation_3d.h b/unit_tests/test_basic_math/test_interpolation_3d.h new file mode 100644 index 0000000000..684c26ba74 --- /dev/null +++ b/unit_tests/test_basic_math/test_interpolation_3d.h @@ -0,0 +1,16 @@ +/* + * Created on: Oct 17, 2013 + * Author: Andrey Belomutskiy, (c) 2012-2013 + */ + +/** + * @file test_interpolation_3d.h + */ + + +#ifndef TEST_INTERPOLATION_3D_H_ +#define TEST_INTERPOLATION_3D_H_ + +void testInterpolate3d(void); + +#endif /* TEST_INTERPOLATION_3D_H_ */ diff --git a/unit_tests/test_data_structures/test_event_registry.c b/unit_tests/test_data_structures/test_event_registry.c new file mode 100644 index 0000000000..e7d2bd025a --- /dev/null +++ b/unit_tests/test_data_structures/test_event_registry.c @@ -0,0 +1,56 @@ +/* + * test_event_registry.c + * + * Created on: Nov 27, 2013 + * Author: Andrey Belomutskiy, (c) 2012-2013 + */ + +#include "event_registry.h" +#include "test_event_registry.h" +#include "main.h" + +static ActuatorEventList eventList; +static ActuatorEventList result; + +int pinDefaultState[IO_PIN_COUNT]; + +void initOutputSignal(OutputSignal *signal, io_pin_e ioPin) { + signal->io_pin = ioPin; +} + +extern int outputSignalCount; + +void testEventRegistry(void) { + printf("*************************************** testEventRegistry\r\n"); + + printf("resetting\r\n"); + resetEventList(&eventList); + resetOutputSignals(); + printf("registering 0\r\n"); + + registerActuatorEvent(&eventList, 0, addOutputSignal(10), 0); + registerActuatorEvent(&eventList, 0, addOutputSignal(20), 10); + assertEquals(2, eventList.size); + + printf("registering 1\r\n"); + registerActuatorEvent(&eventList, 1, addOutputSignal(30), 0); + registerActuatorEvent(&eventList, 1, addOutputSignal(40), 10); + assertEquals(4, eventList.size); + + printf("Looking for 0\r\n"); + findEvents(0, &eventList, &result); + assertEquals(2, result.size); + assertEquals(4, eventList.size); + + printf("Validating pins\r\n"); + assertEquals(10, result.events[0].actuator->io_pin); + assertEquals(20, result.events[1].actuator->io_pin); + + printf("Looking for 1\r\n"); + findEvents(1, &eventList, &result); + assertEquals(2, result.size); + assertEquals(4, eventList.size); + + assertEquals(30, result.events[0].actuator->io_pin); + assertEquals(40, result.events[1].actuator->io_pin); +} diff --git a/unit_tests/test_data_structures/test_event_registry.h b/unit_tests/test_data_structures/test_event_registry.h new file mode 100644 index 0000000000..593a291844 --- /dev/null +++ b/unit_tests/test_data_structures/test_event_registry.h @@ -0,0 +1,13 @@ +/* + * test_event_registry.h + * + * Created on: Nov 27, 2013 + * Author: Andrey Belomutskiy, (c) 2012-2013 + */ + +#ifndef TEST_EVENT_REGISTRY_H_ +#define TEST_EVENT_REGISTRY_H_ + +void testEventRegistry(void); + +#endif /* TEST_EVENT_REGISTRY_H_ */ diff --git a/unit_tests/test_engine_math.c b/unit_tests/test_engine_math.c new file mode 100644 index 0000000000..b2898892c7 --- /dev/null +++ b/unit_tests/test_engine_math.c @@ -0,0 +1,27 @@ +/* + * @file test_engine_math.c + * + * Created on: Nov 14, 2013 + * Author: Andrey Belomutskiy, (c) 2012-2013 + */ + +#include "test_engine_math.h" +#include "main.h" +#include "engine_math.h" +#include "engine_configuration.h" + +extern engine_configuration_s *engineConfiguration; +extern engine_configuration2_s *engineConfiguration2; + +void testEngineMath(void) { + printf("*************************************************** testEngineMath\r\n"); + + engineConfiguration->rpmMultiplier = 0.5; + + assertEqualsM("600 RPM", 5000, getOneDegreeTime(600) * 180); + assertEqualsM("6000 RPM", 500, getOneDegreeTime(6000) * 180); +} + +float getMap(void) { + return 0; +} diff --git a/unit_tests/test_engine_math.h b/unit_tests/test_engine_math.h new file mode 100644 index 0000000000..8f80cc7f41 --- /dev/null +++ b/unit_tests/test_engine_math.h @@ -0,0 +1,13 @@ +/* + * @file test_engine_math.h + * + * Created on: Nov 14, 2013 + * Author: Andrey Belomutskiy, (c) 2012-2013 + */ + +#ifndef TEST_ENGINE_MATH_H_ +#define TEST_ENGINE_MATH_H_ + +void testEngineMath(void); + +#endif /* TEST_ENGINE_MATH_H_ */ diff --git a/unit_tests/test_fuel_map.c b/unit_tests/test_fuel_map.c new file mode 100644 index 0000000000..337dc6a561 --- /dev/null +++ b/unit_tests/test_fuel_map.c @@ -0,0 +1,150 @@ +/** + * @file test_fuel_map.c + * + * Created on: Nov 6, 2013 + * Author: Andrey Belomutskiy, (c) 2012-2013 + */ + +#include "main.h" +#include "engine_configuration.h" +#include "fuel_math.h" +#include "trigger_structure.h" +#include "allsensors.h" +#include "engine_math.h" + +extern engine_configuration_s *engineConfiguration; +extern engine_configuration2_s *engineConfiguration2; + +void testFuelMap(void) { + chDbgCheck(engineConfiguration!=NULL, "engineConfiguration"); + + printf("*************************************************** testFuelMap\r\n"); + + for (int k = 0; k < FUEL_LOAD_COUNT; k++) { + for (int r = 0; r < FUEL_RPM_COUNT; r++) { + engineConfiguration->fuelTable[k][r] = k * 200 + r; + } + } + printf("*************************************************** initThermistors\r\n"); + + initThermistors(); + + printf("*** getInjectorLag\r\n"); + assertEquals(0, getInjectorLag(12)); + + for (int i = 0; i < FUEL_LOAD_COUNT; i++) + engineConfiguration->fuelLoadBins[i] = i; + for (int i = 0; i < FUEL_RPM_COUNT; i++) + engineConfiguration->fuelRpmBins[i] = i; + + printf("*************************************************** prepareFuelMap\r\n"); + prepareFuelMap(); + assertEquals(1005, getBaseFuel(5, 5)); + + engineConfiguration->injectorLag = 0.5; + + for (int i = 0; i < VBAT_INJECTOR_CURVE_SIZE; i++) { + engineConfiguration->battInjectorLagCorrBins[i] = i; + engineConfiguration->battInjectorLagCorr[i] = 2 * i; + } + + // because all the correction tables are zero + printf("*************************************************** getRunningFuel\r\n"); + assertEquals(1005.5, getRunningFuel(5, 5)); + + printf("*************************************************** setting IAT table\r\n"); + for (int i = 0; i < IAT_CURVE_SIZE; i++) { + engineConfiguration->iatFuelCorrBins[i] = i; + engineConfiguration->iatFuelCorr[i] = 2 * i; + } + engineConfiguration->iatFuelCorr[0] = 2; + + printf("*************************************************** setting CLT table\r\n"); + for (int i = 0; i < CLT_CURVE_SIZE; i++) { + engineConfiguration->cltFuelCorrBins[i] = i; + engineConfiguration->cltFuelCorr[i] = 1; + } + engineConfiguration->injectorLag = 0; + + assertEquals(NAN, getIntakeAirTemperature()); + float iatCorrection = getIatCorrection(-KELV); + assertEqualsM("IAT", 2, iatCorrection); + float cltCorrection = getCltCorrection(getCoolantTemperature()); + assertEqualsM("CLT", 1, cltCorrection); + float injectorLag = getInjectorLag(getVBatt()); + assertEquals(0, injectorLag); + + + // 1005 * 2 for IAT correction + printf("*************************************************** getRunningFuel\r\n"); + assertEquals(1005, getRunningFuel(5, 5)); + + engineConfiguration->crankingSettings.coolantTempMaxC = 65; // 8ms at 65C + engineConfiguration->crankingSettings.fuelAtMaxTempMs = 8; + + engineConfiguration->crankingSettings.coolantTempMinC = 0; // 20ms at 0C + engineConfiguration->crankingSettings.fuelAtMinTempMs = 20; + + printf("*************************************************** getStartingFuel\r\n"); + // NAN in case we have issues with the CLT sensor +// assertEquals(16, getStartingFuel(NAN)); + assertEquals(20, getStartingFuel(0)); + assertEquals(18.5231, getStartingFuel(8)); + assertEquals(8, getStartingFuel(70)); +} + +static void confgiureFordAspireTriggerShape(trigger_shape_s * s) { + triggerShapeInit(s); + + triggerAddEvent(s, 53.747, T_SECONDARY, 1); + triggerAddEvent(s, 121.90, T_SECONDARY, 0); + triggerAddEvent(s, 232.76, T_SECONDARY, 1); + triggerAddEvent(s, 300.54, T_SECONDARY, 0); + triggerAddEvent(s, 360, T_PRIMARY, 1); + + triggerAddEvent(s, 409.8412, T_SECONDARY, 1); + triggerAddEvent(s, 478.6505, T_SECONDARY, 0); + triggerAddEvent(s, 588.045, T_SECONDARY, 1); + triggerAddEvent(s, 657.03, T_SECONDARY, 0); + triggerAddEvent(s, 720, T_PRIMARY, 0); +} + + +static ActuatorEventList ae; + +extern int outputSignalCount; + +void testAngleResolver(void) { + printf("*************************************************** testAngleResolver\r\n"); + + engineConfiguration->globalTriggerAngleOffset = 175; + trigger_shape_s * ts = &engineConfiguration2->triggerShape; + + confgiureFordAspireTriggerShape(ts); + assertEqualsM("shape size", 10, ts->size); + + resetOutputSignals(); + + resetEventList(&ae); + printf("*************************************************** testAngleResolver 0\r\n"); + registerActuatorEventExt(engineConfiguration, &engineConfiguration2->triggerShape, &ae, addOutputSignal(INJECTOR_1_OUTPUT), 53 - 175); + assertEquals(1, ae.size); + assertEquals(1, outputSignalCount); + assertEquals(0, ae.events[0].eventIndex); + assertEquals(53, ae.events[0].angleOffset); + + printf("*************************************************** testAngleResolver 2\r\n"); + resetEventList(&ae); + registerActuatorEventExt(engineConfiguration, &engineConfiguration2->triggerShape, &ae, addOutputSignal(INJECTOR_1_OUTPUT), 51 + 180 - 175); + assertEquals(2, ae.events[0].eventIndex); + assertEquals(51.9870, ae.events[0].angleOffset); +} + +void testPinHelper(void) { + printf("*************************************************** testPinHelper\r\n"); + assertEquals(0, getElectricalValue(0, OM_DEFAULT)); + assertEquals(1, getElectricalValue(1, OM_DEFAULT)); + + assertEquals(0, getElectricalValue(1, OM_INVERTED)); + assertEquals(1, getElectricalValue(0, OM_INVERTED)); +} diff --git a/unit_tests/test_fuel_map.h b/unit_tests/test_fuel_map.h new file mode 100644 index 0000000000..4f68e7daf5 --- /dev/null +++ b/unit_tests/test_fuel_map.h @@ -0,0 +1,15 @@ +/* + * test_fuel_map.h + * + * Created on: Nov 6, 2013 + * Author: pc + */ + +#ifndef TEST_FUEL_MAP_H_ +#define TEST_FUEL_MAP_H_ + +void testFuelMap(void); +void testAngleResolver(void); +void testPinHelper(void); + +#endif /* TEST_FUEL_MAP_H_ */ diff --git a/unit_tests/test_idle_controller.c b/unit_tests/test_idle_controller.c new file mode 100644 index 0000000000..8150a638fe --- /dev/null +++ b/unit_tests/test_idle_controller.c @@ -0,0 +1,17 @@ +/* + * test_idle_controller.c + * + * Created on: Oct 17, 2013 + * Author: Andrey + */ + +#include + + +void idleDebug(char *msg, int value) { + printf("%s\r\n", msg); +} + +void isCranking(void) { + return; +} diff --git a/unit_tests/test_idle_controller.h b/unit_tests/test_idle_controller.h new file mode 100644 index 0000000000..ff3aa90ac1 --- /dev/null +++ b/unit_tests/test_idle_controller.h @@ -0,0 +1,7 @@ +#ifndef TEST_IDLE_CONTROLLER_H +#define TEST_IDLE_CONTROLLER_H + +void idleDebug(char *msg, int value); +void isCranking(void); + +#endif diff --git a/unit_tests/test_sensors.c b/unit_tests/test_sensors.c new file mode 100644 index 0000000000..cf4a72c7df --- /dev/null +++ b/unit_tests/test_sensors.c @@ -0,0 +1,35 @@ +/** + * @file test_sensors.c + * + * @date Dec 7, 2013 + * @author Andrey Belomutskiy, (c) 2012-2013 + */ + +#include "main.h" +#include "thermistors.h" +#include "map.h" + +static ThermistorConf tc; + +static void testMapDecoding() { + assertEqualsM("denso 0 volts", -6.64, getMAPValueHonda_Denso183(0)); + assertEquals(31.244, getMAPValueHonda_Denso183(1)); + + assertEqualsM("MPX_4250 0 volts", 8, getMAPValueMPX_4250(0)); + assertEquals(58.4, getMAPValueMPX_4250(1)); +} + +void testSensors(void) { + print("************************************************** testSensors\r\n"); + testMapDecoding(); + setThermistorConfiguration(&tc, 32, 9500, 75, 2100, 120, 1000); + + prepareThermistorCurve(&tc); + + assertEquals(-0.003, tc.s_h_a); + assertEquals(0.001, tc.s_h_b); + assertEquals(0.0, tc.s_h_c); + + float t = convertResistanceToKelvinTemperature(2100, &tc); + assertEquals(75 + KELV, t); +} diff --git a/unit_tests/test_sensors.h b/unit_tests/test_sensors.h new file mode 100644 index 0000000000..b59fd418fd --- /dev/null +++ b/unit_tests/test_sensors.h @@ -0,0 +1,13 @@ +/** + * @file test_sensors.h + * + * @date Dec 7, 2013 + * @author Andrey Belomutskiy, (c) 2012-2013 + */ + +#ifndef TEST_SENSORS_H_ +#define TEST_SENSORS_H_ + +void testSensors(void); + +#endif /* TEST_SENSORS_H_ */ diff --git a/unit_tests/test_signal_executor.c b/unit_tests/test_signal_executor.c new file mode 100644 index 0000000000..ab066951d0 --- /dev/null +++ b/unit_tests/test_signal_executor.c @@ -0,0 +1,69 @@ +/** + * @file test_signal_executor.c + * + * @date Nov 28, 2013 + * @author Andrey Belomutskiy, (c) 2012-2013 + */ + +#include +#include "main.h" + +#include "signal_executor.h" +#include "signal_executor_single_timer_algo.h" +#include "test_signal_executor.h" +#include "io_pins.h" +#include "utlist.h" + +extern OutputSignal *st_output_list; + +static io_pin_e testLastToggledPin; +static int testToggleCounter; + +void setOutputPinValue(io_pin_e pin, int value) { + // this is a test implementation of the method - we use it to see what's going on + testLastToggledPin = pin; + testToggleCounter++; +} + +void scheduleTask(scheduling_s *scheduling, int delay, schfunc_t callback, void *param) { + +} + +void testSignalExecutor() { + print("*************************************** testSignalExecutor\r\n"); + +// OutputSignal s1; +// OutputSignal s2; +// +// registerSignal(&s1); +// registerSignal(&s2); +// +// OutputSignal *out; +// int count; +// LL_COUNT(st_output_list, out, count); +// assertEquals(2, count); +// +// s1.io_pin = 0; +// initOutputSignalBase(&s1); +// assertEqualsM("status", IDLE, s1.status); +// scheduleOutputBase(&s1, 10, 100); +// +// long now = 1; +// print("now = 1\r\n"); +// testToggleCounter = 0; +// assertEqualsM("duration", 10, GET_DURATION(&s1)); +// assertEquals(9, toggleSignalIfNeeded(&s1, now)); +// assertEquals(0, testToggleCounter); +// +// now = 100; +// print("now = 100\r\n"); +// testToggleCounter = 0; +// assertEquals(100, toggleSignalIfNeeded(&s1, now)); +// assertEquals(1, testToggleCounter); +// +// print("now = 300\r\n"); +// now = 300; // let's see what happens if the handler is late +// testToggleCounter = 0; +// assertEquals(10, toggleSignalIfNeeded(&s1, now)); +// assertEquals(1, testToggleCounter); +} diff --git a/unit_tests/test_signal_executor.h b/unit_tests/test_signal_executor.h new file mode 100644 index 0000000000..e61fcf3b38 --- /dev/null +++ b/unit_tests/test_signal_executor.h @@ -0,0 +1,13 @@ +/** + * @file test_signal_executor.h + * + * @date Nov 28, 2013 + * @author Andrey Belomutskiy, (c) 2012-2013 + */ + +#ifndef TEST_SIGNAL_EXECUTOR_H_ +#define TEST_SIGNAL_EXECUTOR_H_ + +void testSignalExecutor(void); + +#endif /* TEST_SIGNAL_EXECUTOR_H_ */ diff --git a/unit_tests/test_trigger_decoder.c b/unit_tests/test_trigger_decoder.c new file mode 100644 index 0000000000..08082b4c3f --- /dev/null +++ b/unit_tests/test_trigger_decoder.c @@ -0,0 +1,245 @@ +/** + * @file test_trigger_decoder.c + * + * @date Dec 24, 2013 + * @author Andrey Belomutskiy, (c) 2012-2013 + */ + +#include "main.h" +#include "test_trigger_decoder.h" +#include "trigger_decoder.h" +#include "engine_math.h" + +#include "ford_aspire.h" +#include "dodge_neon.h" +#include "ford_1995_inline_6.h" +#include "mazda_323.h" + +void sendOutConfirmation(char *value, int i) { + // test implementation +} + +static void testDodgeNeonDecoder(void) { + printf("*************************************************** testDodgeNeonDecoder\r\n"); + initTriggerDecoder(); + + engine_configuration_s ec; + engine_configuration2_s ec2; + + resetConfigurationExt(DODGE_NEON_1995, &ec, &ec2); + + trigger_shape_s * shape = &ec2.triggerShape; + trigger_state_s state; + clearTriggerState(&state); + + + + + assertFalseM("1 shaft_is_synchronized", state.shaft_is_synchronized); + + + int r = 0; + processTriggerEvent(&state, shape, &ec.triggerConfig, SHAFT_PRIMARY_UP, r + 60); + assertFalseM("2 shaft_is_synchronized", state.shaft_is_synchronized); // still no synchronization + + processTriggerEvent(&state, shape, &ec.triggerConfig, SHAFT_PRIMARY_DOWN, r + 210); + assertFalseM("3 shaft_is_synchronized", state.shaft_is_synchronized); // still no synchronization + + processTriggerEvent(&state, shape, &ec.triggerConfig, SHAFT_PRIMARY_UP, r + 420); + assertFalseM("4 shaft_is_synchronized", state.shaft_is_synchronized); // still no synchronization + + processTriggerEvent(&state, shape, &ec.triggerConfig, SHAFT_PRIMARY_DOWN, r + 630); + assertFalse(state.shaft_is_synchronized); // still no synchronization + + printf("2nd camshaft revolution\r\n"); + r = 720; + processTriggerEvent(&state, shape, &ec.triggerConfig, SHAFT_PRIMARY_UP, r + 60); + processTriggerEvent(&state, shape, &ec.triggerConfig, SHAFT_PRIMARY_DOWN, r + 210); + assertTrue(state.shaft_is_synchronized); + assertEquals(0, state.current_index); + processTriggerEvent(&state, shape, &ec.triggerConfig, SHAFT_PRIMARY_UP, r + 420); + assertEquals(1, state.current_index); + processTriggerEvent(&state, shape, &ec.triggerConfig, SHAFT_PRIMARY_DOWN, r + 630); + assertEquals(2, state.current_index); + + printf("3rd camshaft revolution\r\n"); + r = 2 * 720; + processTriggerEvent(&state, shape, &ec.triggerConfig, SHAFT_PRIMARY_UP, r + 60); + assertEqualsM("current index", 3, state.current_index); + processTriggerEvent(&state, shape, &ec.triggerConfig, SHAFT_PRIMARY_DOWN, r + 210); + assertTrue(state.shaft_is_synchronized); + assertEqualsM("current index", 0, state.current_index); + processTriggerEvent(&state, shape, &ec.triggerConfig, SHAFT_PRIMARY_UP, r + 420); + processTriggerEvent(&state, shape, &ec.triggerConfig, SHAFT_PRIMARY_DOWN, r + 630); +} + +static void test1995FordInline6TriggerDecoder(void) { + printf("*************************************************** test1995FordInline6TriggerDecoder\r\n"); + initTriggerDecoder(); + resetOutputSignals(); + + engine_configuration_s ec; + engine_configuration2_s ec2; + + resetConfigurationExt(FORD_INLINE_6_1995, &ec, &ec2); + + ActuatorEventList *ecl = &ec2.engineEventConfiguration.ignitionEvents; + assertEqualsM("ignition events size", 6, ecl->size); + assertEqualsM("event index", 0, ecl->events[0].eventIndex); + assertEquals(13, ecl->events[0].angleOffset); + + assertEqualsM("event index", 10, ecl->events[5].eventIndex); + assertEquals(13, ecl->events[5].angleOffset); + + trigger_state_s state; + clearTriggerState(&state); + + trigger_shape_s * shape = &ec2.triggerShape; + assertFalseM("shaft_is_synchronized", state.shaft_is_synchronized); + int r = 0; + processTriggerEvent(&state, shape, &ec.triggerConfig, SHAFT_PRIMARY_DOWN, r + 10); + assertFalseM("shaft_is_synchronized", state.shaft_is_synchronized); // still no synchronization + processTriggerEvent(&state, shape, &ec.triggerConfig, SHAFT_PRIMARY_UP, r + 11); + assertTrue(state.shaft_is_synchronized); // first signal rise synchronize + assertEquals(0, state.current_index); + processTriggerEvent(&state, shape, &ec.triggerConfig, SHAFT_PRIMARY_DOWN, r++); + assertEquals(1, state.current_index); + + for (int i = 2; i < 10;) { + processTriggerEvent(&state, shape, &ec.triggerConfig, SHAFT_PRIMARY_UP, r++); + assertEqualsM("even", i++, state.current_index); + processTriggerEvent(&state, shape, &ec.triggerConfig, SHAFT_PRIMARY_DOWN, r++); + assertEqualsM("odd", i++, state.current_index); + } + + processTriggerEvent(&state, shape, &ec.triggerConfig, SHAFT_PRIMARY_UP, r++); + assertEquals(10, state.current_index); + + processTriggerEvent(&state, shape, &ec.triggerConfig, SHAFT_PRIMARY_DOWN, r++); + assertEquals(11, state.current_index); + + processTriggerEvent(&state, shape, &ec.triggerConfig, SHAFT_PRIMARY_UP, r++); + assertEquals(0, state.current_index); // new revolution + + assertEqualsM("running dwell", 0.5, getSparkDwellMsT(&ec, 2000)); +} + +void testFordAspire(void) { + printf("*************************************************** testTriggerDecoder\r\n"); + + engine_configuration_s ec; + engine_configuration2_s ec2; + resetConfigurationExt(FORD_ASPIRE_1996, &ec, &ec2); + + assertEqualsM("cranking dwell", 54.166670, getSparkDwellMsT(&ec, 200)); + assertEqualsM("running dwell", 4, getSparkDwellMsT(&ec, 2000)); + + assertEqualsM("higher rpm dwell", 3.25, getSparkDwellMsT(&ec, 6000)); +} + +void testMazda323(void) { + printf("*************************************************** testMazda323\r\n"); + + engine_configuration_s ec; + engine_configuration2_s ec2; + resetConfigurationExt(MAZDA_323, &ec, &ec2); + +} + +void testMazdaMianaNbDecoder(void) { + printf("*************************************************** testMazdaMianaNbDecoder\r\n"); + + engine_configuration_s ec; + engine_configuration2_s ec2; + resetConfigurationExt(MAZDA_MIATA_NB, &ec, &ec2); + + trigger_state_s state; + clearTriggerState(&state); + trigger_shape_s * shape = &ec2.triggerShape; + + int a = 0; + processTriggerEvent(&state, shape, &ec.triggerConfig, SHAFT_PRIMARY_DOWN, a + 20); + assertFalseM("0a shaft_is_synchronized", state.shaft_is_synchronized); + processTriggerEvent(&state, shape, &ec.triggerConfig, SHAFT_PRIMARY_UP, a + 340); + assertFalseM("0b shaft_is_synchronized", state.shaft_is_synchronized); + + processTriggerEvent(&state, shape, &ec.triggerConfig, SHAFT_PRIMARY_DOWN, a + 360); + assertFalseM("0c shaft_is_synchronized", state.shaft_is_synchronized); + processTriggerEvent(&state, shape, &ec.triggerConfig, SHAFT_PRIMARY_UP, a + 380); + assertFalseM("0d shaft_is_synchronized", state.shaft_is_synchronized); + processTriggerEvent(&state, shape, &ec.triggerConfig, SHAFT_PRIMARY_DOWN, a + 400); + assertTrueM("0e shaft_is_synchronized", state.shaft_is_synchronized); + + processTriggerEvent(&state, shape, &ec.triggerConfig, SHAFT_PRIMARY_UP, a + 720); + assertTrueM("0f shaft_is_synchronized", state.shaft_is_synchronized); + + a = 720; + processTriggerEvent(&state, shape, &ec.triggerConfig, SHAFT_PRIMARY_DOWN, a + 20); + assertTrueM("1a shaft_is_synchronized", state.shaft_is_synchronized); + processTriggerEvent(&state, shape, &ec.triggerConfig, SHAFT_PRIMARY_UP, a + 340); + assertTrueM("1b shaft_is_synchronized", state.shaft_is_synchronized); + + processTriggerEvent(&state, shape, &ec.triggerConfig, SHAFT_PRIMARY_DOWN, a + 360); + assertTrueM("1c shaft_is_synchronized", state.shaft_is_synchronized); + processTriggerEvent(&state, shape, &ec.triggerConfig, SHAFT_PRIMARY_UP, a + 380); + assertTrueM("1d shaft_is_synchronized", state.shaft_is_synchronized); + assertEquals(5, state.current_index); + + processTriggerEvent(&state, shape, &ec.triggerConfig, SHAFT_PRIMARY_DOWN, a + 400); + assertTrueM("1e shaft_is_synchronized", state.shaft_is_synchronized); + assertEquals(0, state.current_index); + + + processTriggerEvent(&state, shape, &ec.triggerConfig, SHAFT_PRIMARY_UP, a + 720); + assertTrueM("1f shaft_is_synchronized", state.shaft_is_synchronized); + + +} + +void testGY6_139QMB(void) { + printf("*************************************************** testGY6_139QMB\r\n"); + + engine_configuration_s ec; + engine_configuration2_s ec2; + resetConfigurationExt(GY6_139QMB, &ec, &ec2); + + trigger_state_s state; + clearTriggerState(&state); + assertFalseM("shaft_is_synchronized", state.shaft_is_synchronized); + + trigger_shape_s * shape = &ec2.triggerShape; + + assertFalseM("shaft_is_synchronized", state.shaft_is_synchronized); + assertEquals(0, state.current_index); + + int now = 0; + processTriggerEvent(&state, shape, &ec.triggerConfig, SHAFT_PRIMARY_UP, now++); + assertTrueM("shaft_is_synchronized", state.shaft_is_synchronized); + assertEquals(0, state.current_index); + + processTriggerEvent(&state, shape, &ec.triggerConfig, SHAFT_PRIMARY_DOWN, now++); + assertTrueM("shaft_is_synchronized", state.shaft_is_synchronized); + assertEquals(1, state.current_index); +} + +void testTriggerDecoder(void) { + printf("*************************************************** testTriggerDecoder\r\n"); + + engine_configuration2_s ec2; + + initializeSkippedToothTriggerShapeExt(&ec2, 2, 0); + assertEquals(ec2.triggerShape.size, 4); + assertEquals(ec2.triggerShape.wave.switchTimes[0], 0.25); + assertEquals(ec2.triggerShape.wave.switchTimes[1], 0.5); + assertEquals(ec2.triggerShape.wave.switchTimes[2], 0.75); + assertEquals(ec2.triggerShape.wave.switchTimes[3], 1); + + testDodgeNeonDecoder(); + testFordAspire(); + test1995FordInline6TriggerDecoder(); + testMazdaMianaNbDecoder(); + testGY6_139QMB(); + + testMazda323(); +} + diff --git a/unit_tests/test_trigger_decoder.h b/unit_tests/test_trigger_decoder.h new file mode 100644 index 0000000000..916227865c --- /dev/null +++ b/unit_tests/test_trigger_decoder.h @@ -0,0 +1,13 @@ +/** + * @file test_trigger_decoder.h + * + * @date Dec 24, 2013 + * @author Andrey Belomutskiy, (c) 2012-2013 + */ + +#ifndef TEST_TRIGGER_DECODER_H_ +#define TEST_TRIGGER_DECODER_H_ + +void testTriggerDecoder(void); + +#endif /* TEST_TRIGGER_DECODER_H_ */ diff --git a/unit_tests/test_util.cpp b/unit_tests/test_util.cpp new file mode 100644 index 0000000000..c04bfb6374 --- /dev/null +++ b/unit_tests/test_util.cpp @@ -0,0 +1,278 @@ +/** + * @file test_util.c + * + * @date Dec 8, 2013 + * @author Andrey Belomutskiy, (c) 2012-2013 + */ + +#include + +#include "test_util.h" +#include "cyclic_buffer.h" +#include "main.h" +#include "histogram.h" + +#include "malfunction_central.h" +#include "eficonsole_logic.h" + +#include "nmea.h" + +static cyclic_buffer sb; + + +void testCyclicBuffer(void) { + print("*************************************** testCyclicBuffer\r\n"); + + sb.add(10); + + assertEquals(10, sb.sum(3)); + + sb.add(2); + assertEquals(12, sb.sum(2)); +} + +void testHistogram(void) { + print("******************************************* testHistogram\r\n"); + + initHistogramsModule(); + + assertEquals(80, histogramGetIndex(239)); + assertEquals(223, histogramGetIndex(239239)); + assertEquals(364, histogramGetIndex(239239239)); + + histogram_s h; + + initHistogram(&h, "test"); + + int result[5]; + assertEquals(0, hsReport(&h, result)); + + hsAdd(&h, 10); + assertEquals(1, hsReport(&h, result)); + assertEquals(10, result[0]); + + // let's add same value one more time + hsAdd(&h, 10); + assertEquals(2, hsReport(&h, result)); + assertEquals(10, result[0]); + assertEquals(10, result[1]); + + hsAdd(&h, 10); + hsAdd(&h, 10); + hsAdd(&h, 10); + + hsAdd(&h, 1000); + hsAdd(&h, 100); + + assertEquals(5, hsReport(&h, result)); + + assertEquals(5, result[0]); + assertEquals(10, result[1]); + assertEquals(10, result[2]); + assertEquals(100, result[3]); + // values are not expected to be exactly the same, it's the shape what matters + assertEquals(1011, result[4]); +} + +static void testMalfunctionCentralRemoveNonExistent() { + print("******************************************* testMalfunctionCentralRemoveNonExistent\r\n"); + initMalfunctionCentral(); + + // this should not crash + removeError(OBD_Engine_Coolant_Temperature_Circuit_Malfunction); +} + +static void testMalfunctionCentralSameElementAgain() { + initMalfunctionCentral(); + print("******************************************* testMalfunctionCentralSameElementAgain\r\n"); + error_codes_set_s localCopy; + + addError(OBD_Engine_Coolant_Temperature_Circuit_Malfunction); + addError(OBD_Engine_Coolant_Temperature_Circuit_Malfunction); + getErrorCodes(&localCopy); + assertEquals(1, localCopy.count); +} + +static void testMalfunctionCentralRemoveFirstElement() { + initMalfunctionCentral(); + print("******************************************* testMalfunctionCentralRemoveFirstElement\r\n"); + error_codes_set_s localCopy; + + obd_code_e firstElement = OBD_Engine_Coolant_Temperature_Circuit_Malfunction; + addError(firstElement); + + obd_code_e secondElement = OBD_Intake_Air_Temperature_Circuit_Malfunction; + addError(secondElement); + getErrorCodes(&localCopy); + assertEquals(2, localCopy.count); + + // let's remove first element - code + removeError(firstElement); + + getErrorCodes(&localCopy); + assertEquals(1, localCopy.count); + assertEquals(secondElement, localCopy.error_codes[0]); +} + +void testMalfunctionCentral(void) { + testMalfunctionCentralRemoveNonExistent(); + testMalfunctionCentralSameElementAgain(); + testMalfunctionCentralRemoveFirstElement(); + + print("******************************************* testMalfunctionCentral\r\n"); + initMalfunctionCentral(); + + error_codes_set_s localCopy; + + // on start-up error storage should be empty + getErrorCodes(&localCopy); + assertEquals(0, localCopy.count); + + obd_code_e code = OBD_Engine_Coolant_Temperature_Circuit_Malfunction; + // let's add one error and validate + addError(code); + + getErrorCodes(&localCopy); + assertEqualsM("count #1", 1, localCopy.count); + assertEquals(code, localCopy.error_codes[0]); + + // let's remove value which is not in the collection + removeError((obd_code_e)22); + // element not present - nothing to removed + assertEquals(1, localCopy.count); + assertEquals(code, localCopy.error_codes[0]); + + code = OBD_Intake_Air_Temperature_Circuit_Malfunction; + addError(code); + getErrorCodes(&localCopy); + // todo: assertEquals(2, localCopy.count); + + for (int code = 0; code < 100; code++) { + addError((obd_code_e) code); + } + getErrorCodes(&localCopy); + assertEquals(MAX_ERROR_CODES_COUNT, localCopy.count); + + // now we have full array and code below present + removeError(code); + getErrorCodes(&localCopy); + assertEquals(MAX_ERROR_CODES_COUNT - 1, localCopy.count); +} + +static int lastInteger = -1; +static int lastInteger2 = -1; + +static void testEchoI(int param) { + lastInteger = param; +} + +static void testEchoII(int param, int param2) { + lastInteger = param; + lastInteger2 = param2; +} + +static char *lastFirst = NULL; +static char *lastThird = NULL; + +static void testEchoSSS(char *first, char *second, char *third) { + lastFirst = first; + lastThird = third; +} + +#define UNKNOWN_COMMAND "dfadasdasd" + +static loc_t GPSdata; + +static char nmeaMessage[1000]; + +void testGpsParser(void) { + print("******************************************* testGpsParser\r\n"); + + strcpy(nmeaMessage, ""); + gps_location(&GPSdata, nmeaMessage); + + // we need to pass a mutable string, not a constant because the parser would be modifying the string + strcpy(nmeaMessage, "$GPRMC,173843,A,3349.896,N,11808.521,W,000.0,360.0,230108,013.4,E*69"); + gps_location(&GPSdata, nmeaMessage); + assertEqualsM("1 valid", 4, GPSdata.quality); + assertEqualsM("1 latitude", 3349.896, GPSdata.latitude); + assertEqualsM("1 longitude", 11808.521, GPSdata.longitude); + assertEqualsM("1 speed", 0, GPSdata.speed); +// assertEqualsM("1 altitude", 0, GPSdata.altitude); // GPRMC not overwrite altitude + assertEqualsM("1 course", 360, GPSdata.course); + + strcpy(nmeaMessage, "$GPGGA,111609.14,5001.27,N,3613.06,E,3,08,0.0,10.2,M,0.0,M,0.0,0000*70"); + gps_location(&GPSdata, nmeaMessage); + assertEqualsM("2 valid", 3, GPSdata.quality); // see field details + assertEqualsM("2 latitude", 50.0212, GPSdata.latitude); + assertEqualsM("2 longitude", 36.2177, GPSdata.longitude); + assertEqualsM("2 speed", 0, GPSdata.speed); + assertEqualsM("2 altitude", 10.2, GPSdata.altitude); +// assertEqualsM("2 course", 0, GPSdata.course); // GPGGA not overwrite course + + strcpy(nmeaMessage, "$GPRMC,111609.14,A,5001.27,N,3613.06,E,11.2,0.0,261206,0.0,E*50"); + gps_location(&GPSdata, nmeaMessage); + assertEqualsM("3 valid", 4, GPSdata.quality); + assertEqualsM("3 latitude", 5001.27, GPSdata.latitude); + assertEqualsM("3 longitude", 3613.06, GPSdata.longitude); + assertEqualsM("3 speed", 11.2, GPSdata.speed); +// assertEqualsM("3 altitude", 0, GPSdata.altitude); // GPRMC not overwrite altitude + assertEqualsM("3 course", 0, GPSdata.course); + assertEqualsM("3 GPS yy",2006, GPSdata.GPStm.tm_year+1900); + assertEqualsM("3 GPS mm",12, GPSdata.GPStm.tm_mon); + assertEqualsM("3 GPS yy",26, GPSdata.GPStm.tm_mday); + assertEqualsM("3 GPS hh",11, GPSdata.GPStm.tm_hour); + assertEqualsM("3 GPS mm",16, GPSdata.GPStm.tm_min); + assertEqualsM("3 GPS ss",9, GPSdata.GPStm.tm_sec); + + // check again first one + // we need to pass a mutable string, not a constant because the parser would be modifying the string + strcpy(nmeaMessage, "$GPRMC,173843,A,3349.896,N,11808.521,W,000.0,360.0,230108,013.4,E*69"); + gps_location(&GPSdata, nmeaMessage); + assertEqualsM("4 valid", 4, GPSdata.quality); + assertEqualsM("4 latitude", 3349.896, GPSdata.latitude); + assertEqualsM("4 longitude", 11808.521, GPSdata.longitude); + assertEqualsM("4 speed", 0, GPSdata.speed); + assertEqualsM("4 course", 360, GPSdata.course); +} + +// this buffer is needed because on Unix you would not be able to change static char constants +static char buffer[300]; + +void testConsoleLogic(void) { + print("******************************************* testConsoleLogic\r\n"); + resetConsoleActions(); + + helpCommand(); + + char *ptr = validateSecureLine(UNKNOWN_COMMAND); + assertEquals(0, strcmp(UNKNOWN_COMMAND, ptr)); + assertEquals(10, tokenLength(UNKNOWN_COMMAND)); + + // handling invalid token should work + strcpy(buffer, "sdasdafasd asd"); + handleConsoleLine(buffer); + + print("\r\naddConsoleActionI\r\n"); + addConsoleActionI("echoi", testEchoI); + strcpy(buffer, "echoi 239"); + handleConsoleLine(buffer); + assertEquals(239, lastInteger); + + print("\r\naddConsoleActionII\r\n"); + addConsoleActionII("echoii", testEchoII); + strcpy(buffer, "echoii 22 239"); + handleConsoleLine(buffer); + assertEquals(22, lastInteger); + assertEquals(239, lastInteger2); + + print("\r\addConsoleActionSSS\r\n"); + addConsoleActionSSS("echosss", testEchoSSS); + strcpy(buffer, "echosss 111 222 333"); + handleConsoleLine(buffer); + assertEquals(111, atoi(lastFirst)); + assertEquals(333, atoi(lastThird)); + + //addConsoleActionSSS("GPS", testGpsParser); +} + diff --git a/unit_tests/test_util.h b/unit_tests/test_util.h new file mode 100644 index 0000000000..e77940bcc7 --- /dev/null +++ b/unit_tests/test_util.h @@ -0,0 +1,30 @@ +/** + * @file test_cyclic_buffer.h + * + * @date Dec 8, 2013 + * @author Andrey Belomutskiy, (c) 2012-2013 + */ + +#ifndef TEST_CYCLIC_BUFFER_H_ +#define TEST_CYCLIC_BUFFER_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + + + +void testCyclicBuffer(void); +void testHistogram(void); +void testMalfunctionCentral(void); +void testConsoleLogic(void); +void testGpsParser(void); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* TEST_CYCLIC_BUFFER_H_ */ diff --git a/win32_functional_tests/.cproject b/win32_functional_tests/.cproject new file mode 100644 index 0000000000..2480081e74 --- /dev/null +++ b/win32_functional_tests/.cproject @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/win32_functional_tests/.project b/win32_functional_tests/.project new file mode 100644 index 0000000000..b8ba745391 --- /dev/null +++ b/win32_functional_tests/.project @@ -0,0 +1,27 @@ + + + win32_functional_tests + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.core.ccnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/win32_functional_tests/Makefile b/win32_functional_tests/Makefile new file mode 100644 index 0000000000..60329fbec8 --- /dev/null +++ b/win32_functional_tests/Makefile @@ -0,0 +1,256 @@ +############################################################################## +# Build global options +# NOTE: Can be overridden externally. +# + +PROJECT_DIR = ../firmware +#CHIBIOS = $(PROJECT_DIR)/chibios + +# Compiler options here. +ifeq ($(USE_OPT),) +# this config if debugging is needed, but the binary is about 30M +# USE_OPT = -c -Wall -O0 -ggdb -g3 -Werror-implicit-function-declaration -Werror -Wno-error=pointer-sign -Wno-error=unused-function -Wno-error=unused-variable -Wno-error=sign-compare -Wno-error=unused-parameter -Wno-error=missing-field-initializers -Wno-error=write-strings +# this config producec a smaller binary file + USE_OPT = -c -Wall -O2 -Werror-implicit-function-declaration -Werror -Wno-error=pointer-sign -Wno-error=unused-function -Wno-error=unused-variable -Wno-error=sign-compare -Wno-error=unused-parameter -Wno-error=missing-field-initializers -Wno-error=write-strings -Wno-error=strict-aliasing +endif + +# C specific options here (added to USE_OPT). +ifeq ($(USE_COPT),) + USE_COPT = -std=gnu99 -fgnu89-inline +endif + +# C++ specific options here (added to USE_OPT). +ifeq ($(USE_CPPOPT),) + USE_CPPOPT = -fno-rtti -fpermissive -fno-exceptions -fno-use-cxa-atexit +endif + +# Enable this if you want the linker to remove unused code and data +ifeq ($(USE_LINK_GC),) + USE_LINK_GC = yes +endif + +# If enabled, this option allows to compile the application in THUMB mode. +ifeq ($(USE_THUMB),) + USE_THUMB = no +endif + +# Enable this if you want to see the full log while compiling. +ifeq ($(USE_VERBOSE_COMPILE),) + USE_VERBOSE_COMPILE = no +endif + +# +# Build global options +############################################################################## + +############################################################################## +# Architecture or project specific options +# + + +# List all default C defines here, like -D_DEBUG=1 +DDEFS = + +# +# Architecture or project specific options +############################################################################## + +############################################################################## +# Project, sources and paths +# + + # Define project name here +PROJECT = rusefi_simulator + +#PROJECT_BOARD = OLIMEX_STM32_E407 +#ifneq ($(PROJECT_BOARD),OLIMEX_STM32_E407) +# PROJECT_BOARD = ST_STM32F4_DISCOVERY +#endif +#DDEFS += -D$(PROJECT_BOARD) + +CHIBIOS = ../firmware/chibios +# Imported source files and paths +include $(PROJECT_DIR)/util/util.mk +include $(PROJECT_DIR)/config/engines/engines.mk +include $(PROJECT_DIR)/controllers/algo/algo.mk +include $(PROJECT_DIR)/controllers/math/math.mk +include $(PROJECT_DIR)/controllers/sensors/sensors.mk +include $(PROJECT_DIR)/controllers/trigger/trigger.mk +include $(PROJECT_DIR)/controllers/system/system.mk +include $(PROJECT_DIR)/console/console.mk +include $(PROJECT_DIR)/console_util/console_util.mk + +include $(CHIBIOS)/boards/simulator/board.mk +include ${CHIBIOS}/os/hal/hal.mk +include ${CHIBIOS}/os/hal/platforms/Win32/platform.mk +include ${CHIBIOS}/os/ports/GCC/SIMIA32/port.mk +include ${CHIBIOS}/os/kernel/kernel.mk + +# Define linker script file here +#LDSCRIPT= config/system/STM32F407xG.ld +#LDSCRIPT= $(PORTLD)/STM32F407xG_CCM.ld + +# C sources that can be compiled in ARM or THUMB mode depending on the global +# setting. +CSRC = ${PORTSRC} \ + ${KERNSRC} \ + ${TESTSRC} \ + ${HALSRC} \ + ${PLATFORMSRC} \ + $(TRIGGERSRC) \ + $(SYSTEMSRC) \ + $(CONSOLESRC) \ + $(CONSOLEUTILSRC) \ + $(CONTROLLERS_ALGO_SRC) \ + $(CONTROLLERS_MATH_SRC) \ + $(CONTROLLERS_SENSORS_SRC) \ + $(ENGINES_SRC) \ + $(BOARDSRC) \ + ${CHIBIOS}/os/various/chprintf.c \ + ${CHIBIOS}/os/various/memstreams.c \ + $(UTILSRC) \ + simulator/rusEfiFunctionalTest.c \ + $(PROJECT_DIR)/controllers/settings.c \ + simulator/boards.c \ + main.c + +# C++ sources that can be compiled in ARM or THUMB mode depending on the global +# setting. +CPPSRC = $(UTILSRC_CPP) \ + $(CONTROLLERS_ALGO_SRC_CPP) \ + $(CONTROLLERSSRC_CPP) \ + $(SYSTEMSRC_CPP) \ + $(ENGINES_SRC_CPP) \ + $(TEST_SRC_CPP) +# C sources to be compiled in ARM mode regardless of the global setting. +# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler +# option that results in lower performance and larger code size. +ACSRC = + +# C++ sources to be compiled in ARM mode regardless of the global setting. +# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler +# option that results in lower performance and larger code size. +ACPPSRC = + +# C sources to be compiled in THUMB mode regardless of the global setting. +# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler +# option that results in lower performance and larger code size. +TCSRC = + +# C sources to be compiled in THUMB mode regardless of the global setting. +# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler +# option that results in lower performance and larger code size. +TCPPSRC = + +# List ASM source files here +ASMSRC = $(PORTASM) + +INCDIR = . \ +$(PORTINC) $(KERNINC) $(TESTINC) \ + $(HALINC) $(PLATFORMINC) $(BOARDINC) \ + $(PROJECT_DIR)/util \ + $(PROJECT_DIR)/console \ + $(PROJECT_DIR)/console_util \ + $(PROJECT_DIR)/config/engines \ + $(PROJECT_DIR)/ext_algo \ + $(PROJECT_DIR)/controllers \ + $(PROJECT_DIR)/controllers/sensors \ + $(PROJECT_DIR)/controllers/system \ + $(PROJECT_DIR)/hw_layer/algo \ + $(PROJECT_DIR)/controllers/algo \ + $(PROJECT_DIR)/controllers/math \ + $(PROJECT_DIR)/controllers/math \ + $(PROJECT_DIR)/controllers/trigger \ + ${CHIBIOS}/os/various \ + simulator + + +# +# Project, sources and paths +############################################################################## + +############################################################################## +# Compiler settings +# + +#MCU = cortex-m4 + +ifeq ($(OS),Windows_NT) + TRGT = i686-pc-mingw32- +else + TRGT = +endif + +CC = $(TRGT)gcc +CPPC = $(TRGT)g++ +# Enable loading with g++ only if you need C++ runtime support. +# NOTE: You can use C++ even without C++ support if you are careful. C++ +# runtime support makes code size explode. +#LD = $(TRGT)gcc +LD = $(TRGT)g++ +CP = $(TRGT)objcopy +AS = $(TRGT)gcc -x assembler-with-cpp +OD = $(TRGT)objdump +HEX = $(CP) -O ihex +BIN = $(CP) -O binary + +# ARM-specific options here +AOPT = + +# THUMB-specific options here +TOPT = -mthumb -DTHUMB + +# Define C warning options here +CWARN = -Wall -Wextra -Wstrict-prototypes + +# Define C++ warning options here +CPPWARN = -Wall -Wextra + +# +# Compiler settings +############################################################################## + +############################################################################## +# Start of default section +# + +# List all default ASM defines here, like -D_DEBUG=1 +DADEFS = + +# List all default directories to look for include files here +DINCDIR = + +# List the default directory to look for the libraries here +DLIBDIR = + +# List all default libraries here +DLIBS = -static-libgcc -static-libstdc++ -lws2_32 + +# +# End of default section +############################################################################## + +############################################################################## +# Start of user section +# + +# List all user C define here, like -D_DEBUG=1 +UDEFS = + +# Define ASM defines here +UADEFS = + +# List all user directories here +UINCDIR = + +# List the user directory to look for the libraries here +ULIBDIR = + +# List all user libraries here +ULIBS = -lm + +# +# End of user defines +############################################################################## + +include rules.mk diff --git a/win32_functional_tests/chconf.h b/win32_functional_tests/chconf.h new file mode 100644 index 0000000000..a96eeea1cb --- /dev/null +++ b/win32_functional_tests/chconf.h @@ -0,0 +1,534 @@ +/* + ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file templates/chconf.h + * @brief Configuration file template. + * @details A copy of this file must be placed in each project directory, it + * contains the application specific kernel settings. + * + * @addtogroup config + * @details Kernel related settings and hooks. + * @{ + */ + +#ifndef _CHCONF_H_ +#define _CHCONF_H_ + +#define CHPRINTF_USE_FLOAT TRUE + +/*===========================================================================*/ +/** + * @name Kernel parameters and options + * @{ + */ +/*===========================================================================*/ + +/** + * @brief System tick frequency. + * @details Frequency of the system timer that drives the system ticks. This + * setting also defines the system tick time unit. + */ +#if !defined(CH_FREQUENCY) || defined(__DOXYGEN__) +#define CH_FREQUENCY 100000 +#endif + +/** + * @brief Round robin interval. + * @details This constant is the number of system ticks allowed for the + * threads before preemption occurs. Setting this value to zero + * disables the preemption for threads with equal priority and the + * round robin becomes cooperative. Note that higher priority + * threads can still preempt, the kernel is always preemptive. + * + * @note Disabling the round robin preemption makes the kernel more compact + * and generally faster. + */ +#if !defined(CH_TIME_QUANTUM) || defined(__DOXYGEN__) +#define CH_TIME_QUANTUM 20 +#endif + +/** + * @brief Managed RAM size. + * @details Size of the RAM area to be managed by the OS. If set to zero + * then the whole available RAM is used. The core memory is made + * available to the heap allocator and/or can be used directly through + * the simplified core memory allocator. + * + * @note In order to let the OS manage the whole RAM the linker script must + * provide the @p __heap_base__ and @p __heap_end__ symbols. + * @note Requires @p CH_USE_MEMCORE. + */ +#if !defined(CH_MEMCORE_SIZE) || defined(__DOXYGEN__) +#define CH_MEMCORE_SIZE 0x20000 +#endif + +/** + * @brief Idle thread automatic spawn suppression. + * @details When this option is activated the function @p chSysInit() + * does not spawn the idle thread automatically. The application has + * then the responsibility to do one of the following: + * - Spawn a custom idle thread at priority @p IDLEPRIO. + * - Change the main() thread priority to @p IDLEPRIO then enter + * an endless loop. In this scenario the @p main() thread acts as + * the idle thread. + * . + * @note Unless an idle thread is spawned the @p main() thread must not + * enter a sleep state. + */ +#if !defined(CH_NO_IDLE_THREAD) || defined(__DOXYGEN__) +#define CH_NO_IDLE_THREAD FALSE +#endif + +/** @} */ + +/*===========================================================================*/ +/** + * @name Performance options + * @{ + */ +/*===========================================================================*/ + +/** + * @brief OS optimization. + * @details If enabled then time efficient rather than space efficient code + * is used when two possible implementations exist. + * + * @note This is not related to the compiler optimization options. + * @note The default is @p TRUE. + */ +#if !defined(CH_OPTIMIZE_SPEED) || defined(__DOXYGEN__) +#define CH_OPTIMIZE_SPEED TRUE +#endif + +/** @} */ + +/*===========================================================================*/ +/** + * @name Subsystem options + * @{ + */ +/*===========================================================================*/ + +/** + * @brief Threads registry APIs. + * @details If enabled then the registry APIs are included in the kernel. + * + * @note The default is @p TRUE. + */ +#if !defined(CH_USE_REGISTRY) || defined(__DOXYGEN__) +#define CH_USE_REGISTRY TRUE +#endif + +/** + * @brief Threads synchronization APIs. + * @details If enabled then the @p chThdWait() function is included in + * the kernel. + * + * @note The default is @p TRUE. + */ +#if !defined(CH_USE_WAITEXIT) || defined(__DOXYGEN__) +#define CH_USE_WAITEXIT TRUE +#endif + +/** + * @brief Semaphores APIs. + * @details If enabled then the Semaphores APIs are included in the kernel. + * + * @note The default is @p TRUE. + */ +#if !defined(CH_USE_SEMAPHORES) || defined(__DOXYGEN__) +#define CH_USE_SEMAPHORES TRUE +#endif + +/** + * @brief Semaphores queuing mode. + * @details If enabled then the threads are enqueued on semaphores by + * priority rather than in FIFO order. + * + * @note The default is @p FALSE. Enable this if you have special requirements. + * @note Requires @p CH_USE_SEMAPHORES. + */ +#if !defined(CH_USE_SEMAPHORES_PRIORITY) || defined(__DOXYGEN__) +#define CH_USE_SEMAPHORES_PRIORITY FALSE +#endif + +/** + * @brief Atomic semaphore API. + * @details If enabled then the semaphores the @p chSemSignalWait() API + * is included in the kernel. + * + * @note The default is @p TRUE. + * @note Requires @p CH_USE_SEMAPHORES. + */ +#if !defined(CH_USE_SEMSW) || defined(__DOXYGEN__) +#define CH_USE_SEMSW TRUE +#endif + +/** + * @brief Mutexes APIs. + * @details If enabled then the mutexes APIs are included in the kernel. + * + * @note The default is @p TRUE. + */ +#if !defined(CH_USE_MUTEXES) || defined(__DOXYGEN__) +#define CH_USE_MUTEXES TRUE +#endif + +/** + * @brief Conditional Variables APIs. + * @details If enabled then the conditional variables APIs are included + * in the kernel. + * + * @note The default is @p TRUE. + * @note Requires @p CH_USE_MUTEXES. + */ +#if !defined(CH_USE_CONDVARS) || defined(__DOXYGEN__) +#define CH_USE_CONDVARS TRUE +#endif + +/** + * @brief Conditional Variables APIs with timeout. + * @details If enabled then the conditional variables APIs with timeout + * specification are included in the kernel. + * + * @note The default is @p TRUE. + * @note Requires @p CH_USE_CONDVARS. + */ +#if !defined(CH_USE_CONDVARS_TIMEOUT) || defined(__DOXYGEN__) +#define CH_USE_CONDVARS_TIMEOUT TRUE +#endif + +/** + * @brief Events Flags APIs. + * @details If enabled then the event flags APIs are included in the kernel. + * + * @note The default is @p TRUE. + */ +#if !defined(CH_USE_EVENTS) || defined(__DOXYGEN__) +#define CH_USE_EVENTS TRUE +#endif + +/** + * @brief Events Flags APIs with timeout. + * @details If enabled then the events APIs with timeout specification + * are included in the kernel. + * + * @note The default is @p TRUE. + * @note Requires @p CH_USE_EVENTS. + */ +#if !defined(CH_USE_EVENTS_TIMEOUT) || defined(__DOXYGEN__) +#define CH_USE_EVENTS_TIMEOUT TRUE +#endif + +/** + * @brief Synchronous Messages APIs. + * @details If enabled then the synchronous messages APIs are included + * in the kernel. + * + * @note The default is @p TRUE. + */ +#if !defined(CH_USE_MESSAGES) || defined(__DOXYGEN__) +#define CH_USE_MESSAGES TRUE +#endif + +/** + * @brief Synchronous Messages queuing mode. + * @details If enabled then messages are served by priority rather than in + * FIFO order. + * + * @note The default is @p FALSE. Enable this if you have special requirements. + * @note Requires @p CH_USE_MESSAGES. + */ +#if !defined(CH_USE_MESSAGES_PRIORITY) || defined(__DOXYGEN__) +#define CH_USE_MESSAGES_PRIORITY FALSE +#endif + +/** + * @brief Mailboxes APIs. + * @details If enabled then the asynchronous messages (mailboxes) APIs are + * included in the kernel. + * + * @note The default is @p TRUE. + * @note Requires @p CH_USE_SEMAPHORES. + */ +#if !defined(CH_USE_MAILBOXES) || defined(__DOXYGEN__) +#define CH_USE_MAILBOXES TRUE +#endif + +/** + * @brief I/O Queues APIs. + * @details If enabled then the I/O queues APIs are included in the kernel. + * + * @note The default is @p TRUE. + */ +#if !defined(CH_USE_QUEUES) || defined(__DOXYGEN__) +#define CH_USE_QUEUES TRUE +#endif + +/** + * @brief Core Memory Manager APIs. + * @details If enabled then the core memory manager APIs are included + * in the kernel. + * + * @note The default is @p TRUE. + */ +#if !defined(CH_USE_MEMCORE) || defined(__DOXYGEN__) +#define CH_USE_MEMCORE TRUE +#endif + +/** + * @brief Heap Allocator APIs. + * @details If enabled then the memory heap allocator APIs are included + * in the kernel. + * + * @note The default is @p TRUE. + * @note Requires @p CH_USE_MEMCORE and either @p CH_USE_MUTEXES or + * @p CH_USE_SEMAPHORES. + * @note Mutexes are recommended. + */ +#if !defined(CH_USE_HEAP) || defined(__DOXYGEN__) +#define CH_USE_HEAP TRUE +#endif + +/** + * @brief C-runtime allocator. + * @details If enabled the the heap allocator APIs just wrap the C-runtime + * @p malloc() and @p free() functions. + * + * @note The default is @p FALSE. + * @note Requires @p CH_USE_HEAP. + * @note The C-runtime may or may not require @p CH_USE_MEMCORE, see the + * appropriate documentation. + */ +#if !defined(CH_USE_MALLOC_HEAP) || defined(__DOXYGEN__) +#define CH_USE_MALLOC_HEAP FALSE +#endif + +/** + * @brief Memory Pools Allocator APIs. + * @details If enabled then the memory pools allocator APIs are included + * in the kernel. + * + * @note The default is @p TRUE. + */ +#if !defined(CH_USE_MEMPOOLS) || defined(__DOXYGEN__) +#define CH_USE_MEMPOOLS TRUE +#endif + +/** + * @brief Dynamic Threads APIs. + * @details If enabled then the dynamic threads creation APIs are included + * in the kernel. + * + * @note The default is @p TRUE. + * @note Requires @p CH_USE_WAITEXIT. + * @note Requires @p CH_USE_HEAP and/or @p CH_USE_MEMPOOLS. + */ +#if !defined(CH_USE_DYNAMIC) || defined(__DOXYGEN__) +#define CH_USE_DYNAMIC TRUE +#endif + +/** @} */ + +/*===========================================================================*/ +/** + * @name Debug options + * @{ + */ +/*===========================================================================*/ + +/** + * @brief Debug option, system state check. + * @details If enabled the correct call protocol for system APIs is checked + * at runtime. + * + * @note The default is @p FALSE. + */ +#if !defined(CH_DBG_SYSTEM_STATE_CHECK) || defined(__DOXYGEN__) +#define CH_DBG_SYSTEM_STATE_CHECK TRUE +#endif + +/** + * @brief Debug option, parameters checks. + * @details If enabled then the checks on the API functions input + * parameters are activated. + * + * @note The default is @p FALSE. + */ +#if !defined(CH_DBG_ENABLE_CHECKS) || defined(__DOXYGEN__) +#define CH_DBG_ENABLE_CHECKS TRUE +#endif + +/** + * @brief Debug option, consistency checks. + * @details If enabled then all the assertions in the kernel code are + * activated. This includes consistency checks inside the kernel, + * runtime anomalies and port-defined checks. + * + * @note The default is @p FALSE. + */ +#if !defined(CH_DBG_ENABLE_ASSERTS) || defined(__DOXYGEN__) +#define CH_DBG_ENABLE_ASSERTS TRUE +#endif + +/** + * @brief Debug option, trace buffer. + * @details If enabled then the context switch circular trace buffer is + * activated. + * + * @note The default is @p FALSE. + */ +#if !defined(CH_DBG_ENABLE_TRACE) || defined(__DOXYGEN__) +#define CH_DBG_ENABLE_TRACE FALSE +#endif + +/** + * @brief Debug option, stack checks. + * @details If enabled then a runtime stack check is performed. + * + * @note The default is @p FALSE. + * @note The stack check is performed in a architecture/port dependent way. + * It may not be implemented or some ports. + * @note The default failure mode is to halt the system with the global + * @p panic_msg variable set to @p NULL. + */ +#if !defined(CH_DBG_ENABLE_STACK_CHECK) || defined(__DOXYGEN__) +#define CH_DBG_ENABLE_STACK_CHECK FALSE +#endif + +/** + * @brief Debug option, stacks initialization. + * @details If enabled then the threads working area is filled with a byte + * value when a thread is created. This can be useful for the + * runtime measurement of the used stack. + * + * @note The default is @p FALSE. + */ +#if !defined(CH_DBG_FILL_THREADS) || defined(__DOXYGEN__) +#define CH_DBG_FILL_THREADS FALSE +#endif + +/** + * @brief Debug option, threads profiling. + * @details If enabled then a field is added to the @p Thread structure that + * counts the system ticks occurred while executing the thread. + * + * @note The default is @p TRUE. + * @note This debug option is defaulted to TRUE because it is required by + * some test cases into the test suite. + */ +#if !defined(CH_DBG_THREADS_PROFILING) || defined(__DOXYGEN__) +#define CH_DBG_THREADS_PROFILING TRUE +#endif + +/** @} */ + +/*===========================================================================*/ +/** + * @name Kernel hooks + * @{ + */ +/*===========================================================================*/ + +/** + * @brief Threads descriptor structure extension. + * @details User fields added to the end of the @p Thread structure. + */ +#if !defined(THREAD_EXT_FIELDS) || defined(__DOXYGEN__) +#define THREAD_EXT_FIELDS \ + /* Add threads custom fields here.*/ +#endif + +/** + * @brief Threads initialization hook. + * @details User initialization code added to the @p chThdInit() API. + * + * @note It is invoked from within @p chThdInit() and implicitly from all + * the threads creation APIs. + */ +#if !defined(THREAD_EXT_INIT_HOOK) || defined(__DOXYGEN__) +#define THREAD_EXT_INIT_HOOK(tp) { \ + /* Add threads initialization code here.*/ \ +} +#endif + +/** + * @brief Threads finalization hook. + * @details User finalization code added to the @p chThdExit() API. + * + * @note It is inserted into lock zone. + * @note It is also invoked when the threads simply return in order to + * terminate. + */ +#if !defined(THREAD_EXT_EXIT_HOOK) || defined(__DOXYGEN__) +#define THREAD_EXT_EXIT_HOOK(tp) { \ + /* Add threads finalization code here.*/ \ +} +#endif + +/** + * @brief Context switch hook. + * @details This hook is invoked just before switching between threads. + */ +#if !defined(THREAD_CONTEXT_SWITCH_HOOK) || defined(__DOXYGEN__) +#define THREAD_CONTEXT_SWITCH_HOOK(ntp, otp) { \ + /* System halt code here.*/ \ +} +#endif + +/** + * @brief Idle Loop hook. + * @details This hook is continuously invoked by the idle thread loop. + */ +#if !defined(IDLE_LOOP_HOOK) || defined(__DOXYGEN__) +#define IDLE_LOOP_HOOK() { \ + /* Idle loop code here.*/ \ +} +#endif + +/** + * @brief System tick event hook. + * @details This hook is invoked in the system tick handler immediately + * after processing the virtual timers queue. + */ +#if !defined(SYSTEM_TICK_EVENT_HOOK) || defined(__DOXYGEN__) +#define SYSTEM_TICK_EVENT_HOOK() { \ + /* System tick event code here.*/ \ +} +#endif + + +/** + * @brief System halt hook. + * @details This hook is invoked in case to a system halting error before + * the system is halted. + */ +#if !defined(SYSTEM_HALT_HOOK) || defined(__DOXYGEN__) +#define SYSTEM_HALT_HOOK() { \ + /* System halt code here.*/ \ +} +#endif + +/** @} */ + +/*===========================================================================*/ +/* Port-specific settings (override port settings defaulted in chcore.h). */ +/*===========================================================================*/ + +#endif /* _CHCONF_H_ */ + +/** @} */ diff --git a/win32_functional_tests/clean_compile.bat b/win32_functional_tests/clean_compile.bat new file mode 100644 index 0000000000..b92a4c83e1 --- /dev/null +++ b/win32_functional_tests/clean_compile.bat @@ -0,0 +1,2 @@ +make clean +compile.bat 2> err > log \ No newline at end of file diff --git a/win32_functional_tests/compile.bat b/win32_functional_tests/compile.bat new file mode 100644 index 0000000000..dde7b05a30 --- /dev/null +++ b/win32_functional_tests/compile.bat @@ -0,0 +1,4 @@ +rem this is about CygWin colon issue, .dep files are invalid because of that +rem rm -rf .dep +rm -f build/rusefi_simulator.exe +make diff --git a/win32_functional_tests/global.h b/win32_functional_tests/global.h new file mode 100644 index 0000000000..e93992f18a --- /dev/null +++ b/win32_functional_tests/global.h @@ -0,0 +1,45 @@ + +#include +#include +#include +#include + +#ifndef GLOBAL_FT_H_ +#define GLOBAL_FT_H_ + +#define EFI_SIMULATOR TRUE + +#define EFI_SUPPORT_DODGE_NEON TRUE +#define EFI_SUPPORT_FORD_ASPIRE TRUE +#define EFI_SUPPORT_FORD_FIESTA TRUE +#define EFI_SUPPORT_NISSAN_PRIMERA TRUE +#define EFI_SUPPORT_1995_FORD_INLINE_6 TRUE + +#define EFI_SIGNAL_EXECUTOR_SLEEP TRUE + +#define EFI_WAVE_CHART TRUE + +#include "eficonsole_logic.h" +#include "rusefi_enums.h" + +#define EFI_CUSTOM_PANIC_METHOD 1 + +// project-wide default thread stack size +#define UTILITY_THREAD_STACK_SIZE 1384 + +/** + * @brief @p Win32TestStream virtual methods table. + */ +struct Win32TestStreamVMT { + _base_sequential_stream_methods +}; + +typedef struct { + const struct Win32TestStreamVMT *vmt; +} TestStream; + +extern TestStream testStream; + +//##define TRUE 1 +//#define FALSE 0 +#endif /* GLOBAL_FT_H_ */ diff --git a/win32_functional_tests/halconf.h b/win32_functional_tests/halconf.h new file mode 100644 index 0000000000..37c0489640 --- /dev/null +++ b/win32_functional_tests/halconf.h @@ -0,0 +1,312 @@ +/* + ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file templates/halconf.h + * @brief HAL configuration header. + * @details HAL configuration file, this file allows to enable or disable the + * various device drivers from your application. You may also use + * this file in order to override the device drivers default settings. + * + * @addtogroup HAL_CONF + * @{ + */ + +#ifndef _HALCONF_H_ +#define _HALCONF_H_ + +/*#include "mcuconf.h"*/ + +/** + * @brief Enables the TM subsystem. + */ +#if !defined(HAL_USE_TM) || defined(__DOXYGEN__) +#define HAL_USE_TM FALSE +#endif + +/** + * @brief Enables the PAL subsystem. + */ +#if !defined(HAL_USE_PAL) || defined(__DOXYGEN__) +#define HAL_USE_PAL TRUE +#endif + +/** + * @brief Enables the ADC subsystem. + */ +#if !defined(HAL_USE_ADC) || defined(__DOXYGEN__) +#define HAL_USE_ADC FALSE +#endif + +/** + * @brief Enables the CAN subsystem. + */ +#if !defined(HAL_USE_CAN) || defined(__DOXYGEN__) +#define HAL_USE_CAN FALSE +#endif + +/** + * @brief Enables the EXT subsystem. + */ +#if !defined(HAL_USE_EXT) || defined(__DOXYGEN__) +#define HAL_USE_EXT FALSE +#endif + +/** + * @brief Enables the GPT subsystem. + */ +#if !defined(HAL_USE_GPT) || defined(__DOXYGEN__) +#define HAL_USE_GPT FALSE +#endif + +/** + * @brief Enables the I2C subsystem. + */ +#if !defined(HAL_USE_I2C) || defined(__DOXYGEN__) +#define HAL_USE_I2C FALSE +#endif + +/** + * @brief Enables the ICU subsystem. + */ +#if !defined(HAL_USE_ICU) || defined(__DOXYGEN__) +#define HAL_USE_ICU FALSE +#endif + +/** + * @brief Enables the MAC subsystem. + */ +#if !defined(HAL_USE_MAC) || defined(__DOXYGEN__) +#define HAL_USE_MAC FALSE +#endif + +/** + * @brief Enables the MMC_SPI subsystem. + */ +#if !defined(HAL_USE_MMC_SPI) || defined(__DOXYGEN__) +#define HAL_USE_MMC_SPI FALSE +#endif + +/** + * @brief Enables the PWM subsystem. + */ +#if !defined(HAL_USE_PWM) || defined(__DOXYGEN__) +#define HAL_USE_PWM FALSE +#endif + +/** + * @brief Enables the RTC subsystem. + */ +#if !defined(HAL_USE_RTC) || defined(__DOXYGEN__) +#define HAL_USE_RTC FALSE +#endif + +/** + * @brief Enables the SDC subsystem. + */ +#if !defined(HAL_USE_SDC) || defined(__DOXYGEN__) +#define HAL_USE_SDC FALSE +#endif + +/** + * @brief Enables the SERIAL subsystem. + */ +#if !defined(HAL_USE_SERIAL) || defined(__DOXYGEN__) +#define HAL_USE_SERIAL TRUE +#endif + +/** + * @brief Enables the SERIAL over USB subsystem. + */ +#if !defined(HAL_USE_SERIAL_USB) || defined(__DOXYGEN__) +#define HAL_USE_SERIAL_USB FALSE +#endif + +/** + * @brief Enables the SPI subsystem. + */ +#if !defined(HAL_USE_SPI) || defined(__DOXYGEN__) +#define HAL_USE_SPI FALSE +#endif + +/** + * @brief Enables the UART subsystem. + */ +#if !defined(HAL_USE_UART) || defined(__DOXYGEN__) +#define HAL_USE_UART FALSE +#endif + +/** + * @brief Enables the USB subsystem. + */ +#if !defined(HAL_USE_USB) || defined(__DOXYGEN__) +#define HAL_USE_USB FALSE +#endif + +/*===========================================================================*/ +/* ADC driver related settings. */ +/*===========================================================================*/ + +/** + * @brief Enables synchronous APIs. + * @note Disabling this option saves both code and data space. + */ +#if !defined(ADC_USE_WAIT) || defined(__DOXYGEN__) +#define ADC_USE_WAIT TRUE +#endif + +/** + * @brief Enables the @p adcAcquireBus() and @p adcReleaseBus() APIs. + * @note Disabling this option saves both code and data space. + */ +#if !defined(ADC_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__) +#define ADC_USE_MUTUAL_EXCLUSION TRUE +#endif + +/*===========================================================================*/ +/* CAN driver related settings. */ +/*===========================================================================*/ + +/** + * @brief Sleep mode related APIs inclusion switch. + */ +#if !defined(CAN_USE_SLEEP_MODE) || defined(__DOXYGEN__) +#define CAN_USE_SLEEP_MODE TRUE +#endif + +/*===========================================================================*/ +/* I2C driver related settings. */ +/*===========================================================================*/ + +/** + * @brief Enables the mutual exclusion APIs on the I2C bus. + */ +#if !defined(I2C_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__) +#define I2C_USE_MUTUAL_EXCLUSION TRUE +#endif + +/*===========================================================================*/ +/* MAC driver related settings. */ +/*===========================================================================*/ + +/** + * @brief Enables an event sources for incoming packets. + */ +#if !defined(MAC_USE_ZERO_COPY) || defined(__DOXYGEN__) +#define MAC_USE_ZERO_COPY FALSE +#endif + +/** + * @brief Enables an event sources for incoming packets. + */ +#if !defined(MAC_USE_EVENTS) || defined(__DOXYGEN__) +#define MAC_USE_EVENTS TRUE +#endif + +/*===========================================================================*/ +/* MMC_SPI driver related settings. */ +/*===========================================================================*/ + +/** + * @brief Delays insertions. + * @details If enabled this options inserts delays into the MMC waiting + * routines releasing some extra CPU time for the threads with + * lower priority, this may slow down the driver a bit however. + * This option is recommended also if the SPI driver does not + * use a DMA channel and heavily loads the CPU. + */ +#if !defined(MMC_NICE_WAITING) || defined(__DOXYGEN__) +#define MMC_NICE_WAITING TRUE +#endif + +/*===========================================================================*/ +/* SDC driver related settings. */ +/*===========================================================================*/ + +/** + * @brief Number of initialization attempts before rejecting the card. + * @note Attempts are performed at 10mS intervals. + */ +#if !defined(SDC_INIT_RETRY) || defined(__DOXYGEN__) +#define SDC_INIT_RETRY 100 +#endif + +/** + * @brief Include support for MMC cards. + * @note MMC support is not yet implemented so this option must be kept + * at @p FALSE. + */ +#if !defined(SDC_MMC_SUPPORT) || defined(__DOXYGEN__) +#define SDC_MMC_SUPPORT FALSE +#endif + +/** + * @brief Delays insertions. + * @details If enabled this options inserts delays into the MMC waiting + * routines releasing some extra CPU time for the threads with + * lower priority, this may slow down the driver a bit however. + */ +#if !defined(SDC_NICE_WAITING) || defined(__DOXYGEN__) +#define SDC_NICE_WAITING TRUE +#endif + +/*===========================================================================*/ +/* SERIAL driver related settings. */ +/*===========================================================================*/ + +/** + * @brief Default bit rate. + * @details Configuration parameter, this is the baud rate selected for the + * default configuration. + */ +#if !defined(SERIAL_DEFAULT_BITRATE) || defined(__DOXYGEN__) +#define SERIAL_DEFAULT_BITRATE 38400 +#endif + +/** + * @brief Serial buffers size. + * @details Configuration parameter, you can change the depth of the queue + * buffers depending on the requirements of your application. + * @note The default is 64 bytes for both the transmission and receive + * buffers. + */ +#if !defined(SERIAL_BUFFERS_SIZE) || defined(__DOXYGEN__) +#define SERIAL_BUFFERS_SIZE 32 +#endif + +/*===========================================================================*/ +/* SPI driver related settings. */ +/*===========================================================================*/ + +/** + * @brief Enables synchronous APIs. + * @note Disabling this option saves both code and data space. + */ +#if !defined(SPI_USE_WAIT) || defined(__DOXYGEN__) +#define SPI_USE_WAIT TRUE +#endif + +/** + * @brief Enables the @p spiAcquireBus() and @p spiReleaseBus() APIs. + * @note Disabling this option saves both code and data space. + */ +#if !defined(SPI_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__) +#define SPI_USE_MUTUAL_EXCLUSION TRUE +#endif + +#endif /* _HALCONF_H_ */ + +/** @} */ diff --git a/win32_functional_tests/inc_fix.bat b/win32_functional_tests/inc_fix.bat new file mode 100644 index 0000000000..9020b6e3df --- /dev/null +++ b/win32_functional_tests/inc_fix.bat @@ -0,0 +1,6 @@ +rm build/rusefi_simulator.exe +make + +cd build +rusefi_simulator.exe + diff --git a/win32_functional_tests/main.c b/win32_functional_tests/main.c new file mode 100644 index 0000000000..880904d8c7 --- /dev/null +++ b/win32_functional_tests/main.c @@ -0,0 +1,189 @@ +/* + ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#include "main.h" +#include "chprintf.h" +#include "rusEfiFunctionalTest.h" + +#define SHELL_WA_SIZE THD_WA_SIZE(4096) +#define CONSOLE_WA_SIZE THD_WA_SIZE(4096) + +static Thread *cdtp; +//static Thread *shelltp1; +//static Thread *shelltp2; + +#define cputs(msg) chMsgSend(cdtp, (msg_t)msg) + +void printToWin32Console(char *p) { + cputs(p); +} + +TestStream testStream; + +/* + * Console print server done using synchronous messages. This makes the access + * to the C printf() thread safe and the print operation atomic among threads. + * In this example the message is the zero terminated string itself. + */ +static msg_t console_thread(void *arg) { + + (void) arg; + while (!chThdShouldTerminate()) { + Thread *tp = chMsgWait(); + puts((char *) chMsgGet(tp)); + fflush(stdout); + chMsgRelease(tp, RDY_OK); + } + return 0; +} + +extern int isSerialOverTcpReady; + +/** + * @brief Shell termination handler. + * + * @param[in] id event id. + */ +static void termination_handler(eventid_t id) { + + chThdSleepMilliseconds(10); + + cputs("Init: shell on SD1 terminated"); + chSysLock(); + chOQResetI(&SD1.oqueue); + chSysUnlock(); + +// if (shelltp2 && chThdTerminated(shelltp2)) { +// chThdWait(shelltp2); +// shelltp2 = NULL; +// chThdSleepMilliseconds(10); +// cputs("Init: shell on SD2 terminated"); +// chSysLock(); +// chOQResetI(&SD2.oqueue); +// chSysUnlock(); +// } +} + +static EventListener sd1fel, sd2fel; + +/** + * @brief SD1 status change handler. + * + * @param[in] id event id. + */ +static void sd1_handler(eventid_t id) { + flagsmask_t flags; + + (void) id; + flags = chEvtGetAndClearFlags(&sd1fel); + if ((flags & CHN_CONNECTED)) { + cputs("Init: connection on SD1"); + isSerialOverTcpReady = TRUE; + + } + if (flags & CHN_DISCONNECTED) { + cputs("Init: disconnection on SD1"); + isSerialOverTcpReady = FALSE; + chSysLock(); + chIQResetI(&SD1.iqueue); + chSysUnlock(); + } +} + +/** + * @brief SD2 status change handler. + * + * @param[in] id event id. + */ +static void sd2_handler(eventid_t id) { + flagsmask_t flags; + + (void) id; + flags = chEvtGetAndClearFlags(&sd2fel); + if ((flags & CHN_CONNECTED)) { + cputs("Init: connection on SD2"); + } + if (flags & CHN_DISCONNECTED) { + cputs("Init: disconnection on SD2"); + chSysLock(); + chIQResetI(&SD2.iqueue); + chSysUnlock(); + } +} + +static evhandler_t fhandlers[] = { termination_handler, sd1_handler, sd2_handler }; + +/*------------------------------------------------------------------------* + * Simulator main. * + *------------------------------------------------------------------------*/ +int main(void) { + + initTestStream(&testStream); + + EventListener tel; + + /* + * System initializations. + * - HAL initialization, this also initializes the configured device drivers + * and performs the board-specific initializations. + * - Kernel initialization, the main() function becomes a thread and the + * RTOS is active. + */ + halInit(); + chSysInit(); + + /* + * Serial ports (simulated) initialization. + */ + sdStart(&SD1, NULL); + sdStart(&SD2, NULL); + + /* + * Console thread started. + */ + cdtp = chThdCreateFromHeap(NULL, CONSOLE_WA_SIZE, NORMALPRIO + 1, console_thread, NULL); + + /* + * Initializing connection/disconnection events. + */ + cputs("Shell service started on SD1, SD2"); + cputs(" - Listening for connections on SD1"); + chEvtRegister(chnGetEventSource(&SD1), &sd1fel, 1); + cputs(" - Listening for connections on SD2"); + chEvtRegister(chnGetEventSource(&SD2), &sd2fel, 2); + + rusEfiFunctionalTest(); + + /* + * Events servicing loop. + */ + while (!chThdShouldTerminate()) { + chEvtDispatch(fhandlers, chEvtWaitOne(ALL_EVENTS)); + printPendingMessages(); + chThdSleepMilliseconds(100); + } + + /* + * Clean simulator exit. + */ + chEvtUnregister(chnGetEventSource(&SD1), &sd1fel); + chEvtUnregister(chnGetEventSource(&SD2), &sd2fel); + return 0; +} + +int systicks2ms(int systicks) { + return systicks / TICKS_IN_MS; +} diff --git a/win32_functional_tests/main.h b/win32_functional_tests/main.h new file mode 100644 index 0000000000..e00280b6da --- /dev/null +++ b/win32_functional_tests/main.h @@ -0,0 +1,31 @@ + +#include "global.h" +#include "boards.h" +#include "eficonsole.h" +#include "efilib.h" +#include "datalogging.h" +#include "error_handling.h" + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +void printToWin32Console(char *p); +int systicks2ms(int systicks); + +// todo: move somewhere else? +void lockAnyContext(void); +void unlockAnyContext(void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +/** + * number of SysClock ticks in one ms + */ +#define TICKS_IN_MS (CH_FREQUENCY / 1000) + +#define hal_lld_get_counter_value() 0 diff --git a/win32_functional_tests/rules.mk b/win32_functional_tests/rules.mk new file mode 100644 index 0000000000..5e93b3e311 --- /dev/null +++ b/win32_functional_tests/rules.mk @@ -0,0 +1,168 @@ +# ARM Cortex-Mx common makefile scripts and rules. + +# Output directory and files +ifeq ($(BUILDDIR),) + BUILDDIR = build +endif +ifeq ($(BUILDDIR),.) + BUILDDIR = build +endif +OUTFILES = $(BUILDDIR)/$(PROJECT) + +# Automatic compiler options +OPT = $(USE_OPT) +COPT = $(USE_COPT) +CPPOPT = $(USE_CPPOPT) +ifeq ($(USE_LINK_GC),yes) + OPT += -ffunction-sections -fdata-sections -fno-common +endif + +# Source files groups and paths +ifeq ($(USE_THUMB),yes) + TCSRC += $(CSRC) + TCPPSRC += $(CPPSRC) +else + ACSRC += $(CSRC) + ACPPSRC += $(CPPSRC) +endif +ASRC = $(ACSRC)$(ACPPSRC) +TSRC = $(TCSRC)$(TCPPSRC) +SRCPATHS = $(sort $(dir $(ASMXSRC)) $(dir $(ASMSRC)) $(dir $(ASRC)) $(dir $(TSRC))) + +# Various directories +OBJDIR = $(BUILDDIR)/obj +LSTDIR = $(BUILDDIR)/lst + +# Object files groups +ACOBJS = $(addprefix $(OBJDIR)/, $(notdir $(ACSRC:.c=.o))) +ACPPOBJS = $(addprefix $(OBJDIR)/, $(notdir $(ACPPSRC:.cpp=.o))) +TCOBJS = $(addprefix $(OBJDIR)/, $(notdir $(TCSRC:.c=.o))) +TCPPOBJS = $(addprefix $(OBJDIR)/, $(notdir $(TCPPSRC:.cpp=.o))) +ASMOBJS = $(addprefix $(OBJDIR)/, $(notdir $(ASMSRC:.s=.o))) +ASMXOBJS = $(addprefix $(OBJDIR)/, $(notdir $(ASMXSRC:.S=.o))) +OBJS = $(ASMXOBJS) $(ASMOBJS) $(ACOBJS) $(TCOBJS) $(ACPPOBJS) $(TCPPOBJS) + +# Paths +IINCDIR = $(patsubst %,-I%,$(INCDIR) $(DINCDIR) $(UINCDIR)) +LLIBDIR = $(patsubst %,-L%,$(DLIBDIR) $(ULIBDIR)) + +# Macros +DEFS = $(DDEFS) $(UDEFS) +ADEFS = $(DADEFS) $(UADEFS) + +# Libs +LIBS = $(DLIBS) $(ULIBS) + +# Various settings +#MCFLAGS = -mcpu=$(MCU) +ODFLAGS = -x --syms +ASFLAGS = $(MCFLAGS) -Wa,-amhls=$(LSTDIR)/$(notdir $(<:.s=.lst)) $(ADEFS) +ASXFLAGS = $(MCFLAGS) -Wa,-amhls=$(LSTDIR)/$(notdir $(<:.S=.lst)) $(ADEFS) +CFLAGS = $(MCFLAGS) $(OPT) $(COPT) $(CWARN) -Wa,-alms=$(LSTDIR)/$(notdir $(<:.c=.lst)) $(DEFS) +CPPFLAGS = $(MCFLAGS) $(OPT) $(CPPOPT) $(CPPWARN) -Wa,-alms=$(LSTDIR)/$(notdir $(<:.cpp=.lst)) $(DEFS) +ifeq ($(USE_LINK_GC),yes) + LDFLAGS = $(MCFLAGS) -Wl,-Map=$(BUILDDIR)/$(PROJECT).map,--cref,--no-warn-mismatch,--gc-sections $(LLIBDIR) +else + LDFLAGS = $(MCFLAGS) -Wl,-Map=$(BUILDDIR)/$(PROJECT).map,--cref,--no-warn-mismatch $(LLIBDIR) +endif + +# Generate dependency information +CFLAGS += -MD -MP -MF .dep/$(@F).d +CPPFLAGS += -MD -MP -MF .dep/$(@F).d + +# Paths where to search for sources +VPATH = $(SRCPATHS) + +# +# Makefile rules +# + +all: $(OBJS) $(OUTFILES) MAKE_ALL_RULE_HOOK + +MAKE_ALL_RULE_HOOK: + +$(OBJS): | $(BUILDDIR) + +$(BUILDDIR) $(OBJDIR) $(LSTDIR): +ifneq ($(USE_VERBOSE_COMPILE),yes) + @echo Compiler Options + @echo $(CPPC) -c $(CPPFLAGS) -I. $(IINCDIR) main.cpp -o main.o + @echo +endif + mkdir -p $(OBJDIR) + mkdir -p $(LSTDIR) + +$(ACPPOBJS) : $(OBJDIR)/%.o : %.cpp Makefile +ifeq ($(USE_VERBOSE_COMPILE),yes) + @echo + $(CPPC) -c $(CPPFLAGS) $(AOPT) -I. $(IINCDIR) $< -o $@ +else + @echo Compiling $(/dev/null) $(wildcard .dep/*) + +# *** EOF *** diff --git a/win32_functional_tests/simulator/adc_inputs.h b/win32_functional_tests/simulator/adc_inputs.h new file mode 100644 index 0000000000..b201c63ddb --- /dev/null +++ b/win32_functional_tests/simulator/adc_inputs.h @@ -0,0 +1,13 @@ +/** + * @file adc_inputs.h + * + * @date Dec 7, 2013 + * @author Andrey Belomutskiy, (c) 2012-2013 + */ + +#ifndef ADC_INPUTS_H_ +#define ADC_INPUTS_H_ + +#include "adc_math.h" + +#endif /* ADC_INPUTS_H_ */ diff --git a/win32_functional_tests/simulator/boards.c b/win32_functional_tests/simulator/boards.c new file mode 100644 index 0000000000..f5a53754d3 --- /dev/null +++ b/win32_functional_tests/simulator/boards.c @@ -0,0 +1,57 @@ +/** + * @file board.c + * + * @date Nov 15, 2013 + * @author Andrey Belomutskiy, (c) 2012-2013 + */ + +#include "main.h" +#include "boards.h" +#include "engine_configuration.h" +#include "adc_math.h" + +static Logging logger; +extern engine_configuration_s *engineConfiguration; + + +//float getVoltageDivided(int channel) { +// return 0; +//} +// +static float fakeAdcValues[16]; + +int getAdcValue(int hwChannel) { + return fakeAdcValues[hwChannel]; +} + +static void setVoltage(int hwChannel, float voltage) { + scheduleMsg(&logger, "fake voltage: channel %d value %f", hwChannel, voltage); + fakeAdcValues[hwChannel] = voltsToAdc(voltage); +} + +static void setCltVoltage(float voltage) { + setVoltage(engineConfiguration->cltAdcChannel, voltage); +} + +static void setIatVoltage(float voltage) { + setVoltage(engineConfiguration->iatAdcChannel, voltage); +} + +static void setMafVoltage(float voltage) { + setVoltage(engineConfiguration->mafAdcChannel, voltage); +} + +static void setAfrVoltage(float voltage) { + setVoltage(engineConfiguration->afrSensor.afrAdcChannel, voltage); +} + +void initFakeBoard(void) { + initLogging(&logger, "simulator board"); + + addConsoleActionF("set_fake_clt_voltage", setCltVoltage); + addConsoleActionF("set_fake_iat_voltage", setIatVoltage); + addConsoleActionF("set_fake_maf_voltage", setMafVoltage); + addConsoleActionF("set_fake_afr_voltage", setAfrVoltage); +} + + diff --git a/win32_functional_tests/simulator/boards.h b/win32_functional_tests/simulator/boards.h new file mode 100644 index 0000000000..b581b1ab2f --- /dev/null +++ b/win32_functional_tests/simulator/boards.h @@ -0,0 +1,23 @@ + +#ifndef BOARDS_FT_H_ +#define BOARDS_FT_H_ + + +#define ADC_LOGIC_TPS 0 +#define ADC_LOGIC_AFR 0 +#define ADC_LOGIC_MAF 0 +#define ADC_LOGIC_MAP 0 +#define ADC_CHANNEL_VREF 0 +#define ADC_CHANNEL_VBATT 0 +#define ADC_LOGIC_INTAKE_AIR 0 +#define ADC_LOGIC_COOLANT 0 + +#define DELEGATE (&SD1) + +#define EFI_CONSOLE_UART_DEVICE (&testStream) + +//float getVoltageDivided(int); +int getAdcValue(int channel); +void initFakeBoard(void); + +#endif /* BOARDS_FT_H_ */ diff --git a/win32_functional_tests/simulator/efifeatures.h b/win32_functional_tests/simulator/efifeatures.h new file mode 100644 index 0000000000..86a840af11 --- /dev/null +++ b/win32_functional_tests/simulator/efifeatures.h @@ -0,0 +1,13 @@ +/* + * efifeatures.h + * + * Created on: Mar 7, 2014 + * Author: Andrey + */ + +#ifndef EFIFEATURES_H_ +#define EFIFEATURES_H_ + + + +#endif /* EFIFEATURES_H_ */ diff --git a/win32_functional_tests/simulator/idle_thread.h b/win32_functional_tests/simulator/idle_thread.h new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/win32_functional_tests/simulator/idle_thread.h @@ -0,0 +1 @@ + diff --git a/win32_functional_tests/simulator/lcd_2x16.h b/win32_functional_tests/simulator/lcd_2x16.h new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/win32_functional_tests/simulator/lcd_2x16.h @@ -0,0 +1 @@ + diff --git a/win32_functional_tests/simulator/mmc_card.h b/win32_functional_tests/simulator/mmc_card.h new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/win32_functional_tests/simulator/mmc_card.h @@ -0,0 +1 @@ + diff --git a/win32_functional_tests/simulator/rusEfiFunctionalTest.c b/win32_functional_tests/simulator/rusEfiFunctionalTest.c new file mode 100644 index 0000000000..8de87a43f5 --- /dev/null +++ b/win32_functional_tests/simulator/rusEfiFunctionalTest.c @@ -0,0 +1,163 @@ +/** + * @file rusEfiFunctionalTest.c + * + * @date Mar 1, 2014 + * @author Andrey Belomutskiy, (c) 2012-2013 + */ + +#include "global.h" +#include "rusEfiFunctionalTest.h" +#include "eficonsole.h" +#include "engine_configuration.h" +#include "rusefi_enums.h" +#include "pwm_generator_logic.h" +#include "wave_math.h" +#include "boards.h" +#include "trigger_central.h" +#include "datalogging.h" +#include "algo.h" +#include "rpm_calculator.h" +#include "wave_chart.h" +#include "status_loop.h" +#include "trigger_emulator_algo.h" +#include "main_trigger_callback.h" +#include "allsensors.h" + + +extern WaveChart waveChart; + +static engine_configuration_s ec; +static engine_configuration2_s ec2; + +engine_configuration_s * engineConfiguration = &ec; +engine_configuration2_s *engineConfiguration2 = &ec2; + +void setOutputPinValue(io_pin_e pin, int logicValue) { +} + +int isInjectionEnabled(void) { + return TRUE; +} + + +//void initOutputSignal(OutputSignal *signal, io_pin_e ioPin) { +// +//} + +void idleDebug(char *msg, int value) { + +} + +float getMap(void) { + return 0; +} + +static int primaryWheelState = FALSE; +static int secondaryWheelState = FALSE; + +static void triggerEmulatorCallback(PwmConfig *state, int stateIndex) { + int newPrimaryWheelState = state->multiWave.waves[0].pinStates[stateIndex]; + int newSecondaryWheelState = state->multiWave.waves[1].pinStates[stateIndex]; + + if (primaryWheelState != newPrimaryWheelState) { + primaryWheelState = newPrimaryWheelState; + hwHandleShaftSignal(primaryWheelState ? SHAFT_PRIMARY_UP : SHAFT_PRIMARY_DOWN); + } + + if (secondaryWheelState != newSecondaryWheelState) { + secondaryWheelState = newSecondaryWheelState; + hwHandleShaftSignal(secondaryWheelState ? SHAFT_SECONDARY_UP : SHAFT_SECONDARY_DOWN); + } + +// print("hello %d\r\n", chTimeNow()); +} + +void rusEfiFunctionalTest(void) { + initializeConsole(); + + initFakeBoard(); + + initStatusLoop(); + + resetConfigurationExt(FORD_ASPIRE_1996, engineConfiguration, engineConfiguration2); + + initThermistors(); + initAlgo(); + initRpmCalculator(); + + initTriggerEmulatorLogic(triggerEmulatorCallback); + + initMainEventListener(); + + initTriggerCentral(); + +} + +void printPendingMessages(void) { + printPending(); + if (getFullLog()) { + printSensors(); + finishStatusLine(); + publishChartIfFull(&waveChart); + } +} + +static size_t wt_writes(void *ip, const uint8_t *bp, size_t n) { + printToWin32Console((char*)bp); + return DELEGATE->vmt->write(DELEGATE, bp, n); +} + +static size_t wt_reads(void *ip, uint8_t *bp, size_t n) { + return DELEGATE->vmt->read(DELEGATE, bp, n); +} + +static char putMessageBuffer[2]; + +static msg_t wt_put(void *ip, uint8_t b) { + putMessageBuffer[0] = b; + putMessageBuffer[1] = 0; + printToWin32Console((char*)putMessageBuffer); +// cputs("wt_put"); + return DELEGATE->vmt->put(DELEGATE, b); +} + +static msg_t wt_get(void *ip) { +// cputs("wt_get"); + //return 0; + return DELEGATE->vmt->get(DELEGATE); +} + +static const struct Win32TestStreamVMT vmt = { wt_writes, wt_reads, wt_put, wt_get }; + +void initTestStream(TestStream *ts) { + ts->vmt = &vmt; +} + +int isSerialOverTcpReady; + +int is_serial_ready(void) { + return isSerialOverTcpReady; +} + +void onFatalError(const char *msg, char * file, int line) { + printf("onFatalError %s %s%d", msg, file, line); + exit(-1); +} + +int warning(const char *fmt, ...) { + printf("Warning: %s\r\n", fmt); + return 0; +} + + +void firmwareError(const char *fmt, ...) { + fatal3((char*)fmt, __FILE__, __LINE__); +} + +int hasFatalError(void) { + return false; +} + +int getVersion(void) { + return 239; +} diff --git a/win32_functional_tests/simulator/rusEfiFunctionalTest.h b/win32_functional_tests/simulator/rusEfiFunctionalTest.h new file mode 100644 index 0000000000..7679f5dc2d --- /dev/null +++ b/win32_functional_tests/simulator/rusEfiFunctionalTest.h @@ -0,0 +1,15 @@ +/** + * @file rusEfiFunctionalTest.h + * + * @date Mar 1, 2014 + * @author Andrey Belomutskiy, (c) 2012-2013 + */ + +#ifndef RUSEFIFUNCTIONALTEST_H_ +#define RUSEFIFUNCTIONALTEST_H_ + +void rusEfiFunctionalTest(void); +void initTestStream(TestStream *ts); +void printPendingMessages(void); + +#endif /* RUSEFIFUNCTIONALTEST_H_ */ diff --git a/win32_functional_tests/simulator/rusefi.h b/win32_functional_tests/simulator/rusefi.h new file mode 100644 index 0000000000..ca65535f63 --- /dev/null +++ b/win32_functional_tests/simulator/rusefi.h @@ -0,0 +1,2 @@ + +int getVersion(void);