Add `Update Calibrations` button #6864

This commit is contained in:
kifir 2024-09-18 00:09:37 +03:00 committed by rusefillc
parent 0fe5649794
commit 6fd13d446a
3 changed files with 229 additions and 9 deletions

View File

@ -0,0 +1,86 @@
package com.rusefi.maintenance;
import com.devexperts.logging.Logging;
import com.opensr5.ConfigurationImage;
import com.rusefi.io.LinkManager;
import com.rusefi.io.UpdateOperationCallbacks;
import javax.swing.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import static com.devexperts.logging.Logging.getLogging;
public enum CalibrationsUpdater {
INSTANCE;
private static final Logging log = getLogging(CalibrationsUpdater.class);
private volatile ConfigurationImage calibrationsToUpload = null;
public void setCalibrationsToUpload(final ConfigurationImage calibrations) {
calibrationsToUpload = calibrations;
}
public synchronized void updateCalibrations(
final String port,
final UpdateOperationCallbacks callbacks
) {
final ConfigurationImage calibrationsImage = calibrationsToUpload;
if (calibrationsImage != null) {
final int calibrationsImageSize = calibrationsImage.getSize();
try (LinkManager linkManager = new LinkManager()
.setNeedPullText(false)
.setNeedPullLiveData(true)) {
callbacks.logLine(String.format("Connecting to port %s...", port));
try {
linkManager.connect(port).await(60, TimeUnit.SECONDS);
} catch (final InterruptedException e) {
final String errorMsg = String.format("Failed to connect to port %s", port);
log.error(errorMsg, e);
callbacks.logLine(errorMsg);
callbacks.error();
return;
}
callbacks.logLine(String.format(
"Updating configuration image (%d bytes) to port %s...",
calibrationsImageSize,
port
));
final CountDownLatch latch = new CountDownLatch(1);
linkManager.execute(() -> {
linkManager.getBinaryProtocol().uploadChanges(calibrationsImage);
latch.countDown();
});
try {
if (!latch.await(1, TimeUnit.MINUTES)) {
callbacks.logLine(String.format(
"Failed to update configuration image (%d bytes) to port %s in a minute",
calibrationsImageSize,
port
));
callbacks.error();
} else {
callbacks.logLine(String.format(
"Configuration image (%d bytes) has been uploaded to port %s",
calibrationsImageSize,
port
));
callbacks.done();
}
} catch (final InterruptedException e) {
final String errorMsg = String.format(
"Updating calibrations to port %s was interrupted",
port
);
log.error(errorMsg, e);
callbacks.logLine(errorMsg);
callbacks.error();
}
}
} else {
callbacks.logLine("ERROR: Calibrations to update are undefined");
}
}
}

View File

