From bde6b65c9a29738567eb4fc9bc94c85311203fdc Mon Sep 17 00:00:00 2001 From: rusefi Date: Wed, 14 Aug 2019 22:40:12 -0400 Subject: [PATCH] rusEfi console usability: auto-detect serial port --- .../.idea/runConfigurations/Launcher_Auto.xml | 10 ++++ java_console/ui/src/com/rusefi/Launcher.java | 24 +++++++- .../ui/src/com/rusefi/StartupFrame.java | 18 +++++- .../com/rusefi/autodetect/PortDetector.java | 49 ++++++++++++++++ .../rusefi/autodetect/SerialAutoChecker.java | 56 +++++++++++++++++++ 5 files changed, 152 insertions(+), 5 deletions(-) create mode 100644 java_console/.idea/runConfigurations/Launcher_Auto.xml create mode 100644 java_console/ui/src/com/rusefi/autodetect/PortDetector.java create mode 100644 java_console/ui/src/com/rusefi/autodetect/SerialAutoChecker.java diff --git a/java_console/.idea/runConfigurations/Launcher_Auto.xml b/java_console/.idea/runConfigurations/Launcher_Auto.xml new file mode 100644 index 0000000000..c46a03e234 --- /dev/null +++ b/java_console/.idea/runConfigurations/Launcher_Auto.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/java_console/ui/src/com/rusefi/Launcher.java b/java_console/ui/src/com/rusefi/Launcher.java index dd933b2987..5b18329197 100644 --- a/java_console/ui/src/com/rusefi/Launcher.java +++ b/java_console/ui/src/com/rusefi/Launcher.java @@ -1,6 +1,7 @@ package com.rusefi; import com.fathzer.soft.javaluator.DoubleEvaluator; +import com.rusefi.autodetect.PortDetector; import com.rusefi.binaryprotocol.BinaryProtocol; import com.rusefi.binaryprotocol.BinaryProtocolHolder; import com.rusefi.config.generated.Fields; @@ -47,7 +48,7 @@ import static com.rusefi.ui.storage.PersistentConfiguration.getConfig; * @see EngineSnifferPanel */ public class Launcher { - public static final int CONSOLE_VERSION = 20190809; + public static final int CONSOLE_VERSION = 20190814; public static final String INPUT_FILES_PATH = ".."; private static final String TAB_INDEX = "main_tab"; protected static final String PORT_KEY = "port"; @@ -301,6 +302,10 @@ public class Launcher { System.exit(0); } + /** + * rusEfi console entry point + * @see StartupFrame if no parameters specified + */ public static void main(final String[] args) throws Exception { String toolName = args.length == 0 ? null : args[0]; if (TOOL_NAME_COMPILE_FSIO_FILE.equalsIgnoreCase(toolName)) { @@ -364,8 +369,23 @@ public class Launcher { boolean isBaudRateDefined = args.length > 1; if (isBaudRateDefined) PortHolder.BAUD_RATE = Integer.parseInt(args[1]); + + String port = null; + if (isPortDefined) + port = args[0]; + + + if (isPortDefined && port.toLowerCase().startsWith("auto")) { + String autoDetectedPort = PortDetector.autoDetectSerial(); + if (autoDetectedPort == null) { + isPortDefined = false; + } else { + port = autoDetectedPort; + } + } + if (isPortDefined) { - new Launcher(args[0]); + new Launcher(port); } else { for (String p : SerialPortList.getPortNames()) MessagesCentral.getInstance().postMessage(Launcher.class, "Available port: " + p); diff --git a/java_console/ui/src/com/rusefi/StartupFrame.java b/java_console/ui/src/com/rusefi/StartupFrame.java index 91a25536a6..45435d2921 100644 --- a/java_console/ui/src/com/rusefi/StartupFrame.java +++ b/java_console/ui/src/com/rusefi/StartupFrame.java @@ -1,5 +1,6 @@ package com.rusefi; +import com.rusefi.autodetect.PortDetector; import com.rusefi.io.LinkManager; import com.rusefi.io.serial.PortHolder; import com.rusefi.io.tcp.TcpConnector; @@ -45,6 +46,7 @@ public class StartupFrame { private static final String LOGO = "logo.gif"; public static final String LINK_TEXT = "rusEfi (c) 2012-2019"; private static final String URI = "http://rusefi.com/?java_console"; + private static final String AUTO_SERIAL = "Auto Serial"; private final JFrame frame; private final Timer scanPortsTimes = new Timer(1000, new ActionListener() { @@ -122,9 +124,16 @@ public class StartupFrame { connect.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - disposeFrameAndProceed(); PortHolder.BAUD_RATE = Integer.parseInt((String) comboSpeeds.getSelectedItem()); - new Launcher(comboPorts.getSelectedItem().toString()); + String selectedPort = comboPorts.getSelectedItem().toString(); + if (AUTO_SERIAL.equals(selectedPort)) { + String autoDetectedPort = PortDetector.autoDetectPort(StartupFrame.this.frame); + if (autoDetectedPort == null) + return; + selectedPort = autoDetectedPort; + } + disposeFrameAndProceed(); + new Launcher(selectedPort); } }); @@ -230,7 +239,10 @@ public class StartupFrame { @NotNull private List findAllAvailablePorts() { List ports = new ArrayList<>(); - ports.addAll(Arrays.asList(SerialPortList.getPortNames())); + String[] serialPorts = SerialPortList.getPortNames(); + if (serialPorts.length > 0 || serialPorts.length < 15) + ports.add(AUTO_SERIAL); + ports.addAll(Arrays.asList(serialPorts)); ports.addAll(TcpConnector.getAvailablePorts()); return ports; } diff --git a/java_console/ui/src/com/rusefi/autodetect/PortDetector.java b/java_console/ui/src/com/rusefi/autodetect/PortDetector.java new file mode 100644 index 0000000000..224564a1cb --- /dev/null +++ b/java_console/ui/src/com/rusefi/autodetect/PortDetector.java @@ -0,0 +1,49 @@ +package com.rusefi.autodetect; + +import jssc.SerialPortList; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; + +/** + * (c) Andrey Belomutskiy 2013-2019 + */ +public class PortDetector { + /** + * Connect to all serial ports and find out which one respond first + */ + public static String autoDetectSerial() { + String[] serialPorts = SerialPortList.getPortNames(); + List serialFinder = new ArrayList<>(); + CountDownLatch portFound = new CountDownLatch(1); + AtomicReference result = new AtomicReference<>(); + for (String serialPort : serialPorts) { + Thread thread = new Thread(new SerialAutoChecker(serialPort, portFound, result)); + serialFinder.add(thread); + thread.start(); + } + try { + portFound.await(5, TimeUnit.SECONDS); + } catch (InterruptedException e) { + throw new IllegalStateException(e); + } + for (Thread thread : serialFinder) + thread.interrupt(); + return result.get(); + } + + @Nullable + public static String autoDetectPort(JFrame parent) { + String autoDetectedPort = autoDetectSerial(); + if (autoDetectedPort == null) { + JOptionPane.showMessageDialog(parent, "Failed to located device"); + return null; + } + return autoDetectedPort; + } +} diff --git a/java_console/ui/src/com/rusefi/autodetect/SerialAutoChecker.java b/java_console/ui/src/com/rusefi/autodetect/SerialAutoChecker.java new file mode 100644 index 0000000000..36e71fd148 --- /dev/null +++ b/java_console/ui/src/com/rusefi/autodetect/SerialAutoChecker.java @@ -0,0 +1,56 @@ +package com.rusefi.autodetect; + +import com.opensr5.Logger; +import com.rusefi.FileLog; +import com.rusefi.binaryprotocol.BinaryProtocol; +import com.rusefi.binaryprotocol.BinaryProtocolCommands; +import com.rusefi.binaryprotocol.IncomingDataBuffer; +import com.rusefi.config.generated.Fields; +import com.rusefi.io.IoStream; +import com.rusefi.io.serial.PortHolder; + +import java.io.IOException; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicReference; + +import static com.rusefi.binaryprotocol.IoHelper.checkResponseCode; + +class SerialAutoChecker implements Runnable { + private final String serialPort; + private final CountDownLatch portFound; + private final AtomicReference result; + + public SerialAutoChecker(String serialPort, CountDownLatch portFound, AtomicReference result) { + this.serialPort = serialPort; + this.portFound = portFound; + this.result = result; + } + + @Override + public void run() { + PortHolder.EstablishConnection establishConnection = new PortHolder.EstablishConnection(serialPort).invoke(); + if (!establishConnection.isConnected()) + return; + IoStream stream = establishConnection.getStream(); + Logger logger = FileLog.LOGGER; + IncomingDataBuffer incomingData = new IncomingDataBuffer(logger); + stream.setInputListener(incomingData::addData); + try { + BinaryProtocol.sendPacket(new byte[]{BinaryProtocolCommands.COMMAND_HELLO}, logger, stream); + byte[] response = incomingData.getPacket(logger, "", false, System.currentTimeMillis()); + if (!checkResponseCode(response, BinaryProtocolCommands.RESPONSE_OK)) + return; + String message = new String(response, 1, response.length - 1); + System.out.println("Got " + message + " from " + serialPort); + if (message.startsWith(Fields.TS_SIGNATURE)) { + result.set(serialPort); + portFound.countDown(); + } + } catch (IOException | InterruptedException ignore) { + return; + } finally { + stream.close(); + } + + } +}