integration

This commit is contained in:
rusefillc 2024-01-30 22:38:01 -05:00
parent ad4aa4f0de
commit 0f7e444944
3 changed files with 157 additions and 26 deletions

View File

@ -18,6 +18,8 @@ import org.jetbrains.annotations.NotNull;
import java.io.IOException; import java.io.IOException;
import java.util.*; import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Function;
import java.util.stream.Collectors;
/** /**
* @author Andrey Belomutskiy * @author Andrey Belomutskiy
@ -111,30 +113,158 @@ public enum SerialPortScanner {
startTimer(); startTimer();
} }
private static PortResult inspectPort(String serialPort) {
log.info("Determining type of serial port: " + serialPort);
boolean isOpenblt = isPortOpenblt(serialPort);
log.info("Port " + serialPort + (isOpenblt ? " looks like" : " does not look like") + " an OpenBLT bootloader");
if (isOpenblt) {
return new PortResult(serialPort, SerialPortType.OpenBlt);
} else {
// See if this looks like an ECU
String signature = getEcuSignature(serialPort);
boolean isEcu = signature != null;
log.info("Port " + serialPort + (isEcu ? " looks like" : " does not look like") + " an ECU");
if (isEcu) {
boolean ecuHasOpenblt = ecuHasOpenblt(serialPort);
log.info("ECU at " + serialPort + (ecuHasOpenblt ? " has" : " does not have") + " an OpenBLT bootloader");
return new PortResult(serialPort, ecuHasOpenblt ? SerialPortType.EcuWithOpenblt : SerialPortType.Ecu, signature);
} else {
// Dunno what this is, leave it in the list anyway
return new PortResult(serialPort, SerialPortType.Unknown);
}
}
}
private static List<PortResult> inspectPorts(final List<String> ports) {
if (ports.isEmpty()) {
return new ArrayList<>();
}
final Object resultsLock = new Object();
final Map<String, PortResult> results = new HashMap<>();
// When the last port is found, we need to cancel the timeout
final Thread callingThread = Thread.currentThread();
// One thread per port to check
final List<Thread> threads = ports.stream().map(p -> {
Thread t = new Thread(() -> {
PortResult r = inspectPort(p);
// Record the result under lock
synchronized (resultsLock) {
if (Thread.currentThread().isInterrupted()) {
// If interrupted, don't try to write our result
return;
}
results.put(p, r);
if (results.size() == ports.size()) {
// We now have all the results - interrupt the calling thread
callingThread.interrupt();
}
}
});
t.setName("SerialPortScanner inspectPort " + p);
t.setDaemon(true);
t.start();
return t;
}).collect(Collectors.toList());
// Give everyone a chance to finish
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// We got interrupted because the last port got found, nothing to do
}
// Interrupt all threads under lock to ensure no more objects are added to results
synchronized (resultsLock) {
for (Thread t : threads) {
t.interrupt();
}
}
// Now check that we got everything - if any timed out, register them as unknown
for (String port : ports) {
if (!results.containsKey(port)) {
log.info("Port " + port + " timed out, adding as Unknown.");
results.put(port, new PortResult(port, SerialPortType.Unknown));
}
}
return new ArrayList<>(results.values());
}
private final static Map<String, PortResult> portCache = new HashMap<>();
/** /**
* Find all available serial ports and checks if simulator local TCP port is available * Find all available serial ports and checks if simulator local TCP port is available
*/ */
private void findAllAvailablePorts(boolean includeSlowLookup) { private void findAllAvailablePorts(boolean includeSlowLookup) {
List<String> ports = new ArrayList<>(); List<PortResult> ports = new ArrayList<>();
boolean dfuConnected; boolean dfuConnected;
boolean stLinkConnected; boolean stLinkConnected;
boolean PCANConnected; boolean PCANConnected;
String[] serialPorts = LinkManager.getCommPorts(); String[] serialPorts = LinkManager.getCommPorts();
if (serialPorts.length > 0)
ports.add(AUTO_SERIAL); List<String> portsToInspect = new ArrayList<>();
for (String serialPort : serialPorts) { for (String serialPort : serialPorts) {
// Filter out some macOS trash // First, check the port cache
if (serialPort.contains("wlan-debug") || if (portCache.containsKey(serialPort)) {
serialPort.contains("Bluetooth-Incoming-Port") || // We've already probed this port - don't re-probe it again
serialPort.startsWith("cu.")) { PortResult cached = portCache.get(serialPort);
continue;
ports.add(cached);
} else {
portsToInspect.add(serialPort);
}
}
for (PortResult p : inspectPorts(portsToInspect)) {
log.info("Port " + p.port + " detected as: " + p.type.friendlyString);
ports.add(p);
portCache.put(p.port, p);
}
{
// Clean the port cache of any entries that no longer exist
// If the same port appears later, we want to re-probe it at that time
// In any other scenario, auto could have unexpected behavior for the user
List<String> toRemove = new ArrayList<>();
for (String x : portCache.keySet()) {
if (Arrays.stream(serialPorts).noneMatch(x::equals)) {
toRemove.add(x);
}
}
// two steps to avoid ConcurrentModificationException
toRemove.forEach(p -> {
portCache.remove(p);
log.info("Removing port " + p);
});
}
// Sort ports by their type to put your ECU at the top
ports.sort(Comparator.comparingInt(a -> a.type.sortOrder));
if (includeSlowLookup) {
for (String tcpPort : TcpConnector.getAvailablePorts()) {
ports.add(new PortResult(tcpPort, SerialPortType.Ecu));
} }
ports.add(serialPort);
} }
if (includeSlowLookup) { if (includeSlowLookup) {
ports.addAll(TcpConnector.getAvailablePorts()); for (String tcpPort : TcpConnector.getAvailablePorts()) {
ports.add(new PortResult(tcpPort, SerialPortType.Ecu));
}
dfuConnected = DfuFlasher.detectSTM32BootloaderDriverState(UpdateOperationCallbacks.DUMMY); dfuConnected = DfuFlasher.detectSTM32BootloaderDriverState(UpdateOperationCallbacks.DUMMY);
stLinkConnected = DfuFlasher.detectStLink(UpdateOperationCallbacks.DUMMY); stLinkConnected = DfuFlasher.detectStLink(UpdateOperationCallbacks.DUMMY);
PCANConnected = DfuFlasher.detectPcan(UpdateOperationCallbacks.DUMMY); PCANConnected = DfuFlasher.detectPcan(UpdateOperationCallbacks.DUMMY);
@ -144,9 +274,9 @@ public enum SerialPortScanner {
PCANConnected = false; PCANConnected = false;
} }
if (PCANConnected) if (PCANConnected)
ports.add(LinkManager.PCAN); ports.add(new PortResult(LinkManager.PCAN, SerialPortType.CAN));
if (SHOW_SOCKETCAN) if (SHOW_SOCKETCAN)
ports.add(LinkManager.SOCKET_CAN); ports.add(new PortResult(LinkManager.SOCKET_CAN, SerialPortType.CAN));
boolean isListUpdated; boolean isListUpdated;
AvailableHardware currentHardware = new AvailableHardware(ports, dfuConnected, stLinkConnected, PCANConnected); AvailableHardware currentHardware = new AvailableHardware(ports, dfuConnected, stLinkConnected, PCANConnected);
@ -188,12 +318,12 @@ public enum SerialPortScanner {
public static class AvailableHardware { public static class AvailableHardware {
private final List<String> ports; private final List<PortResult> ports;
private final boolean dfuFound; private final boolean dfuFound;
private final boolean stLinkConnected; private final boolean stLinkConnected;
private final boolean PCANConnected; private final boolean PCANConnected;
public <T> AvailableHardware(List<String> ports, boolean dfuFound, boolean stLinkConnected, boolean PCANConnected) { public <T> AvailableHardware(List<PortResult> ports, boolean dfuFound, boolean stLinkConnected, boolean PCANConnected) {
this.ports = ports; this.ports = ports;
this.dfuFound = dfuFound; this.dfuFound = dfuFound;
this.stLinkConnected = stLinkConnected; this.stLinkConnected = stLinkConnected;
@ -201,7 +331,7 @@ public enum SerialPortScanner {
} }
@NotNull @NotNull
public List<String> getKnownPorts() {return new ArrayList<>(ports);} public List<PortResult> getKnownPorts() {return new ArrayList<>(ports);}
public boolean isDfuFound() { public boolean isDfuFound() {
return dfuFound; return dfuFound;

View File

@ -51,7 +51,7 @@ public class StartupFrame {
private final JFrame frame; private final JFrame frame;
private final JPanel connectPanel = new JPanel(new FlowLayout()); private final JPanel connectPanel = new JPanel(new FlowLayout());
// todo: move this line to the connectPanel // todo: move this line to the connectPanel
private final JComboBox<String> comboPorts = new JComboBox<>(); private final JComboBox<SerialPortScanner.PortResult> comboPorts = new JComboBox<>();
private final JPanel leftPanel = new JPanel(new VerticalFlowLayout()); private final JPanel leftPanel = new JPanel(new VerticalFlowLayout());
private final JPanel realHardwarePanel = new JPanel(new MigLayout()); private final JPanel realHardwarePanel = new JPanel(new MigLayout());
@ -234,7 +234,7 @@ public class StartupFrame {
} }
private void applyKnownPorts(SerialPortScanner.AvailableHardware currentHardware) { private void applyKnownPorts(SerialPortScanner.AvailableHardware currentHardware) {
List<String> ports = currentHardware.getKnownPorts(); List<SerialPortScanner.PortResult> ports = currentHardware.getKnownPorts();
log.info("Rendering available ports: " + ports); log.info("Rendering available ports: " + ports);
connectPanel.setVisible(!ports.isEmpty()); connectPanel.setVisible(!ports.isEmpty());
noPortsMessage.setVisible(ports.isEmpty()); noPortsMessage.setVisible(ports.isEmpty());
@ -299,10 +299,11 @@ public class StartupFrame {
SerialPortScanner.INSTANCE.stopTimer(); SerialPortScanner.INSTANCE.stopTimer();
} }
private void applyPortSelectionToUIcontrol(List<String> ports) { private void applyPortSelectionToUIcontrol(List<SerialPortScanner.PortResult> ports) {
comboPorts.removeAllItems(); comboPorts.removeAllItems();
for (final String port : ports) for (final SerialPortScanner.PortResult port : ports) {
comboPorts.addItem(port); comboPorts.addItem(port);
}
String defaultPort = getConfig().getRoot().getProperty(ConsoleUI.PORT_KEY); String defaultPort = getConfig().getRoot().getProperty(ConsoleUI.PORT_KEY);
if (!PersistentConfiguration.getBoolProperty(ALWAYS_AUTO_PORT)) { if (!PersistentConfiguration.getBoolProperty(ALWAYS_AUTO_PORT)) {
comboPorts.setSelectedItem(defaultPort); comboPorts.setSelectedItem(defaultPort);

View File

@ -44,7 +44,7 @@ public class ProgramSelector {
private final JPanel controls = new JPanel(new FlowLayout()); private final JPanel controls = new JPanel(new FlowLayout());
private final JComboBox<String> mode = new JComboBox<>(); private final JComboBox<String> mode = new JComboBox<>();
public ProgramSelector(JComboBox<String> comboPorts) { public ProgramSelector(JComboBox<SerialPortScanner.PortResult> comboPorts) {
content.add(controls, BorderLayout.NORTH); content.add(controls, BorderLayout.NORTH);
content.add(noHardware, BorderLayout.SOUTH); content.add(noHardware, BorderLayout.SOUTH);
controls.setVisible(false); controls.setVisible(false);
@ -61,7 +61,7 @@ public class ProgramSelector {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
final String selectedMode = (String) mode.getSelectedItem(); final String selectedMode = (String) mode.getSelectedItem();
final String selectedPort = (String) comboPorts.getSelectedItem(); final SerialPortScanner.PortResult selectedPort = ((SerialPortScanner.PortResult) comboPorts.getSelectedItem());
getConfig().getRoot().setProperty(getClass().getSimpleName(), selectedMode); getConfig().getRoot().setProperty(getClass().getSimpleName(), selectedMode);
@ -72,7 +72,7 @@ public class ProgramSelector {
switch (selectedMode) { switch (selectedMode) {
case AUTO_DFU: case AUTO_DFU:
jobName = "DFU update"; jobName = "DFU update";
job = (callbacks) -> DfuFlasher.doAutoDfu(comboPorts, selectedPort, callbacks); job = (callbacks) -> DfuFlasher.doAutoDfu(comboPorts, selectedPort.port, callbacks);
break; break;
case MANUAL_DFU: case MANUAL_DFU:
jobName = "DFU update"; jobName = "DFU update";
@ -86,11 +86,11 @@ public class ProgramSelector {
break; break;
case DFU_SWITCH: case DFU_SWITCH:
jobName = "DFU switch"; jobName = "DFU switch";
job = (callbacks) -> rebootToDfu(comboPorts, selectedPort, callbacks); job = (callbacks) -> rebootToDfu(comboPorts, selectedPort.port, callbacks);
break; break;
case OPENBLT_SWITCH: case OPENBLT_SWITCH:
jobName = "OpenBLT switch"; jobName = "OpenBLT switch";
job = (callbacks) -> rebootToOpenblt(comboPorts, selectedPort, callbacks); job = (callbacks) -> rebootToOpenblt(comboPorts, selectedPort.port, callbacks);
break; break;
case OPENBLT_CAN: case OPENBLT_CAN:
jobName = "OpenBLT via CAN"; jobName = "OpenBLT via CAN";
@ -98,11 +98,11 @@ public class ProgramSelector {
break; break;
case OPENBLT_MANUAL: case OPENBLT_MANUAL:
jobName = "OpenBLT via Serial"; jobName = "OpenBLT via Serial";
job = (callbacks) -> flashOpenbltSerialJni(selectedPort, callbacks); job = (callbacks) -> flashOpenbltSerialJni(selectedPort.port, callbacks);
break; break;
case OPENBLT_AUTO: case OPENBLT_AUTO:
jobName = "OpenBLT via Serial"; jobName = "OpenBLT via Serial";
job = (callbacks) -> flashOpenbltSerialAutomatic(comboPorts, selectedPort, callbacks); job = (callbacks) -> flashOpenbltSerialAutomatic(comboPorts, selectedPort.port, callbacks);
break; break;
case DFU_ERASE: case DFU_ERASE:
jobName = "DFU erase"; jobName = "DFU erase";