diff --git a/java_console/shared_ui/src/com/rusefi/tools/online/Online.java b/java_console/shared_ui/src/com/rusefi/tools/online/Online.java index 5e8d870039..f136976a68 100644 --- a/java_console/shared_ui/src/com/rusefi/tools/online/Online.java +++ b/java_console/shared_ui/src/com/rusefi/tools/online/Online.java @@ -13,6 +13,7 @@ import org.apache.http.entity.mime.MultipartEntity; import org.apache.http.entity.mime.content.FileBody; import org.apache.http.entity.mime.content.StringBody; import org.apache.http.impl.client.DefaultHttpClient; +import org.jetbrains.annotations.Nullable; import org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.json.simple.parser.ParseException; @@ -73,32 +74,33 @@ public class Online { } /** - * we are here in case of individual tune upload + * we are here in case of individual tune upload */ public static BasicFuture uploadTune(Msq tune, AuthTokenPanel authTokenPanel, JComponent parent, FutureCallback callback) { + try { + tune.writeXmlFile(outputXmlFileName); + } catch (JAXBException | IOException e) { + throw new IllegalStateException("While writing tune", e); + } + + return uploadFile(parent, callback, outputXmlFileName); + } + + @Nullable + public static BasicFuture uploadFile(JComponent parent, FutureCallback callback, final String fileName) { BasicFuture result = new BasicFuture<>(callback); - String authToken = authTokenPanel.getToken(); - if (!authTokenPanel.hasToken()) { - authTokenPanel.showError(parent); + String authToken = AuthTokenPanel.getAuthToken(); + if (!AuthTokenPanel.hasToken()) { + AuthTokenPanel.showError(parent); return null; } new Thread(new Runnable() { @Override public void run() { - UploadResult array = doUpload(authToken, tune); + UploadResult array = upload(new File(fileName), authToken); result.completed(array); } }).start(); return result; } - - private static UploadResult doUpload(String authToken, Msq tune) { - try { - tune.writeXmlFile(outputXmlFileName); - // todo: network upload should not happen on UI thread - return upload(new File(outputXmlFileName), authToken); - } catch (JAXBException | IOException ex) { - return new UploadResult(true, "IO error " + ex); - } - } } diff --git a/java_console/shared_ui/src/com/rusefi/ui/AuthTokenPanel.java b/java_console/shared_ui/src/com/rusefi/ui/AuthTokenPanel.java index e0b700fe7f..039e3ccbf1 100644 --- a/java_console/shared_ui/src/com/rusefi/ui/AuthTokenPanel.java +++ b/java_console/shared_ui/src/com/rusefi/ui/AuthTokenPanel.java @@ -127,15 +127,15 @@ public class AuthTokenPanel { return content; } - public boolean hasToken() { - return AutoTokenUtil.isToken(authTokenTestField.getText()); + public static boolean hasToken() { + return AutoTokenUtil.isToken(getAuthToken()); } public String getToken() { return authTokenTestField.getText(); } - public void showError(JComponent parent) { + public static void showError(JComponent parent) { JOptionPane.showMessageDialog(parent, "Does not work without auth token, see below."); } } diff --git a/java_tools/ts_plugin/src/main/java/com/rusefi/ts_plugin/LogUploadSelector.java b/java_tools/ts_plugin/src/main/java/com/rusefi/ts_plugin/LogUploadSelector.java index 3a32fae2c2..9bcbcd5b02 100644 --- a/java_tools/ts_plugin/src/main/java/com/rusefi/ts_plugin/LogUploadSelector.java +++ b/java_tools/ts_plugin/src/main/java/com/rusefi/ts_plugin/LogUploadSelector.java @@ -1,27 +1,125 @@ package com.rusefi.ts_plugin; +import com.efiAnalytics.plugin.ecu.ControllerAccess; import com.rusefi.TsTuneReader; +import com.rusefi.autoupdate.AutoupdateUtil; +import com.rusefi.tools.online.Online; +import com.rusefi.tools.online.UploadResult; import com.rusefi.ui.util.FrameHelper; +import org.apache.http.concurrent.FutureCallback; import org.jetbrains.annotations.NotNull; +import org.mockito.stubbing.Answer; import org.putgemin.VerticalFlowLayout; import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; import java.io.File; +import java.util.Objects; +import java.util.function.Supplier; + +import static org.mockito.Mockito.*; public class LogUploadSelector { - private final JPanel content = new JPanel(new VerticalFlowLayout()); + private final JPanel content = new JPanel(new BorderLayout()); + private final JLabel uploadState = new JLabel(); + + private final JPanel fileList = new JPanel(new VerticalFlowLayout()); + + public static final Answer NEGATIVE_ANSWER = invocation -> { + throw new UnsupportedOperationException("Not mocked " + invocation); + }; + private final Supplier controllerAccessSupplier; public static void main(String[] args) { - new FrameHelper().showFrame(new LogUploadSelector().getContent()); + String projectName = "mre_f4"; + ControllerAccess controllerAccess = mock(ControllerAccess.class, NEGATIVE_ANSWER); + doReturn(new String[]{projectName}).when(controllerAccess).getEcuConfigurationNames(); + + new FrameHelper().showFrame(new LogUploadSelector(() -> controllerAccess).getContent()); } - public LogUploadSelector() { - String projectName = "dev"; - String folder = getLogsFolderDir(projectName); + public LogUploadSelector(Supplier controllerAccessSupplier) { + this.controllerAccessSupplier = controllerAccessSupplier; - for (String fileName : new File(folder).list((dir, name) -> name.endsWith(".mlg"))) { - System.out.println(fileName); + JButton refresh = new JButton("Refresh"); + refresh.addActionListener(new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + refresh(); + } + }); + + JPanel topPanel = new JPanel(new FlowLayout()); + topPanel.add(refresh); + + JPanel filePanel = new JPanel(new BorderLayout()); + filePanel.add(fileList, BorderLayout.CENTER); + + JScrollPane fileScroll = new JScrollPane(filePanel, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + + content.add(topPanel, BorderLayout.NORTH); + content.add(fileScroll, BorderLayout.CENTER); + content.add(uploadState, BorderLayout.SOUTH); + refresh(); + } + + private void refresh() { + fileList.removeAll(); + + String folder = getLogsFolderDir(controllerAccessSupplier.get().getEcuConfigurationNames()[0]); + + for (String fileName : Objects.requireNonNull(new File(folder).list((dir, name) -> name.endsWith(".mlg")))) { + JPanel panel = new JPanel(new FlowLayout()); + JButton delete = new JButton("Delete"); + JButton upload = new JButton("Upload"); + final String fullFileName = folder + File.separator + fileName; + + panel.add(delete); + panel.add(upload); + + delete.addActionListener(new AbstractAction() { + @SuppressWarnings("ResultOfMethodCallIgnored") + @Override + public void actionPerformed(ActionEvent e) { + int result = JOptionPane.showConfirmDialog(null, "Are you sure you want to remove " + fileName, + "rusEfi", JOptionPane.YES_NO_OPTION); + if (result == JOptionPane.YES_OPTION) { + new File(fullFileName).delete(); + } + } + }); + + upload.addActionListener(new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + + Online.uploadFile(content, new FutureCallback() { + @Override + public void completed(UploadResult uploadResult) { + SwingUtilities.invokeLater(() -> UploadView.setResult(uploadResult, uploadState)); + } + + @Override + public void failed(Exception e) { + + } + + @Override + public void cancelled() { + + } + }, fullFileName); + + } + }); + + + panel.add(new JLabel(fileName)); + fileList.add(panel); } + + AutoupdateUtil.trueLayout(content); } @NotNull @@ -29,8 +127,7 @@ public class LogUploadSelector { return TsTuneReader.getProjectsDir() + File.separator + projectName + File.separator + "DataLogs"; } - private JComponent getContent() { + public JComponent getContent() { return content; } - } diff --git a/java_tools/ts_plugin/src/main/java/com/rusefi/ts_plugin/PluginEntry.java b/java_tools/ts_plugin/src/main/java/com/rusefi/ts_plugin/PluginEntry.java index 72a68ec1cb..e10c4844d6 100644 --- a/java_tools/ts_plugin/src/main/java/com/rusefi/ts_plugin/PluginEntry.java +++ b/java_tools/ts_plugin/src/main/java/com/rusefi/ts_plugin/PluginEntry.java @@ -12,7 +12,7 @@ import java.util.function.Supplier; /** * {@link TsPluginLauncher} creates an instance of this class via reflection. - * @see UploadTab upload tune & TODO upload logs + * @see TuneUploadTab upload tune & TODO upload logs * @see RemoteTab remote ECU access & control * @see BroadcastTab offer your ECU for remove access & control * @see PluginBodySandbox @@ -40,12 +40,14 @@ public class PluginEntry implements TsPluginBody { return; } - UploadTab uploadTab = new UploadTab(controllerAccessSupplier); + TuneUploadTab tuneUploadTab = new TuneUploadTab(controllerAccessSupplier); + LogUploadSelector logUploadTab = new LogUploadSelector(controllerAccessSupplier); BroadcastTab broadcastTab = new BroadcastTab(); RemoteTab remoteTab = new RemoteTab(); JTabbedPane tabbedPane = new JTabbedPane(); - tabbedPane.addTab("Upload", uploadTab.getContent()); + tabbedPane.addTab("Tune Upload", tuneUploadTab.getContent()); + tabbedPane.addTab("Log Upload", logUploadTab.getContent()); tabbedPane.addTab("Broadcast", broadcastTab.getContent()); tabbedPane.addTab("Remote ECU", remoteTab.getContent()); tabbedPane.addTab("Read SD Card", new SdCardReader(controllerAccessSupplier).getContent()); diff --git a/java_tools/ts_plugin/src/main/java/com/rusefi/ts_plugin/UploadTab.java b/java_tools/ts_plugin/src/main/java/com/rusefi/ts_plugin/TuneUploadTab.java similarity index 97% rename from java_tools/ts_plugin/src/main/java/com/rusefi/ts_plugin/UploadTab.java rename to java_tools/ts_plugin/src/main/java/com/rusefi/ts_plugin/TuneUploadTab.java index d2e2f80518..c9e3a2c0a1 100644 --- a/java_tools/ts_plugin/src/main/java/com/rusefi/ts_plugin/UploadTab.java +++ b/java_tools/ts_plugin/src/main/java/com/rusefi/ts_plugin/TuneUploadTab.java @@ -23,7 +23,10 @@ import java.util.Date; import java.util.Map; import java.util.function.Supplier; -public class UploadTab { +/** + * + */ +public class TuneUploadTab { private final JComponent content = new JPanel(new VerticalFlowLayout()); // 2 seconds aggregation by default private static final int AUTO_UPDATE_AGGREGATION = Integer.parseInt(System.getProperty("autoupload.aggregation", "2000")); @@ -54,7 +57,7 @@ public class UploadTab { private final ControllerParameterChangeListener listener; - public UploadTab(Supplier controllerAccessSupplier) { + public TuneUploadTab(Supplier controllerAccessSupplier) { this.controllerAccessSupplier = controllerAccessSupplier; timer.stop(); @@ -118,7 +121,7 @@ public class UploadTab { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { - uploadView.setResult(array); + UploadView.setResult(array, uploadView.uploadState); } }); } diff --git a/java_tools/ts_plugin/src/main/java/com/rusefi/ts_plugin/UploadView.java b/java_tools/ts_plugin/src/main/java/com/rusefi/ts_plugin/UploadView.java index 4b2b812334..90dcd957c4 100644 --- a/java_tools/ts_plugin/src/main/java/com/rusefi/ts_plugin/UploadView.java +++ b/java_tools/ts_plugin/src/main/java/com/rusefi/ts_plugin/UploadView.java @@ -39,7 +39,7 @@ public class UploadView { return PersistentConfiguration.getConfig().getRoot().getBoolProperty(AUTO_UPLOAD, false); } - public void setResult(UploadResult result) { + public static void setResult(UploadResult result, JLabel uploadState) { uploadState.setText(result.getFirstMessage()); uploadState.setVisible(true); } diff --git a/java_tools/ts_plugin/src/test/java/com/rusefi/ts_plugin/PluginBodySandbox.java b/java_tools/ts_plugin/src/test/java/com/rusefi/ts_plugin/PluginBodySandbox.java index c7fdacc9be..4e424098c6 100644 --- a/java_tools/ts_plugin/src/test/java/com/rusefi/ts_plugin/PluginBodySandbox.java +++ b/java_tools/ts_plugin/src/test/java/com/rusefi/ts_plugin/PluginBodySandbox.java @@ -1,6 +1,7 @@ package com.rusefi.ts_plugin; import com.efiAnalytics.plugin.ecu.ControllerAccess; +import com.efiAnalytics.plugin.ecu.ControllerException; import com.efiAnalytics.plugin.ecu.servers.ControllerParameterServer; import com.opensr5.ini.IniFileModel; import com.rusefi.TsTuneReader; @@ -9,9 +10,9 @@ import com.rusefi.ui.util.FrameHelper; import java.util.ArrayList; import java.util.Objects; +import static com.rusefi.ts_plugin.LogUploadSelector.NEGATIVE_ANSWER; import static org.mockito.Matchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +import static org.mockito.Mockito.*; /** * Sandbox for plugin body @@ -22,25 +23,22 @@ public class PluginBodySandbox { private static final String PROJECT_NAME = "dev"; - public static void main(String[] args) { + public static void main(String[] args) throws ControllerException { String iniFile = TsTuneReader.getProjectModeFileName(PROJECT_NAME); IniFileModel model = new IniFileModel().readIniFile(iniFile); Objects.requireNonNull(model, "model"); java.util.List fieldNamesList = new ArrayList<>(model.allIniFields.keySet()); String[] parameterNames = fieldNamesList.toArray(new String[0]); + ControllerParameterServer controllerParameterServer = mock(ControllerParameterServer.class, NEGATIVE_ANSWER); + doReturn(parameterNames).when(controllerParameterServer).getParameterNames(any()); + doNothing().when(controllerParameterServer).subscribe(any(), any(), any()); - ControllerParameterServer controllerParameterServer = mock(ControllerParameterServer.class); - when(controllerParameterServer.getParameterNames(any())).thenReturn(parameterNames); + ControllerAccess controllerAccess = mock(ControllerAccess.class, NEGATIVE_ANSWER); + doReturn(new String[]{PROJECT_NAME}).when(controllerAccess).getEcuConfigurationNames(); + doReturn(controllerParameterServer).when(controllerAccess).getControllerParameterServer(); - ControllerAccess controllerAccess = mock(ControllerAccess.class); - when(controllerAccess.getEcuConfigurationNames()).thenReturn(new String[]{PROJECT_NAME}); - when(controllerAccess.getControllerParameterServer()).thenReturn(controllerParameterServer); - - - new FrameHelper().showFrame(new PluginEntry(() -> { - return controllerAccess; - }).getContent()); + new FrameHelper().showFrame(new PluginEntry(() -> controllerAccess).getContent()); } }