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.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* @author Andrey Belomutskiy
@ -111,30 +113,158 @@ public enum SerialPortScanner {
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
*/
private void findAllAvailablePorts(boolean includeSlowLookup) {
List<String> ports = new ArrayList<>();
List<PortResult> ports = new ArrayList<>();
boolean dfuConnected;
boolean stLinkConnected;
boolean PCANConnected;
String[] serialPorts = LinkManager.getCommPorts();
if (serialPorts.length > 0)
ports.add(AUTO_SERIAL);
List<String> portsToInspect = new ArrayList<>();
for (String serialPort : serialPorts) {
// Filter out some macOS trash
if (serialPort.contains("wlan-debug") ||
serialPort.contains("Bluetooth-Incoming-Port") ||
serialPort.startsWith("cu.")) {
continue;
// First, check the port cache
if (portCache.containsKey(serialPort)) {
// We've already probed this port - don't re-probe it again
PortResult cached = portCache.get(serialPort);
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) {
ports.addAll(TcpConnector.getAvailablePorts());
for (String tcpPort : TcpConnector.getAvailablePorts()) {
ports.add(new PortResult(tcpPort, SerialPortType.Ecu));
}
dfuConnected = DfuFlasher.detectSTM32BootloaderDriverState(UpdateOperationCallbacks.DUMMY);
stLinkConnected = DfuFlasher.detectStLink(UpdateOperationCallbacks.DUMMY);
PCANConnected = DfuFlasher.detectPcan(UpdateOperationCallbacks.DUMMY);
@ -144,9 +274,9 @@ public enum SerialPortScanner {
PCANConnected = false;
}
if (PCANConnected)
ports.add(LinkManager.PCAN);
ports.add(new PortResult(LinkManager.PCAN, SerialPortType.CAN));
if (SHOW_SOCKETCAN)
ports.add(LinkManager.SOCKET_CAN);
ports.add(new PortResult(LinkManager.SOCKET_CAN, SerialPortType.CAN));
boolean isListUpdated;
AvailableHardware currentHardware = new AvailableHardware(ports, dfuConnected, stLinkConnected, PCANConnected);
@ -188,12 +318,12 @@ public enum SerialPortScanner {
public static class AvailableHardware {
private final List<String> ports;
private final List<PortResult> ports;
private final boolean dfuFound;
private final boolean stLinkConnected;
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.dfuFound = dfuFound;
this.stLinkConnected = stLinkConnected;
@ -201,7 +331,7 @@ public enum SerialPortScanner {
}
@NotNull
public List<String> getKnownPorts() {return new ArrayList<>(ports);}
public List<PortResult> getKnownPorts() {return new ArrayList<>(ports);}
public boolean isDfuFound() {
return dfuFound;

View File

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

View File

@ -44,7 +44,7 @@ public class ProgramSelector {
private final JPanel controls = new JPanel(new FlowLayout());
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(noHardware, BorderLayout.SOUTH);
controls.setVisible(false);
@ -61,7 +61,7 @@ public class ProgramSelector {
@Override
public void actionPerformed(ActionEvent e) {
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);
@ -72,7 +72,7 @@ public class ProgramSelector {
switch (selectedMode) {
case AUTO_DFU:
jobName = "DFU update";
job = (callbacks) -> DfuFlasher.doAutoDfu(comboPorts, selectedPort, callbacks);
job = (callbacks) -> DfuFlasher.doAutoDfu(comboPorts, selectedPort.port, callbacks);
break;
case MANUAL_DFU:
jobName = "DFU update";
@ -86,11 +86,11 @@ public class ProgramSelector {
break;
case DFU_SWITCH:
jobName = "DFU switch";
job = (callbacks) -> rebootToDfu(comboPorts, selectedPort, callbacks);
job = (callbacks) -> rebootToDfu(comboPorts, selectedPort.port, callbacks);
break;
case OPENBLT_SWITCH:
jobName = "OpenBLT switch";
job = (callbacks) -> rebootToOpenblt(comboPorts, selectedPort, callbacks);
job = (callbacks) -> rebootToOpenblt(comboPorts, selectedPort.port, callbacks);
break;
case OPENBLT_CAN:
jobName = "OpenBLT via CAN";
@ -98,11 +98,11 @@ public class ProgramSelector {
break;
case OPENBLT_MANUAL:
jobName = "OpenBLT via Serial";
job = (callbacks) -> flashOpenbltSerialJni(selectedPort, callbacks);
job = (callbacks) -> flashOpenbltSerialJni(selectedPort.port, callbacks);
break;
case OPENBLT_AUTO:
jobName = "OpenBLT via Serial";
job = (callbacks) -> flashOpenbltSerialAutomatic(comboPorts, selectedPort, callbacks);
job = (callbacks) -> flashOpenbltSerialAutomatic(comboPorts, selectedPort.port, callbacks);
break;
case DFU_ERASE:
jobName = "DFU erase";