@ -52,6 +52,7 @@ public class ProgramSelector {
private static final String INSTALL_OPENBLT = "Install OpenBLT";
private static final String OPENBLT_CAN = "OpenBLT via CAN";
public static final String UPDATE_CALIBRATIONS = "Update Calibrations";
private final JPanel content = new JPanel(new BorderLayout());
private final JLabel noHardware = new JLabel("Nothing detected");
@ -133,6 +134,13 @@ public class ProgramSelector {
jobName = "DFU erase";
job = DfuFlasher::runDfuEraseAsync;
break;
case UPDATE_CALIBRATIONS:
jobName = "Update calibrations";
job = (callbacks) -> CalibrationsUpdater.INSTANCE.updateCalibrations(
selectedPort.port,
callbacks
);
break;
default:
throw new IllegalArgumentException("How did you " + selectedMode);
}

View File

@ -1,6 +1,9 @@
package com.rusefi.ui.basic;
import com.devexperts.logging.Logging;
import com.opensr5.ConfigurationImage;
import com.opensr5.io.ConfigurationImageFile;
import com.rusefi.AvailableHardware;
import com.rusefi.Launcher;
import com.rusefi.SerialPortScanner;
@ -8,11 +11,10 @@ import com.rusefi.StartupFrame;
import com.rusefi.autodetect.PortDetector;
import com.rusefi.core.FindFileHelper;
import com.rusefi.core.net.ConnectionAndMeta;
import com.rusefi.core.preferences.storage.PersistentConfiguration;
import com.rusefi.core.ui.AutoupdateUtil;
import com.rusefi.core.ui.FrameHelper;
import com.rusefi.maintenance.DfuFlasher;
import com.rusefi.maintenance.ProgramSelector;
import com.rusefi.maintenance.StatusAnimation;
import com.rusefi.maintenance.UpdateStatusWindow;
import com.rusefi.maintenance.*;
import com.rusefi.ui.LogoHelper;
import com.rusefi.ui.util.DefaultExceptionHandler;
import com.rusefi.ui.util.HorizontalLine;
@ -21,14 +23,16 @@ import com.rusefi.ui.widgets.ToolButtons;
import org.putgemin.VerticalFlowLayout;
import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static com.devexperts.logging.Logging.getLogging;
import static com.rusefi.FileLog.isWindows;
@ -40,6 +44,8 @@ import static com.rusefi.FileLog.isWindows;
public class BasicStartupFrame {
private static final Logging log = getLogging(BasicStartupFrame.class);
private static final String BINARY_IMAGE_DEFAULT_DIRECTORY_PROPERTY_NAME = "binary_image_default_directory";
private final String whiteLabel = ConnectionAndMeta.getWhiteLabel(ConnectionAndMeta.getProperties());
private final FrameHelper frame = FrameHelper.createFrame(
whiteLabel + " basic console " + Launcher.CONSOLE_VERSION
@ -49,8 +55,14 @@ public class BasicStartupFrame {
private final JLabel statusMessage = new JLabel();
private final StatusAnimation status = new StatusAnimation(this::updateStatus, StartupFrame.SCANNING_PORTS);
private final JButton updateFirmwareButton = ProgramSelector.createUpdateFirmwareButton();
private final JButton updateCalibrationsButton = new JButton(
"Update Calibrations",
AutoupdateUtil.loadIcon("writeconfig48.png")
);
private final JFileChooser calibrationsFileChooser = createConfigurationImageFileChooser();
private volatile Optional<SerialPortScanner.PortResult> portToUpdateObfuscatedFirmware = Optional.empty();
private volatile Optional<SerialPortScanner.PortResult> portToUpdateCalibrations = Optional.empty();
public static void main(String[] args) {
runTool(null);
@ -75,12 +87,12 @@ public class BasicStartupFrame {
panel.add(statusMessage);
}
panel.add(updateFirmwareButton);
SerialPortScanner.INSTANCE.addListener(currentHardware -> SwingUtilities.invokeLater(() -> {
onHardwareUpdated(currentHardware);
}));
} else {
panel.add(new JLabel("Sorry only works on Windows"));
}
SerialPortScanner.INSTANCE.addListener(currentHardware -> SwingUtilities.invokeLater(() -> {
onHardwareUpdated(currentHardware);
}));
panel.add(new HorizontalLine());
JLabel logoLabel = LogoHelper.createLogoLabel();
@ -89,6 +101,10 @@ public class BasicStartupFrame {
if (ConnectionAndMeta.isDefaultWhitelabel(whiteLabel))
panel.add(LogoHelper.createUrlLabel());
updateCalibrationsButton.addActionListener(this::onUpdateCalibrationsButtonClicked);
updateCalibrationsButton.setEnabled(false);
panel.add(updateCalibrationsButton);
frame.showFrame(panel, false);
UiUtils.centerWindow(frame.getFrame());
packFrame();
@ -120,6 +136,7 @@ public class BasicStartupFrame {
if (isObfusacted) {
updatePortToUpdateObfuscatedFirmware(currentHardware);
}
updatePortToUpdateCalibrations(currentHardware);
}
private void updatePortToUpdateObfuscatedFirmware(final AvailableHardware currentHardware) {
@ -175,7 +192,46 @@ public class BasicStartupFrame {
statusMessage.setText(reason);
}
private void onUpdateFirmwareButtonClicked(final ActionEvent e) {
private void updatePortToUpdateCalibrations(final AvailableHardware currentHardware) {
final List<SerialPortScanner.PortResult> ecuPortsToUpdateCalibrations = currentHardware.getKnownPorts(Set.of(
SerialPortScanner.SerialPortType.Ecu,
SerialPortScanner.SerialPortType.EcuWithOpenblt
));
switch (ecuPortsToUpdateCalibrations.size()) {
case 0: {
resetPortToUpdateCalibrations();
log.info("No ECU ports to update calibrations found");
break;
}
case 1: {
setPortToUpdateCalibrations(ecuPortsToUpdateCalibrations.get(0));
break;
}
default: {
resetPortToUpdateCalibrations();
log.info(String.format(
"Multiple ECU ports to update calibrations found on: %s",
ecuPortsToUpdateCalibrations.stream()
.map(portResult -> portResult.port)
.collect(Collectors.joining(", "))
));
break;
}
}
}
private void setPortToUpdateCalibrations(final SerialPortScanner.PortResult port) {
portToUpdateCalibrations = Optional.of(port);
updateCalibrationsButton.setEnabled(true);
}
private void resetPortToUpdateCalibrations() {
portToUpdateCalibrations = Optional.empty();
updateCalibrationsButton.setEnabled(false);
}
private void onUpdateFirmwareButtonClicked(final ActionEvent actionEvent) {
if (isObfusacted) {
portToUpdateObfuscatedFirmware.ifPresentOrElse(port -> {
switch (port.type) {
@ -201,6 +257,76 @@ public class BasicStartupFrame {
}
}
private void onUpdateCalibrationsButtonClicked(final ActionEvent actionEvent) {
portToUpdateCalibrations.ifPresentOrElse(
port -> {
final int selectedOption = calibrationsFileChooser.showOpenDialog(updateCalibrationsButton);
if (selectedOption == JFileChooser.APPROVE_OPTION) {
final File selectedFile = calibrationsFileChooser.getSelectedFile();
saveBinaryImageDefaultDirectory(selectedFile.getParent());
try {
final ConfigurationImage calibrationsImage = ConfigurationImageFile.readFromFile(
selectedFile.getAbsolutePath()
);
CalibrationsUpdater.INSTANCE.setCalibrationsToUpload(calibrationsImage);
ProgramSelector.executeJob(
updateCalibrationsButton,
ProgramSelector.UPDATE_CALIBRATIONS,
port
);
} catch (final IOException e) {
final String errorMsg = String.format(
"Failed to load calibrations from file %s",
selectedFile.getAbsolutePath()
);
log.error(errorMsg, e);
JOptionPane.showMessageDialog(
updateCalibrationsButton,
errorMsg,
"Error",
JOptionPane.ERROR_MESSAGE
);
}
}
}, () -> {
JOptionPane.showMessageDialog(
updateCalibrationsButton,
"Device is not connected",
"Error",
JOptionPane.ERROR_MESSAGE
);
}
);
}
private void saveBinaryImageDefaultDirectory(final String path) {
PersistentConfiguration.getConfig().getRoot().setProperty(
BINARY_IMAGE_DEFAULT_DIRECTORY_PROPERTY_NAME,
path
);
PersistentConfiguration.getConfig().save();
}
private String loadBinaryImageDefaultDirectory() {
return PersistentConfiguration.getConfig().getRoot().getProperty(
BINARY_IMAGE_DEFAULT_DIRECTORY_PROPERTY_NAME,
""
);
}
private void runTool() {
}
private JFileChooser createConfigurationImageFileChooser() {
final JFileChooser fc = new JFileChooser();
fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
fc.setFileFilter(new FileNameExtensionFilter("Binary image files (.binary_image)", "binary_image"));
final String currentDirectory = loadBinaryImageDefaultDirectory();
if (currentDirectory != null) {
fc.setCurrentDirectory(new File(currentDirectory));
}
return fc;
}
}