mirror of https://github.com/FOME-Tech/fome-fw.git
ts plugin
This commit is contained in:
parent
40a0523fc3
commit
17492fcf93
|
@ -1,33 +0,0 @@
|
|||
name: TS Plugin
|
||||
|
||||
on: [push,pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
java-version: '8'
|
||||
|
||||
- name: Test Compiler
|
||||
run: javac -version
|
||||
|
||||
- name: Install Tools
|
||||
working-directory: ./.github/workflows/
|
||||
run: |
|
||||
sudo ./add-ubuntu-latest-apt-mirrors.sh
|
||||
sudo apt-get install sshpass
|
||||
|
||||
- name: Build TS plugin body
|
||||
working-directory: ./java_tools/ts_plugin
|
||||
run: ant
|
||||
|
||||
# - name: Upload plugin body
|
||||
# working-directory: .
|
||||
# run: java_console/upload_file.sh ${{ secrets.RUSEFI_SSH_USER }} ${{ secrets.RUSEFI_SSH_PASS }} ${{ secrets.RUSEFI_SSH_SERVER }} build_server/autoupdate java_tools/ts_plugin/build/jar/rusefi_plugin_body.jar
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
plugins {
|
||||
id 'java-library'
|
||||
id 'com.github.johnrengelman.shadow' version '6.1.0'
|
||||
}
|
||||
|
||||
apply from: '../../android/dependencies.gradle'
|
||||
|
||||
dependencies {
|
||||
api project(':core_ui')
|
||||
api project(':shared_ui')
|
||||
api project(':ecu_io')
|
||||
api project(':inifile')
|
||||
api project(':ts_plugin_launcher')
|
||||
testImplementation global_libs.mockito
|
||||
testImplementation testFixtures( project(':ecu_io'))
|
||||
implementation files('../ts_plugin_launcher/lib/TunerStudioPluginAPI.jar')
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
<project default="jar">
|
||||
<property name="jar_file_folder" value="build/jar"/>
|
||||
<property name="jar_file" value="${jar_file_folder}/rusefi_plugin_body.jar"/>
|
||||
<property name="console_path" value="../../java_console"/>
|
||||
<property name="launcher_path" value="../ts_plugin_launcher"/>
|
||||
|
||||
<target name="clean">
|
||||
<delete dir="build"/>
|
||||
</target>
|
||||
|
||||
<target name="compile">
|
||||
<mkdir dir="build/classes"/>
|
||||
|
||||
<resources id="libs">
|
||||
<string>lib/mockito-all-1.10.19.jar</string>
|
||||
<string>${console_path}/../java_tools/configuration_definition/lib/snakeyaml.jar</string>
|
||||
<string>${console_path}/lib/javacan-core.jar</string>
|
||||
</resources>
|
||||
<pathconvert property="lib_list" refid="libs" pathsep=":" />
|
||||
|
||||
<javac debug="yes"
|
||||
destdir="build/classes"
|
||||
classpath="${lib_list}:${console_path}/lib/log4j-api-2.13.3.jar:${console_path}/lib/log4j-core-2.13.3.jar:${console_path}/lib/jsr305-2.0.1.jar:${console_path}/lib/jcip-annotations-1.0.jar:${console_path}/lib/annotations.jar:${console_path}/lib/jSerialComm.jar:${console_path}/lib/junit.jar:${console_path}/lib/json-simple-1.1.1.jar:${console_path}/lib/annotations.jar:${launcher_path}/lib/TunerStudioPluginAPI.jar:${console_path}/lib/httpclient.jar:${console_path}/lib/httpmime.jar:${console_path}/lib/httpcore.jar"
|
||||
>
|
||||
<src path="${console_path}/shared_ui/src"/>
|
||||
<src path="${console_path}/core_ui/src/main/java"/>
|
||||
<src path="${console_path}/autoupdate/src/main/java"/>
|
||||
<src path="${console_path}/shared_io/src/main/java"/>
|
||||
<src path="${console_path}/inifile/src"/>
|
||||
<src path="${console_path}/logging/src"/>
|
||||
<src path="${console_path}/io/src/main/java"/>
|
||||
<src path="${console_path}/logging-api/src/main/java"/>
|
||||
<src path="${console_path}/models/src"/>
|
||||
<src path="${launcher_path}/src"/>
|
||||
<src path="src/main/java"/>
|
||||
<src path="src/test/java"/>
|
||||
<exclude name="**/*Sandbox.java"/>
|
||||
</javac>
|
||||
|
||||
</target>
|
||||
|
||||
<target name="jar" depends="compile">
|
||||
<mkdir dir="${jar_file_folder}"/>
|
||||
<delete file="${jar_file}"/>
|
||||
|
||||
<copy todir="build/classes">
|
||||
<fileset dir="../ts_plugin_launcher/src/main/resources" includes="**/*.png"/>
|
||||
<fileset dir="${console_path}/shared_ui/resources" includes="**/*.png"/>
|
||||
</copy>
|
||||
|
||||
<tstamp>
|
||||
<format property="TODAY" pattern="yyyy-MM-dd HH:mm:ss"/>
|
||||
<format property="TODAY_DATE" pattern="yyyy-MM-dd"/>
|
||||
</tstamp>
|
||||
|
||||
<jar destfile="${jar_file}" basedir="build/classes">
|
||||
<manifest>
|
||||
<attribute name="Built-Date" value="${TODAY_DATE}"/>
|
||||
<attribute name="Built-Timestamp" value="${TODAY}"/>
|
||||
<attribute name="Signature-Vendor" value="rusEFI LLC"/>
|
||||
</manifest>
|
||||
|
||||
<zipfileset src="../../java_console/lib/httpclient.jar" includes="**/*.class"/>
|
||||
<zipfileset src="../../java_console/lib/httpcore.jar" includes="**/*.class"/>
|
||||
<zipfileset src="../../java_console/lib/httpmime.jar" includes="**/*.class"/>
|
||||
<zipfileset src="../../java_console/lib/javacan-core.jar" includes="**/*.class"/>
|
||||
<zipfileset src="../../java_console/lib/json-simple-1.1.1.jar" includes="**/*.class"/>
|
||||
<zipfileset src="../../java_console/lib/jSerialComm.jar" includes="**/*.class **/*.so **/*.dll **/*.jnilib"/>
|
||||
<!-- <zipfileset src="lib/commons-logging.jar" includes="**/*.class"/>-->
|
||||
</jar>
|
||||
|
||||
</target>
|
||||
|
||||
<target name="body_local_install" depends="jar">
|
||||
<copy file="${jar_file}" todir="${user.home}/.rusEFI/"/>
|
||||
</target>
|
||||
</project>
|
|
@ -1,59 +0,0 @@
|
|||
package com.rusefi;
|
||||
|
||||
import com.rusefi.tune.xml.Msq;
|
||||
import com.rusefi.xml.XmlUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.filechooser.FileSystemView;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Properties;
|
||||
|
||||
public class TsTuneReader {
|
||||
private static final String TS_USER_FILE = System.getProperty("user.home") + File.separator + ".efiAnalytics" + File.separator + "tsUser.properties";
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
String ecuName = "dev";
|
||||
|
||||
Msq tune = readTsTune(ecuName);
|
||||
System.out.println(tune);
|
||||
}
|
||||
|
||||
public static Msq readTsTune(String ecuName) throws Exception {
|
||||
String fileName = getTsTuneFileName(ecuName);
|
||||
return XmlUtil.readModel(Msq.class, fileName);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static String getTsTuneFileName(String ecuName) {
|
||||
String projectsDir = getProjectsDir();
|
||||
|
||||
return projectsDir + File.separator + ecuName + File.separator + "CurrentTune.msq";
|
||||
}
|
||||
|
||||
public static String getProjectsDir() {
|
||||
try {
|
||||
Properties tsUser = new Properties();
|
||||
tsUser.load(new FileInputStream(TS_USER_FILE));
|
||||
// reading TS properties
|
||||
return tsUser.getProperty("projectsDir");
|
||||
} catch (IOException e) {
|
||||
JFileChooser fr = new JFileChooser();
|
||||
FileSystemView fw = fr.getFileSystemView();
|
||||
File defaultDirectory = fw.getDefaultDirectory();
|
||||
|
||||
// fallback mechanism just in case
|
||||
return defaultDirectory + File.separator + "TunerStudioProjects";
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static String getProjectModeFileName(String projectName) {
|
||||
return getProjectsDir() +
|
||||
File.separator + projectName +
|
||||
File.separator + "projectCfg" +
|
||||
File.separator + "mainController.ini";
|
||||
}
|
||||
}
|
|
@ -1,127 +0,0 @@
|
|||
package com.rusefi.ts_plugin;
|
||||
|
||||
import com.rusefi.auth.AuthTokenUtil;
|
||||
import com.rusefi.autodetect.PortDetector;
|
||||
import com.rusefi.core.ui.AutoupdateUtil;
|
||||
import com.rusefi.proxy.NetworkConnector;
|
||||
import com.rusefi.proxy.NetworkConnectorContext;
|
||||
import com.rusefi.tools.VehicleToken;
|
||||
import com.rusefi.ui.AuthTokenPanel;
|
||||
import com.rusefi.ui.util.URLLabel;
|
||||
import org.putgemin.VerticalFlowLayout;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.datatransfer.Clipboard;
|
||||
import java.awt.datatransfer.StringSelection;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
|
||||
/**
|
||||
* @see PluginEntry
|
||||
*/
|
||||
public class BroadcastTab {
|
||||
private final JComponent content = new JPanel(new VerticalFlowLayout());
|
||||
|
||||
private final JLabel status = new JLabel();
|
||||
private final JButton disconnect = new JButton("Stop Broadcasting");
|
||||
private NetworkConnector networkConnector;
|
||||
|
||||
public BroadcastTab() {
|
||||
JButton broadcast = new JButton("Broadcast");
|
||||
disconnect.setEnabled(false);
|
||||
|
||||
broadcast.addActionListener(e -> {
|
||||
String authToken = AuthTokenPanel.getAuthToken();
|
||||
if (!AuthTokenUtil.isToken(authToken)) {
|
||||
status.setText("Auth token is required to broadcast ECU");
|
||||
return;
|
||||
}
|
||||
|
||||
new Thread(() -> {
|
||||
String autoDetectedPort = PortDetector.autoDetectSerial(null).getSerialPort();
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
startBroadcasting(authToken, autoDetectedPort);
|
||||
});
|
||||
|
||||
}).start();
|
||||
});
|
||||
|
||||
disconnect.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
networkConnector.close();
|
||||
disconnect.setEnabled(false);
|
||||
}
|
||||
});
|
||||
|
||||
content.add(createVehicleTokenPanel());
|
||||
|
||||
content.add(broadcast);
|
||||
content.add(status);
|
||||
content.add(disconnect);
|
||||
content.add(new URLLabel(RemoteTab.HOWTO_REMOTE_TUNING));
|
||||
content.add(new JLabel(PluginEntry.LOGO));
|
||||
|
||||
AutoupdateUtil.trueLayout(content);
|
||||
}
|
||||
|
||||
private Component createVehicleTokenPanel() {
|
||||
JLabel label = new JLabel();
|
||||
updateVehicleTokenLabel(label);
|
||||
|
||||
JButton copy = new JButton("Copy to clipboard");
|
||||
copy.addActionListener(new AbstractAction() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
StringSelection stringSelection = new StringSelection("" + VehicleToken.getOrCreate());
|
||||
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
|
||||
clipboard.setContents(stringSelection, null);
|
||||
}
|
||||
});
|
||||
|
||||
JButton reset = new JButton("Reset");
|
||||
reset.addActionListener(new AbstractAction() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
VehicleToken.refresh();
|
||||
updateVehicleTokenLabel(label);
|
||||
}
|
||||
});
|
||||
|
||||
JPanel panel = new JPanel(new FlowLayout());
|
||||
panel.add(label);
|
||||
panel.add(copy);
|
||||
panel.add(reset);
|
||||
return panel;
|
||||
}
|
||||
|
||||
private void updateVehicleTokenLabel(JLabel label) {
|
||||
label.setText("Vehicle access token: " + VehicleToken.getOrCreate());
|
||||
}
|
||||
|
||||
private void startBroadcasting(String authToken, String autoDetectedPort) {
|
||||
if (autoDetectedPort == null) {
|
||||
status.setText("<html>rusEFI ECU not detected.<br/>Please make sure that TunerStudio is currently not connected to ECU.</html>");
|
||||
} else {
|
||||
status.setText("rusEFI detected at " + autoDetectedPort);
|
||||
disconnect.setEnabled(true);
|
||||
|
||||
NetworkConnectorContext connectorContext = new NetworkConnectorContext();
|
||||
|
||||
new Thread(() -> {
|
||||
networkConnector = new NetworkConnector();
|
||||
|
||||
NetworkConnector.NetworkConnectorResult networkConnectorResult = networkConnector.start(NetworkConnector.Implementation.Plugin, authToken, autoDetectedPort, connectorContext);
|
||||
|
||||
SwingUtilities.invokeLater(() -> status.setText("One time password to connect to this ECU: " + networkConnectorResult.getOneTimeToken()));
|
||||
|
||||
}).start();
|
||||
}
|
||||
AutoupdateUtil.trueLayout(content);
|
||||
}
|
||||
|
||||
public JComponent getContent() {
|
||||
return content;
|
||||
}
|
||||
}
|
|
@ -1,140 +0,0 @@
|
|||
package com.rusefi.ts_plugin;
|
||||
|
||||
import com.devexperts.logging.Logging;
|
||||
import com.rusefi.autodetect.PortDetector;
|
||||
import com.rusefi.io.ConnectionStateListener;
|
||||
import com.rusefi.io.LinkManager;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import static com.devexperts.logging.Logging.getLogging;
|
||||
|
||||
/**
|
||||
* todo: move IO away from AWT thread
|
||||
*/
|
||||
public class ConnectPanel {
|
||||
private static final Logging log = getLogging(ConnectPanel.class);
|
||||
static final Executor IO_THREAD = Executors.newSingleThreadExecutor();
|
||||
private final JPanel content = new JPanel(new BorderLayout());
|
||||
private final JLabel status = new JLabel();
|
||||
|
||||
private LinkManager controllerConnector;
|
||||
private final JButton connect = new JButton("Connect");
|
||||
private final JButton disconnect = new JButton("Disconnect");
|
||||
private boolean isFirstAttempt = true;
|
||||
|
||||
public ConnectPanel(final ConnectionStateListener connectionStateListener) {
|
||||
JPanel flow = new JPanel(new FlowLayout());
|
||||
|
||||
disconnect.setEnabled(false);
|
||||
disconnect.addActionListener(new AbstractAction() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
controllerConnector.close();
|
||||
status.setText("Disconnected");
|
||||
disconnect.setEnabled(false);
|
||||
connect.setEnabled(true);
|
||||
}
|
||||
});
|
||||
|
||||
connect.addActionListener(e -> {
|
||||
if (isFirstAttempt) {
|
||||
isFirstAttempt = false;
|
||||
Window topFrame = SwingUtilities.getWindowAncestor(connect);
|
||||
log.info("Adding Window Listener to " + topFrame);
|
||||
topFrame.addWindowListener(new WindowAdapter() {
|
||||
@Override
|
||||
public void windowDeactivated(WindowEvent e) {
|
||||
log.info("topFrame windowDeactivated " + topFrame);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void windowClosing(WindowEvent e) {
|
||||
log.info("windowClosing " + topFrame);
|
||||
if (controllerConnector != null)
|
||||
controllerConnector.close();
|
||||
// I am super confused about the life cycle of parent Window
|
||||
connect.setEnabled(true);
|
||||
disconnect.setEnabled(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void windowClosed(WindowEvent e) {
|
||||
log.info("windowClosed " + topFrame);
|
||||
}
|
||||
});
|
||||
topFrame.addComponentListener(new ComponentAdapter() {
|
||||
@Override
|
||||
public void componentHidden(ComponentEvent e) {
|
||||
log.info("componentHidden " + topFrame);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
connect.setEnabled(false);
|
||||
status.setText("Looking for rusEFI...");
|
||||
|
||||
IO_THREAD.execute(() -> {
|
||||
controllerConnector = new LinkManager()
|
||||
.setCompositeLogicEnabled(false)
|
||||
.setNeedPullData(false);
|
||||
try {
|
||||
tryToConnect(connectionStateListener);
|
||||
} catch (Throwable er) {
|
||||
log.error("Error connecting", er);
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
status.setText("Some error, see logs.");
|
||||
connect.setEnabled(true);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
flow.add(connect);
|
||||
flow.add(disconnect);
|
||||
|
||||
content.add(flow, BorderLayout.NORTH);
|
||||
content.add(status, BorderLayout.SOUTH);
|
||||
}
|
||||
|
||||
private void tryToConnect(ConnectionStateListener connectionStateListener) {
|
||||
String autoDetectedPort = PortDetector.autoDetectSerial(null).getSerialPort();
|
||||
if (autoDetectedPort == null) {
|
||||
status.setText("rusEFI not found");
|
||||
connect.setEnabled(true);
|
||||
} else {
|
||||
controllerConnector.startAndConnect(autoDetectedPort, new ConnectionStateListener() {
|
||||
public void onConnectionEstablished() {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
status.setText("Connected to rusEFI " + autoDetectedPort);
|
||||
disconnect.setEnabled(true);
|
||||
connectionStateListener.onConnectionEstablished();
|
||||
});
|
||||
}
|
||||
|
||||
public void onConnectionFailed(String message) {
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public LinkManager getControllerConnector() {
|
||||
return controllerConnector;
|
||||
}
|
||||
|
||||
public static String getLastFour(String fileName) {
|
||||
int dotIndex = fileName.indexOf(".");
|
||||
fileName = fileName.substring(0, dotIndex);
|
||||
if (fileName.length() < 5)
|
||||
return fileName;
|
||||
return fileName.substring(fileName.length() - 4);
|
||||
}
|
||||
|
||||
public JComponent getContent() {
|
||||
return content;
|
||||
}
|
||||
}
|
|
@ -1,69 +0,0 @@
|
|||
package com.rusefi.ts_plugin;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.text.*;
|
||||
|
||||
public class IntegerDocumentFilter extends DocumentFilter {
|
||||
public static void install(JTextField jTextField) {
|
||||
PlainDocument doc = (PlainDocument) jTextField.getDocument();
|
||||
doc.setDocumentFilter(new IntegerDocumentFilter());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertString(FilterBypass fb, int offset, String string,
|
||||
AttributeSet attr) throws BadLocationException {
|
||||
|
||||
Document doc = fb.getDocument();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(doc.getText(0, doc.getLength()));
|
||||
sb.insert(offset, string);
|
||||
|
||||
if (test(sb.toString())) {
|
||||
super.insertString(fb, offset, string, attr);
|
||||
} else {
|
||||
// warn the user and don't allow the insert
|
||||
}
|
||||
}
|
||||
|
||||
private boolean test(String text) {
|
||||
try {
|
||||
Integer.parseInt(text);
|
||||
return true;
|
||||
} catch (NumberFormatException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void replace(FilterBypass fb, int offset, int length, String text,
|
||||
AttributeSet attrs) throws BadLocationException {
|
||||
|
||||
Document doc = fb.getDocument();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(doc.getText(0, doc.getLength()));
|
||||
sb.replace(offset, offset + length, text);
|
||||
|
||||
if (test(sb.toString())) {
|
||||
super.replace(fb, offset, length, text, attrs);
|
||||
} else {
|
||||
// warn the user and don't allow the insert
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(FilterBypass fb, int offset, int length)
|
||||
throws BadLocationException {
|
||||
Document doc = fb.getDocument();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(doc.getText(0, doc.getLength()));
|
||||
sb.delete(offset, offset + length);
|
||||
|
||||
if (test(sb.toString())) {
|
||||
super.remove(fb, offset, length);
|
||||
} else {
|
||||
// warn the user and don't allow the insert
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1,124 +0,0 @@
|
|||
package com.rusefi.ts_plugin;
|
||||
|
||||
import com.efiAnalytics.plugin.ecu.ControllerAccess;
|
||||
import com.rusefi.TsTuneReader;
|
||||
import com.rusefi.core.ui.AutoupdateUtil;
|
||||
import com.rusefi.tools.online.Online;
|
||||
import com.rusefi.tools.online.UploadResult;
|
||||
import org.apache.http.concurrent.FutureCallback;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
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;
|
||||
|
||||
public class LogUploadSelector {
|
||||
private final JPanel content = new JPanel(new BorderLayout());
|
||||
private final JLabel uploadState = new JLabel();
|
||||
|
||||
private final JPanel fileList = new JPanel(new VerticalFlowLayout());
|
||||
|
||||
private final Supplier<ControllerAccess> controllerAccessSupplier;
|
||||
|
||||
public LogUploadSelector(Supplier<ControllerAccess> controllerAccessSupplier) {
|
||||
this.controllerAccessSupplier = controllerAccessSupplier;
|
||||
|
||||
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[] ecuConfigurationNames = controllerAccessSupplier.get().getEcuConfigurationNames();
|
||||
if (ecuConfigurationNames != null && ecuConfigurationNames.length > 0) {
|
||||
String folder = getLogsFolderDir(ecuConfigurationNames[0]);
|
||||
processFolder(folder);
|
||||
}
|
||||
|
||||
AutoupdateUtil.trueLayout(content);
|
||||
}
|
||||
|
||||
private void processFolder(String folder) {
|
||||
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<UploadResult>() {
|
||||
@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);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static String getLogsFolderDir(String projectName) {
|
||||
return TsTuneReader.getProjectsDir() + File.separator + projectName + File.separator + "DataLogs";
|
||||
}
|
||||
|
||||
public JComponent getContent() {
|
||||
return content;
|
||||
}
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
package com.rusefi.ts_plugin;
|
||||
|
||||
import com.opensr5.ini.IniFileMetaInfo;
|
||||
import com.opensr5.ini.RawIniFile;
|
||||
import com.rusefi.TsTuneReader;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
|
||||
public class MetaDataCache {
|
||||
|
||||
private static String cachedProjectName;
|
||||
private static IniFileMetaInfo cache;
|
||||
|
||||
@Nullable
|
||||
public synchronized static IniFileMetaInfo getModel(String projectName) {
|
||||
if (projectName == null)
|
||||
return null;
|
||||
if (!projectName.equals(cachedProjectName)) {
|
||||
cache = null;
|
||||
}
|
||||
if (cache == null) {
|
||||
System.out.println("Reading meta " + projectName);
|
||||
String modeFileName = TsTuneReader.getProjectModeFileName(projectName);
|
||||
try {
|
||||
cache = new IniFileMetaInfo(RawIniFile.read(modeFileName));
|
||||
} catch (FileNotFoundException e) {
|
||||
System.out.println("No luck reading " + modeFileName + ": " + e);
|
||||
return null;
|
||||
}
|
||||
cachedProjectName = projectName;
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,111 +0,0 @@
|
|||
package com.rusefi.ts_plugin;
|
||||
|
||||
import com.efiAnalytics.plugin.ecu.ControllerAccess;
|
||||
import com.rusefi.core.ui.AutoupdateUtil;
|
||||
import com.rusefi.ts_plugin.auth.InstanceAuthContext;
|
||||
import com.rusefi.ts_plugin.util.ManifestHelper;
|
||||
import com.rusefi.tune.xml.Constant;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* {@link TsPluginLauncher} creates an instance of this class via reflection.
|
||||
*
|
||||
* @see TuneUploadTab upload tune & TODO upload logs
|
||||
* @see RemoteTab remote ECU access & control
|
||||
* @see BroadcastTab offer your ECU for remove access & control
|
||||
* @see PluginBodySandbox
|
||||
*/
|
||||
public class PluginEntry implements TsPluginBody {
|
||||
private final JPanel content = new JPanel(new BorderLayout());
|
||||
|
||||
static final ImageIcon LOGO = AutoupdateUtil.loadIcon("/rusefi_online_color_300.png");
|
||||
|
||||
private final JTabbedPane tabbedPane = new JTabbedPane();
|
||||
|
||||
/**
|
||||
* the real constructor - this one is invoked via reflection
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public PluginEntry() {
|
||||
this(ControllerAccess::getInstance);
|
||||
}
|
||||
|
||||
public PluginEntry(Supplier<ControllerAccess> controllerAccessSupplier) {
|
||||
System.out.println("PluginEntry init " + this);
|
||||
|
||||
if (isLauncherTooOld()) {
|
||||
content.add(new JLabel("<html>Please manually install latest plugin version<br/>Usually we can update to latest version but this time there was a major change.<br/>" +
|
||||
"Please use TunerStudio controls to update to plugin from recent rusEFI bundle."));
|
||||
return;
|
||||
}
|
||||
|
||||
TuneUploadTab tuneUploadTab = new TuneUploadTab(controllerAccessSupplier);
|
||||
LogUploadSelector logUploadTab = new LogUploadSelector(controllerAccessSupplier);
|
||||
BroadcastTab broadcastTab = new BroadcastTab();
|
||||
RemoteTab remoteTab = new RemoteTab();
|
||||
|
||||
tabbedPane.addTab("Tune Upload", tuneUploadTab.getContent());
|
||||
tabbedPane.addTab("Log Upload", logUploadTab.getContent());
|
||||
tabbedPane.addTab("Broadcast", broadcastTab.getContent());
|
||||
tabbedPane.addTab("Remote ECU", remoteTab.getContent());
|
||||
this.content.add(tabbedPane);
|
||||
|
||||
InstanceAuthContext.startup();
|
||||
}
|
||||
|
||||
public static String getNonDaemonThreads() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (Thread thread : Thread.getAllStackTraces().keySet()) {
|
||||
// Daemon thread will not prevent the JVM from exiting
|
||||
if (!thread.isDaemon())
|
||||
sb.append(thread.getName() + "\n");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private boolean isLauncherTooOld() {
|
||||
try {
|
||||
// at some point we did not have this field so using reflection for the next couple of months
|
||||
Field field = TsPluginLauncher.class.getField("BUILD_VERSION");
|
||||
int launcherVersion = (int) field.get(null);
|
||||
System.out.println("Launcher version " + launcherVersion + " detected");
|
||||
return false;
|
||||
} catch (NoSuchFieldException | IllegalAccessException e) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isEmpty(Constant constant) {
|
||||
if (constant == null)
|
||||
return true;
|
||||
return isEmpty(constant.getValue());
|
||||
}
|
||||
|
||||
private static boolean isEmpty(String value) {
|
||||
return value == null || value.trim().length() == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JComponent getContent() {
|
||||
return content;
|
||||
}
|
||||
/*
|
||||
public void close() {
|
||||
PersistentConfiguration.getConfig().save();
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* this method is invoked by refection
|
||||
*
|
||||
* @see TsPluginBody#GET_VERSION
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public static String getVersion() {
|
||||
return ManifestHelper.getVersion();
|
||||
}
|
||||
}
|
|
@ -1,358 +0,0 @@
|
|||
package com.rusefi.ts_plugin;
|
||||
|
||||
import com.rusefi.NamedThreadFactory;
|
||||
import com.rusefi.core.SignatureHelper;
|
||||
import com.rusefi.Timeouts;
|
||||
import com.rusefi.core.ui.AutoupdateUtil;
|
||||
import com.rusefi.core.Pair;
|
||||
import com.rusefi.io.serial.StreamStatistics;
|
||||
import com.rusefi.io.tcp.ServerSocketReference;
|
||||
import com.rusefi.io.tcp.TcpIoStream;
|
||||
import com.rusefi.proxy.NetworkConnector;
|
||||
import com.rusefi.proxy.client.LocalApplicationProxy;
|
||||
import com.rusefi.proxy.client.LocalApplicationProxyContextImpl;
|
||||
import com.rusefi.proxy.client.UpdateType;
|
||||
import com.rusefi.core.rusEFIVersion;
|
||||
import com.rusefi.server.ApplicationRequest;
|
||||
import com.rusefi.server.ControllerInfo;
|
||||
import com.rusefi.server.SessionDetails;
|
||||
import com.rusefi.server.UserDetails;
|
||||
import com.rusefi.tools.online.HttpUtil;
|
||||
import com.rusefi.tools.online.ProxyClient;
|
||||
import com.rusefi.tools.online.PublicSession;
|
||||
import com.rusefi.ts_plugin.auth.InstanceAuthContext;
|
||||
import com.rusefi.ts_plugin.auth.SelfInfo;
|
||||
import com.rusefi.ui.AuthTokenPanel;
|
||||
import com.rusefi.ui.util.URLLabel;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.putgemin.VerticalFlowLayout;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import static com.rusefi.core.preferences.storage.PersistentConfiguration.getConfig;
|
||||
|
||||
/**
|
||||
* remote ECU access & control
|
||||
*
|
||||
* @see RemoteTabSandbox
|
||||
* @see PluginEntry
|
||||
*/
|
||||
public class RemoteTab {
|
||||
private static final String APPLICATION_PORT = "application_port";
|
||||
public static final String HOWTO_REMOTE_TUNING = "https://github.com/rusefi/rusefi/wiki/HOWTO-Remote-Tuning";
|
||||
private final JComponent content = new JPanel(new BorderLayout());
|
||||
private final JScrollPane scroll = new JScrollPane(content, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
|
||||
|
||||
private final JPanel list = new JPanel(new VerticalFlowLayout());
|
||||
private final JTextField oneTimePasswordControl = new JTextField("0") {
|
||||
@Override
|
||||
public Dimension getPreferredSize() {
|
||||
Dimension size = super.getPreferredSize();
|
||||
// todo: dynamic calculation of desired with based on String width?
|
||||
return new Dimension(100, size.height);
|
||||
}
|
||||
};
|
||||
|
||||
private StreamStatusControl streamStatusControl = null;
|
||||
|
||||
private final JButton disconnect = new JButton("Disconnect");
|
||||
|
||||
private final Executor listDownloadExecutor = Executors.newSingleThreadExecutor(new NamedThreadFactory("online list downloader", true));
|
||||
|
||||
public RemoteTab() {
|
||||
JButton refresh = new JButton("Refresh Remote Controllers List");
|
||||
refresh.addActionListener(e -> requestControllersList());
|
||||
|
||||
disconnect.addActionListener(e -> {
|
||||
LocalApplicationProxy localApplicationProxy = RemoteTabController.INSTANCE.getLocalApplicationProxy();
|
||||
if (localApplicationProxy != null)
|
||||
localApplicationProxy.close();
|
||||
RemoteTabController.INSTANCE.setState(RemoteTabController.State.NOT_CONNECTED);
|
||||
requestControllersList();
|
||||
});
|
||||
|
||||
|
||||
Timer timer = new Timer(Timeouts.SECOND, new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
if (streamStatusControl != null)
|
||||
streamStatusControl.update();
|
||||
}
|
||||
});
|
||||
timer.start();
|
||||
|
||||
JTextField applicationPort = new JTextField() {
|
||||
@Override
|
||||
public Dimension getPreferredSize() {
|
||||
Dimension size = super.getPreferredSize();
|
||||
// todo: dynamic calculation of desired with based on String width?
|
||||
return new Dimension(100, size.height);
|
||||
}
|
||||
};
|
||||
IntegerDocumentFilter.install(applicationPort);
|
||||
IntegerDocumentFilter.install(oneTimePasswordControl);
|
||||
String portProperty = getLocalPort();
|
||||
applicationPort.setText(portProperty);
|
||||
|
||||
JPanel topLines = new JPanel(new VerticalFlowLayout());
|
||||
|
||||
topLines.add(new URLLabel(HOWTO_REMOTE_TUNING));
|
||||
topLines.add(new SelfInfo().getContent());
|
||||
topLines.add(refresh);
|
||||
topLines.add(new JLabel("Local Port for tuning software"));
|
||||
topLines.add(applicationPort);
|
||||
topLines.add(new JLabel("One time password:"));
|
||||
topLines.add(oneTimePasswordControl);
|
||||
|
||||
content.add(topLines, BorderLayout.NORTH);
|
||||
content.add(list, BorderLayout.CENTER);
|
||||
list.add(new JLabel("Requesting list of ECUs"));
|
||||
|
||||
InstanceAuthContext.listeners.add(userDetails -> requestControllersList());
|
||||
|
||||
LocalApplicationProxy currentState = RemoteTabController.INSTANCE.getLocalApplicationProxy();
|
||||
if (currentState == null) {
|
||||
requestControllersList();
|
||||
} else {
|
||||
setConnectedStatus(currentState.getApplicationRequest().getVehicleOwner(), null,
|
||||
currentState.getApplicationRequest().getSessionDetails().getControllerInfo());
|
||||
}
|
||||
}
|
||||
|
||||
private String getLocalPort() {
|
||||
return getConfig().getRoot().getProperty(APPLICATION_PORT, "29001");
|
||||
}
|
||||
|
||||
private void requestControllersList() {
|
||||
listDownloadExecutor.execute(() -> {
|
||||
try {
|
||||
List<PublicSession> userDetails = ProxyClient.getOnlineApplications(HttpUtil.PROXY_JSON_API_HTTP_PORT);
|
||||
SwingUtilities.invokeLater(() -> showList(userDetails));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void showList(List<PublicSession> userDetails) {
|
||||
if (RemoteTabController.INSTANCE.getState() != RemoteTabController.State.NOT_CONNECTED)
|
||||
return;
|
||||
list.removeAll();
|
||||
if (userDetails.isEmpty()) {
|
||||
list.add(new JLabel("No ECUs are broadcasting at the moment :("));
|
||||
} else {
|
||||
|
||||
JPanel verticalPanel = new JPanel(new VerticalFlowLayout());
|
||||
list.add(verticalPanel);
|
||||
|
||||
for (PublicSession user : userDetails) {
|
||||
verticalPanel.add(createControllerRow(user));
|
||||
}
|
||||
}
|
||||
AutoupdateUtil.trueLayout(list);
|
||||
}
|
||||
|
||||
private JComponent createControllerRow(PublicSession publicSession) {
|
||||
ControllerInfo controllerInfo = publicSession.getControllerInfo();
|
||||
|
||||
JComponent topLine = new JPanel(new FlowLayout());
|
||||
topLine.add(new JLabel(publicSession.getVehicleOwner().getUserName()));
|
||||
topLine.add(new JLabel(controllerInfo.getVehicleName() + " " + controllerInfo.getEngineMake() + " " + controllerInfo.getEngineCode()));
|
||||
|
||||
JPanel bottomPanel = new JPanel(new VerticalFlowLayout());
|
||||
|
||||
if (publicSession.isUsed()) {
|
||||
bottomPanel.add(new JLabel(" Used by " + publicSession.getTunerName()));
|
||||
} else {
|
||||
JButton connect = new JButton("Connect to " + publicSession.getVehicleOwner().getUserName() + " ECU");
|
||||
connect.addActionListener(event -> connectToProxy(publicSession));
|
||||
bottomPanel.add(connect);
|
||||
|
||||
if (InstanceAuthContext.isOurController(publicSession.getVehicleOwner().getUserId())) {
|
||||
|
||||
if (publicSession.getImplementation().equals(NetworkConnector.Implementation.SBC.name())) {
|
||||
|
||||
JPanel updateSoftwarePanel = new JPanel(new FlowLayout());
|
||||
|
||||
|
||||
JButton updateSoftwareLatest = new JButton("Update Connector to Latest");
|
||||
updateSoftwareLatest.addActionListener(new AbstractAction() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
requestUpdate(publicSession, updateSoftwareLatest, UpdateType.CONTROLLER);
|
||||
}
|
||||
});
|
||||
|
||||
JButton updateSoftwareRelease = new JButton("Update Connector to Release");
|
||||
updateSoftwareRelease.addActionListener(new AbstractAction() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
requestUpdate(publicSession, updateSoftwareRelease, UpdateType.CONTROLLER_RELEASE);
|
||||
}
|
||||
});
|
||||
updateSoftwarePanel.add(updateSoftwareLatest);
|
||||
updateSoftwarePanel.add(updateSoftwareRelease);
|
||||
|
||||
bottomPanel.add(updateSoftwarePanel);
|
||||
}
|
||||
|
||||
JPanel updateFirmwarePanel = createUpdateFirmwarePanel(publicSession);
|
||||
|
||||
bottomPanel.add(updateFirmwarePanel);
|
||||
}
|
||||
}
|
||||
|
||||
JPanel userPanel = new JPanel(new BorderLayout());
|
||||
|
||||
JPanel infoLine = new JPanel(new FlowLayout());
|
||||
infoLine.add(new JLabel("Age " + publicSession.getAge()));
|
||||
infoLine.add(getSignatureDownload(controllerInfo));
|
||||
|
||||
userPanel.add(topLine, BorderLayout.NORTH);
|
||||
userPanel.add(infoLine, BorderLayout.CENTER);
|
||||
userPanel.add(bottomPanel, BorderLayout.SOUTH);
|
||||
|
||||
userPanel.setBorder(BorderFactory.createLineBorder(Color.BLACK));
|
||||
|
||||
return userPanel;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private JPanel createUpdateFirmwarePanel(PublicSession publicSession) {
|
||||
JButton updateFirmwareLatest = new JButton("Update ECU to latest");
|
||||
updateFirmwareLatest.addActionListener(new AbstractAction() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
requestUpdate(publicSession, updateFirmwareLatest, UpdateType.FIRMWARE);
|
||||
}
|
||||
});
|
||||
|
||||
JButton updateFirmwareRelease = new JButton("Update ECU to release");
|
||||
updateFirmwareRelease.addActionListener(new AbstractAction() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
requestUpdate(publicSession, updateFirmwareRelease, UpdateType.FIRMWARE_RELEASE);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
JPanel updateFirmwarePanel = new JPanel(new FlowLayout());
|
||||
updateFirmwarePanel.add(updateFirmwareLatest);
|
||||
updateFirmwarePanel.add(updateFirmwareRelease);
|
||||
return updateFirmwarePanel;
|
||||
}
|
||||
|
||||
private void requestUpdate(PublicSession publicSession, JButton updateSoftware, UpdateType type) {
|
||||
try {
|
||||
LocalApplicationProxy.requestSoftwareUpdate(HttpUtil.PROXY_JSON_API_HTTP_PORT,
|
||||
getApplicationRequest(publicSession), type);
|
||||
updateSoftware.setText("Update requested");
|
||||
} catch (IOException ioException) {
|
||||
ioException.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private URLLabel getSignatureDownload(ControllerInfo controllerInfo) {
|
||||
Pair<String, String> url = SignatureHelper.getUrl(controllerInfo.getSignature());
|
||||
|
||||
return new URLLabel(url.second, url.first);
|
||||
}
|
||||
|
||||
private void connectToProxy(PublicSession publicSession) {
|
||||
RemoteTabController.INSTANCE.setState(RemoteTabController.State.CONNECTING);
|
||||
setStatus("Connecting to " + publicSession.getVehicleOwner().getUserName());
|
||||
|
||||
LocalApplicationProxy.ConnectionListener connectionListener = (localApplicationProxy, authenticatorToProxyStream) -> {
|
||||
RemoteTabController.INSTANCE.setConnected(localApplicationProxy);
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
setConnectedStatus(publicSession.getVehicleOwner(), authenticatorToProxyStream, publicSession.getControllerInfo());
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
new Thread(() -> {
|
||||
runAuthenticator(publicSession, connectionListener);
|
||||
}, "Authenticator").start();
|
||||
}
|
||||
|
||||
private void setConnectedStatus(UserDetails userDetails, StreamStatistics authenticatorToProxyStream, ControllerInfo controllerInfo) {
|
||||
if (authenticatorToProxyStream != null) {
|
||||
streamStatusControl = new StreamStatusControl(authenticatorToProxyStream);
|
||||
}
|
||||
|
||||
setStatus("Connected to " + userDetails.getUserName(),
|
||||
new JLabel("You can now connect your TunerStudio to IP address localhost and port " + getLocalPort()),
|
||||
new URLLabel(SignatureHelper.getUrl(controllerInfo.getSignature()).first),
|
||||
disconnect, streamStatusControl == null ? null : streamStatusControl.getContent());
|
||||
}
|
||||
|
||||
private void setStatus(String text, JComponent... extra) {
|
||||
list.removeAll();
|
||||
list.add(new JLabel(text));
|
||||
for (JComponent component : extra) {
|
||||
if (component != null) {
|
||||
list.add(component);
|
||||
}
|
||||
}
|
||||
AutoupdateUtil.trueLayout(list);
|
||||
}
|
||||
|
||||
private void runAuthenticator(PublicSession publicSession, LocalApplicationProxy.ConnectionListener connectionListener) {
|
||||
ApplicationRequest applicationRequest = getApplicationRequest(publicSession);
|
||||
|
||||
try {
|
||||
AtomicReference<ServerSocketReference> serverHolderAtomicReference = new AtomicReference<>();
|
||||
|
||||
TcpIoStream.DisconnectListener disconnectListener = message -> SwingUtilities.invokeLater(() -> {
|
||||
System.out.println("Disconnected " + message);
|
||||
setStatus("Disconnected: " + message);
|
||||
RemoteTabController.INSTANCE.setState(RemoteTabController.State.NOT_CONNECTED);
|
||||
ServerSocketReference serverHolder = serverHolderAtomicReference.get();
|
||||
if (serverHolder != null)
|
||||
serverHolder.close();
|
||||
});
|
||||
|
||||
LocalApplicationProxyContextImpl context = new LocalApplicationProxyContextImpl() {
|
||||
@Override
|
||||
public int authenticatorPort() {
|
||||
return Integer.parseInt(getLocalPort());
|
||||
}
|
||||
};
|
||||
ServerSocketReference serverHolder = LocalApplicationProxy.startAndRun(
|
||||
context,
|
||||
applicationRequest,
|
||||
HttpUtil.PROXY_JSON_API_HTTP_PORT, disconnectListener, connectionListener);
|
||||
serverHolderAtomicReference.set(serverHolder);
|
||||
} catch (IOException e) {
|
||||
setStatus("IO error: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private ApplicationRequest getApplicationRequest(PublicSession publicSession) {
|
||||
SessionDetails sessionDetails = new SessionDetails(NetworkConnector.Implementation.Plugin,
|
||||
publicSession.getControllerInfo(), AuthTokenPanel.getAuthToken(),
|
||||
Integer.parseInt(oneTimePasswordControl.getText()), rusEFIVersion.CONSOLE_VERSION);
|
||||
|
||||
return new ApplicationRequest(sessionDetails, publicSession.getVehicleOwner());
|
||||
}
|
||||
|
||||
public JComponent getContent() {
|
||||
return scroll;
|
||||
}
|
||||
|
||||
interface Listener {
|
||||
void onConnected();
|
||||
}
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
package com.rusefi.ts_plugin;
|
||||
|
||||
import com.rusefi.proxy.client.LocalApplicationProxy;
|
||||
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
public enum RemoteTabController {
|
||||
/**
|
||||
* TunerStudio likes to close plugin panel, we need a singleton to preserve the state
|
||||
*/
|
||||
INSTANCE;
|
||||
|
||||
private State state = State.NOT_CONNECTED;
|
||||
private LocalApplicationProxy localApplicationProxy;
|
||||
|
||||
public final CopyOnWriteArrayList<Listener> listeners = new CopyOnWriteArrayList<>();
|
||||
|
||||
public synchronized void setState(State state) {
|
||||
localApplicationProxy = null;
|
||||
if (state != this.state) {
|
||||
this.state = state;
|
||||
for (Listener listener : listeners)
|
||||
listener.onChange(state);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized State getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public synchronized void setConnected(LocalApplicationProxy localApplicationProxy) {
|
||||
setState(State.CONNECTED);
|
||||
this.localApplicationProxy = localApplicationProxy;
|
||||
}
|
||||
|
||||
public LocalApplicationProxy getLocalApplicationProxy() {
|
||||
return localApplicationProxy;
|
||||
}
|
||||
|
||||
public enum State {
|
||||
NOT_CONNECTED,
|
||||
CONNECTING,
|
||||
CONNECTED
|
||||
}
|
||||
|
||||
interface Listener {
|
||||
void onChange(State state);
|
||||
}
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
package com.rusefi.ts_plugin;
|
||||
|
||||
import com.rusefi.io.serial.StreamStatistics;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
public class StreamStatusControl {
|
||||
private final StreamStatistics authenticatorToProxyStream;
|
||||
|
||||
private final JLabel content = new JLabel();
|
||||
|
||||
public StreamStatusControl(StreamStatistics statistics) {
|
||||
this.authenticatorToProxyStream = statistics;
|
||||
}
|
||||
|
||||
public void update() {
|
||||
content.setText("In " + authenticatorToProxyStream.getBytesIn() + " Out " + authenticatorToProxyStream.getBytesOut());
|
||||
}
|
||||
|
||||
public JComponent getContent() {
|
||||
return content;
|
||||
}
|
||||
}
|
|
@ -1,210 +0,0 @@
|
|||
package com.rusefi.ts_plugin;
|
||||
|
||||
import com.efiAnalytics.plugin.ecu.ControllerAccess;
|
||||
import com.efiAnalytics.plugin.ecu.ControllerException;
|
||||
import com.efiAnalytics.plugin.ecu.ControllerParameterChangeListener;
|
||||
import com.opensr5.ini.IniFileModel;
|
||||
import com.opensr5.ini.field.IniField;
|
||||
import com.rusefi.NamedThreadFactory;
|
||||
import com.rusefi.TsTuneReader;
|
||||
import com.rusefi.config.generated.Fields;
|
||||
import com.rusefi.tools.online.Online;
|
||||
import com.rusefi.tools.online.UploadResult;
|
||||
import com.rusefi.ts_plugin.util.ManifestHelper;
|
||||
import com.rusefi.tune.xml.Msq;
|
||||
import com.rusefi.ui.AuthTokenPanel;
|
||||
import com.rusefi.ui.util.URLLabel;
|
||||
import org.apache.http.concurrent.FutureCallback;
|
||||
import org.putgemin.VerticalFlowLayout;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* @see PluginBodySandbox
|
||||
*/
|
||||
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"));
|
||||
private static final ThreadFactory THREAD_FACTORY = new NamedThreadFactory("Tune Upload", true);
|
||||
|
||||
private static final String REO_URL = "https://rusefi.com/online/";
|
||||
private final AuthTokenPanel tokenPanel = new AuthTokenPanel();
|
||||
|
||||
private final Supplier<ControllerAccess> controllerAccessSupplier;
|
||||
|
||||
private final UploadView uploadView = new UploadView();
|
||||
|
||||
private final JButton upload = new JButton("Upload Current Tune");
|
||||
|
||||
private String projectName;
|
||||
|
||||
private final UploaderStatus uploaderStatus = new UploaderStatus();
|
||||
|
||||
private final Timer timer = new Timer(AUTO_UPDATE_AGGREGATION, new AbstractAction() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
// System.out.println("Timer! " + System.currentTimeMillis() + " " + timer + " " + e);
|
||||
if (UploadView.isAutoUpload()) {
|
||||
System.out.println(new Date() + ": enqueue tune");
|
||||
UploadQueue.enqueue(controllerAccessSupplier.get(), projectName);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
private final ControllerParameterChangeListener listener;
|
||||
|
||||
public TuneUploadTab(Supplier<ControllerAccess> controllerAccessSupplier) {
|
||||
this.controllerAccessSupplier = controllerAccessSupplier;
|
||||
|
||||
timer.stop();
|
||||
timer.setRepeats(false);
|
||||
UploadQueue.start();
|
||||
listener = parameterName -> {
|
||||
// System.out.println("Parameter value changed " + parameterName);
|
||||
timer.restart();
|
||||
};
|
||||
upload.setBackground(new Color(0x90EE90));
|
||||
|
||||
Thread t = THREAD_FACTORY.newThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
while (true) {
|
||||
String configurationName = getConfigurationName();
|
||||
if ((projectName == null && configurationName != null)
|
||||
|| !projectName.equals(configurationName)) {
|
||||
handleConfigurationChange(configurationName, controllerAccessSupplier.get());
|
||||
}
|
||||
|
||||
boolean isProjectActive = configurationName != null;
|
||||
uploaderStatus.updateProjectStatus(configurationName, isProjectActive);
|
||||
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
updateUploadEnabled();
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
t.start();
|
||||
|
||||
upload.addActionListener(new AbstractAction() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
if (!tokenPanel.hasToken()) {
|
||||
tokenPanel.showError(content);
|
||||
return;
|
||||
}
|
||||
String configurationName = getConfigurationName();
|
||||
if (configurationName == null) {
|
||||
JOptionPane.showMessageDialog(content, "No project opened");
|
||||
return;
|
||||
}
|
||||
uploadView.uploadState.setVisible(true);
|
||||
uploadView.uploadState.setText("Uploading...");
|
||||
|
||||
Msq tune = TuneUploder.writeCurrentTune(controllerAccessSupplier.get(), configurationName);
|
||||
Online.uploadTune(tune, tokenPanel, content, new FutureCallback<UploadResult>() {
|
||||
@Override
|
||||
public void completed(UploadResult array) {
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
UploadView.setResult(array, uploadView.uploadState);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Exception e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelled() {
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
content.add(new JLabel("Version " + ManifestHelper.getBuildTimestamp()));
|
||||
// content.add(new JLabel("Active project: " + getConfigurationName()));
|
||||
|
||||
|
||||
content.add(uploadView.getContent());
|
||||
content.add(upload);
|
||||
|
||||
content.add(new JLabel(PluginEntry.LOGO));
|
||||
content.add(tokenPanel.getContent());
|
||||
content.add(new URLLabel(REO_URL));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is invoked every time we defect a switch between projects
|
||||
*/
|
||||
private void handleConfigurationChange(String projectName, ControllerAccess controllerAccess) {
|
||||
uploaderStatus.readTuneState(projectName);
|
||||
|
||||
if (projectName != null) {
|
||||
subscribeToUpdates(projectName, controllerAccess);
|
||||
}
|
||||
|
||||
updateUploadEnabled();
|
||||
|
||||
this.projectName = projectName;
|
||||
}
|
||||
|
||||
private void updateUploadEnabled() {
|
||||
uploadView.update(uploaderStatus);
|
||||
|
||||
upload.setEnabled(uploaderStatus.isTuneOk() && uploaderStatus.isProjectIsOk());
|
||||
}
|
||||
|
||||
private void subscribeToUpdates(String configurationName, ControllerAccess controllerAccess) {
|
||||
IniFileModel model = new IniFileModel().readIniFile(TsTuneReader.getProjectModeFileName(configurationName));
|
||||
Map<String, IniField> allIniFields = model.allIniFields;
|
||||
if (model.allIniFields == null)
|
||||
return;
|
||||
for (Map.Entry<String, IniField> field : allIniFields.entrySet()) {
|
||||
boolean isOnlineTuneField = field.getValue().getOffset() >= Fields.engine_configuration_s_size;
|
||||
if (!isOnlineTuneField) {
|
||||
try {
|
||||
controllerAccess.getControllerParameterServer().subscribe(configurationName, field.getKey(), listener);
|
||||
} catch (ControllerException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String getConfigurationName() {
|
||||
ControllerAccess controllerAccess = controllerAccessSupplier.get();
|
||||
if (controllerAccess == null) {
|
||||
System.out.println("No ControllerAccess");
|
||||
return null;
|
||||
}
|
||||
String[] configurationNames = controllerAccess.getEcuConfigurationNames();
|
||||
if (configurationNames.length == 0)
|
||||
return null;
|
||||
return configurationNames[0];
|
||||
}
|
||||
|
||||
public JComponent getContent() {
|
||||
return content;
|
||||
}
|
||||
}
|
|
@ -1,140 +0,0 @@
|
|||
package com.rusefi.ts_plugin;
|
||||
|
||||
import com.devexperts.logging.Logging;
|
||||
import com.efiAnalytics.plugin.ecu.ControllerAccess;
|
||||
import com.efiAnalytics.plugin.ecu.ControllerException;
|
||||
import com.efiAnalytics.plugin.ecu.ControllerParameter;
|
||||
import com.efiAnalytics.plugin.ecu.servers.ControllerParameterServer;
|
||||
import com.opensr5.ini.IniFileMetaInfo;
|
||||
import com.rusefi.TsTuneReader;
|
||||
import com.rusefi.tools.online.Online;
|
||||
import com.rusefi.tune.xml.Constant;
|
||||
import com.rusefi.tune.xml.Msq;
|
||||
import com.rusefi.tune.xml.Page;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.xml.bind.JAXBException;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.TreeMap;
|
||||
|
||||
public class TuneUploder {
|
||||
private final static Logging log = Logging.getLogging(TuneUploder.class);
|
||||
|
||||
static Msq writeCurrentTune(ControllerAccess controllerAccess, String configurationName) {
|
||||
Msq msq = grabTune(controllerAccess, configurationName);
|
||||
if (msq == null)
|
||||
return null;
|
||||
try {
|
||||
String fileName = Online.outputXmlFileName;
|
||||
msq.writeXmlFile(fileName);
|
||||
return msq;
|
||||
} catch (JAXBException | IOException e) {
|
||||
System.out.println("Error writing XML: " + e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Msq grabTune(ControllerAccess controllerAccess, String configurationName) {
|
||||
Objects.requireNonNull(controllerAccess, "controllerAccess");
|
||||
IniFileMetaInfo meta = MetaDataCache.getModel(configurationName);
|
||||
if (meta == null)
|
||||
return null;
|
||||
Msq msq = Msq.create(meta.getTotalSize(), meta.getSignature());
|
||||
ControllerParameterServer controllerParameterServer = controllerAccess.getControllerParameterServer();
|
||||
Objects.requireNonNull(controllerParameterServer, "controllerParameterServer");
|
||||
|
||||
Map<String, Constant> fileSystemValues = getFileSystemValues(configurationName);
|
||||
|
||||
try {
|
||||
String[] parameterNames = controllerParameterServer.getParameterNames(configurationName);
|
||||
for (String parameterName : parameterNames) {
|
||||
if (!fileSystemValues.containsKey(parameterName)) {
|
||||
System.out.println("Skipping " + parameterName + " since not in model, maybe pcVariable?");
|
||||
continue;
|
||||
}
|
||||
applyParameterValue(configurationName, msq, controllerParameterServer, fileSystemValues, parameterName);
|
||||
}
|
||||
} catch (ControllerException e) {
|
||||
System.out.println("Error saving configuration: " + e);
|
||||
return null;
|
||||
}
|
||||
return msq;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
static Map<String, Constant> getFileSystemValues(String configurationName) {
|
||||
if (configurationName == null)
|
||||
return Collections.emptyMap();
|
||||
Msq tsTune;
|
||||
try {
|
||||
tsTune = TsTuneReader.readTsTune(configurationName);
|
||||
} catch (Exception e) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
Map<String, Constant> byName = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
|
||||
for (Constant c : tsTune.findPage().constant) {
|
||||
byName.put(c.getName(), c);
|
||||
}
|
||||
return byName;
|
||||
}
|
||||
|
||||
private static void applyParameterValue(String configurationName, Msq msq, ControllerParameterServer controllerParameterServer, Map<String, Constant> byName, String parameterName) throws ControllerException {
|
||||
ControllerParameter cp = controllerParameterServer.getControllerParameter(configurationName, parameterName);
|
||||
Objects.requireNonNull(cp, "ControllerParameter");
|
||||
String type = cp.getParamClass();
|
||||
String value;
|
||||
if (ControllerParameter.PARAM_CLASS_BITS.equals(type)) {
|
||||
value = cp.getStringValue();
|
||||
log.info("bits " + parameterName + ": " + value);
|
||||
} else if (ControllerParameter.PARAM_CLASS_SCALAR.equals(type)) {
|
||||
value = toString(cp.getScalarValue(), cp.getDecimalPlaces());
|
||||
System.out.println("TsPlugin scalar " + parameterName + ": " + cp.getScalarValue() + "/" + cp.getStringValue());
|
||||
|
||||
} else if (ControllerParameter.PARAM_CLASS_ARRAY.equals(type)) {
|
||||
value = getArrayValue(cp.getArrayValues());
|
||||
} else if ("string".equals(type)) {
|
||||
//value = cp.getStringValue();
|
||||
// WOW hack
|
||||
// TS does not provide values for string parameters?! so we read the file directly
|
||||
Constant constant = byName.get(parameterName);
|
||||
if (constant == null) {
|
||||
System.out.println("Not found in TS tune " + parameterName);
|
||||
value = null;
|
||||
} else {
|
||||
value = constant.getValue();
|
||||
System.out.println("TsPlugin name=" + parameterName + " string=" + cp.getStringValue() + "/h=" + value);
|
||||
}
|
||||
} else {
|
||||
System.out.println("TsPlugin name=" + parameterName + " unexpected type " + type + "/" + cp.getStringValue());
|
||||
value = cp.getStringValue();
|
||||
}
|
||||
|
||||
Page page = msq.findPage();
|
||||
page.constant.add(new Constant(parameterName, cp.getUnits(), value, Integer.toString(cp.getDecimalPlaces())));
|
||||
}
|
||||
|
||||
private static String getArrayValue(double[][] arrayValues) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int rowIndex = 0; rowIndex < arrayValues.length; rowIndex++) {
|
||||
double[] array = arrayValues[rowIndex];
|
||||
sb.append("\n\t");
|
||||
for (int colIndex = 0; colIndex < array.length; colIndex++) {
|
||||
double value = array[colIndex];
|
||||
sb.append(' ');
|
||||
sb.append(value);
|
||||
}
|
||||
}
|
||||
sb.append("\n");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static String toString(double scalarValue, int decimalPlaces) {
|
||||
// todo: start using decimalPlaces parameter!
|
||||
return Double.toString(scalarValue);
|
||||
}
|
||||
}
|
|
@ -1,140 +0,0 @@
|
|||
package com.rusefi.ts_plugin;
|
||||
|
||||
import com.devexperts.logging.Logging;
|
||||
import com.efiAnalytics.plugin.ecu.ControllerAccess;
|
||||
import com.rusefi.core.FileUtil;
|
||||
import com.rusefi.tools.online.Online;
|
||||
import com.rusefi.tools.online.UploadResult;
|
||||
import com.rusefi.tune.xml.Msq;
|
||||
import com.rusefi.ui.AuthTokenPanel;
|
||||
|
||||
import javax.xml.bind.JAXBException;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.LinkedBlockingDeque;
|
||||
|
||||
public class UploadQueue {
|
||||
private final static Logging log = Logging.getLogging(UploadQueue.class);
|
||||
|
||||
public static final String OUTBOX_FOLDER = FileUtil.RUSEFI_SETTINGS_FOLDER + File.separator + "outbox";
|
||||
private static final LinkedBlockingDeque<FileAndFolder> queue = new LinkedBlockingDeque<>(128);
|
||||
|
||||
private static boolean isStarted;
|
||||
|
||||
public synchronized static void start() {
|
||||
if (isStarted)
|
||||
return;
|
||||
isStarted = true;
|
||||
readOutbox();
|
||||
Thread t = new Thread(() -> {
|
||||
try {
|
||||
uploadLoop();
|
||||
} catch (InterruptedException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}, "Posting Thread");
|
||||
t.setDaemon(true);
|
||||
t.start();
|
||||
}
|
||||
|
||||
private static void readOutbox() {
|
||||
File folder = new File(OUTBOX_FOLDER);
|
||||
if (!folder.exists())
|
||||
return;
|
||||
String[] files = folder.list((dir, name) -> name.endsWith(".msq"));
|
||||
if (files == null)
|
||||
return;
|
||||
|
||||
for (String file : files) {
|
||||
if (queue.size() > 90)
|
||||
return;
|
||||
System.out.println(UploadQueue.class.getSimpleName() + " readOutbox " + file);
|
||||
try {
|
||||
queue.put(new FileAndFolder(OUTBOX_FOLDER, file));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
System.out.println(UploadQueue.class.getSimpleName() + " readOutbox got " + queue.size());
|
||||
}
|
||||
|
||||
@SuppressWarnings("InfiniteLoopStatement")
|
||||
private static void uploadLoop() throws InterruptedException {
|
||||
while (true) {
|
||||
FileAndFolder file = queue.take();
|
||||
|
||||
UploadResult result = Online.upload(new File(file.getFullName()), AuthTokenPanel.getAuthToken());
|
||||
System.out.println("isError " + result.isError());
|
||||
System.out.println("first " + result.getFirstMessage());
|
||||
if (result.isError() && result.getFirstMessage().contains("This file already exists")) {
|
||||
System.out.println(UploadQueue.class.getSimpleName() + " No need to re-try this one");
|
||||
file.postUpload();
|
||||
// do not retry this error
|
||||
continue;
|
||||
}
|
||||
if (result.isError()) {
|
||||
System.out.println(UploadQueue.class.getSimpleName() + " Re-queueing " + file.getFullName());
|
||||
queue.put(file);
|
||||
continue;
|
||||
}
|
||||
file.postUpload();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("ResultOfMethodCallIgnored")
|
||||
private static void delete(String fileName) {
|
||||
System.out.println(UploadQueue.class.getSimpleName() + " Deleting " + fileName);
|
||||
new File(fileName).delete();
|
||||
}
|
||||
|
||||
@SuppressWarnings("ResultOfMethodCallIgnored")
|
||||
public static void enqueue(ControllerAccess controllerAccess, String configurationName) {
|
||||
start();
|
||||
if (queue.size() > 100) {
|
||||
// too much pending drama
|
||||
return;
|
||||
}
|
||||
Msq msq = TuneUploder.grabTune(controllerAccess, configurationName);
|
||||
if (msq == null) {
|
||||
log.error("Error saving tune");
|
||||
return;
|
||||
}
|
||||
msq.bibliography.setTuneComment("Auto-saved");
|
||||
try {
|
||||
new File(OUTBOX_FOLDER).mkdirs();
|
||||
String fileName = System.currentTimeMillis() + ".msq";
|
||||
String fullFileName = OUTBOX_FOLDER + File.separator + fileName;
|
||||
msq.writeXmlFile(fullFileName);
|
||||
queue.put(new FileAndFolder(OUTBOX_FOLDER, fileName));
|
||||
} catch (InterruptedException | JAXBException | IOException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
static class FileAndFolder {
|
||||
private static final boolean DEBUG_SAVE_UPLOADED = false;
|
||||
private final String folder;
|
||||
private final String file;
|
||||
|
||||
public FileAndFolder(String folder, String file) {
|
||||
this.folder = folder;
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
public String getFullName() {
|
||||
return folder + File.separator + file;
|
||||
}
|
||||
|
||||
@SuppressWarnings("ResultOfMethodCallIgnored")
|
||||
public void postUpload() {
|
||||
if (DEBUG_SAVE_UPLOADED) {
|
||||
log.info("Renaming file " + file);
|
||||
String uploadedDir = folder + File.separator + "uploaded";
|
||||
new File(uploadedDir).mkdirs();
|
||||
new File(getFullName()).renameTo(new File(uploadedDir + File.separator + file));
|
||||
} else {
|
||||
delete(getFullName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
package com.rusefi.ts_plugin;
|
||||
|
||||
import com.rusefi.tools.online.UploadResult;
|
||||
import com.rusefi.core.preferences.storage.PersistentConfiguration;
|
||||
import org.putgemin.VerticalFlowLayout;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
|
||||
public class UploadView {
|
||||
private final JComponent content = new JPanel(new VerticalFlowLayout());
|
||||
|
||||
private static final String AUTO_UPLOAD = "AUTO_UPLOAD";
|
||||
|
||||
public final JLabel uploadState = new JLabel();
|
||||
private final JLabel projectWarning = new JLabel(UploaderStatus.NO_PROJECT);
|
||||
private final JLabel tuneInfo = new JLabel();
|
||||
private final JCheckBox autoUpload = new JCheckBox("Continuous auto-upload");
|
||||
|
||||
public UploadView() {
|
||||
content.add(projectWarning);
|
||||
content.add(tuneInfo);
|
||||
content.add(uploadState);
|
||||
content.add(autoUpload);
|
||||
|
||||
autoUpload.setSelected(isAutoUpload());
|
||||
autoUpload.addActionListener(new AbstractAction() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
PersistentConfiguration.getConfig().getRoot().setProperty(AUTO_UPLOAD, autoUpload.isSelected());
|
||||
PersistentConfiguration.getConfig().save();
|
||||
}
|
||||
});
|
||||
uploadState.setVisible(false);
|
||||
}
|
||||
|
||||
public static boolean isAutoUpload() {
|
||||
return PersistentConfiguration.getConfig().getRoot().getBoolProperty(AUTO_UPLOAD, false);
|
||||
}
|
||||
|
||||
public static void setResult(UploadResult result, JLabel uploadState) {
|
||||
uploadState.setText(result.getFirstMessage());
|
||||
uploadState.setVisible(true);
|
||||
}
|
||||
|
||||
public JComponent getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void update(UploaderStatus uploaderStatus) {
|
||||
if (uploaderStatus.isTuneOk()) {
|
||||
tuneInfo.setText(uploaderStatus.tuneInfo);
|
||||
tuneInfo.setForeground(Color.black);
|
||||
} else {
|
||||
tuneInfo.setText(uploaderStatus.tuneWarning);
|
||||
tuneInfo.setForeground(Color.red);
|
||||
}
|
||||
|
||||
if (uploaderStatus.isProjectIsOk()) {
|
||||
projectWarning.setVisible(false);
|
||||
} else {
|
||||
projectWarning.setVisible(true);
|
||||
projectWarning.setText(uploaderStatus.projectWarning);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
package com.rusefi.ts_plugin;
|
||||
|
||||
import com.rusefi.TsTuneReader;
|
||||
import com.rusefi.tune.xml.Constant;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Map;
|
||||
|
||||
public class UploaderStatus {
|
||||
static final String NO_PROJECT = "Please open project";
|
||||
public String projectWarning;
|
||||
public String tuneInfo;
|
||||
public String tuneWarning;
|
||||
|
||||
void updateProjectStatus(String configurationName, boolean isProjectActive) {
|
||||
if (!isProjectActive) {
|
||||
this.projectWarning = NO_PROJECT;
|
||||
} else if (!new File(TsTuneReader.getTsTuneFileName(configurationName)).exists()) {
|
||||
this.projectWarning = "Tune not found " + configurationName;
|
||||
} else {
|
||||
this.projectWarning = null;
|
||||
}
|
||||
}
|
||||
|
||||
void readTuneState(String configurationName) {
|
||||
Map<String, Constant> fileSystemValues = TuneUploder.getFileSystemValues(configurationName);
|
||||
Constant engineMake = fileSystemValues.get("enginemake");
|
||||
Constant engineCode = fileSystemValues.get("enginecode");
|
||||
Constant vehicleName = fileSystemValues.get("VEHICLENAME");
|
||||
String warning = "";
|
||||
if (PluginEntry.isEmpty(engineMake)) {
|
||||
warning += " engine make";
|
||||
}
|
||||
if (PluginEntry.isEmpty(engineCode)) {
|
||||
warning += " engine code";
|
||||
}
|
||||
if (PluginEntry.isEmpty(vehicleName)) {
|
||||
warning += " vehicle name";
|
||||
}
|
||||
if (warning.isEmpty()) {
|
||||
tuneInfo = engineMake.getValue() + " " + engineCode.getValue() + " " + vehicleName.getValue();
|
||||
tuneWarning = null;
|
||||
} else {
|
||||
tuneInfo = null;
|
||||
tuneWarning = "<html>Please set " + warning + " on Base Settings tab<br>and reopen Project";
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isProjectIsOk() {
|
||||
return projectWarning == null;
|
||||
}
|
||||
|
||||
public boolean isTuneOk() {
|
||||
return tuneWarning == null;
|
||||
}
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
package com.rusefi.ts_plugin.auth;
|
||||
|
||||
import com.devexperts.logging.Logging;
|
||||
import com.rusefi.server.JsonUserDetailsResolver;
|
||||
import com.rusefi.server.UserDetails;
|
||||
import com.rusefi.ui.AuthTokenPanel;
|
||||
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
public class InstanceAuthContext {
|
||||
private final static Logging log = Logging.getLogging(InstanceAuthContext.class);
|
||||
|
||||
private static boolean isInitialized;
|
||||
|
||||
public static CopyOnWriteArrayList<Listener> listeners = new CopyOnWriteArrayList<>();
|
||||
|
||||
public static UserDetails self;
|
||||
|
||||
public synchronized static void startup() {
|
||||
if (isInitialized)
|
||||
return;
|
||||
if (!AuthTokenPanel.hasToken()) {
|
||||
// exiting without marking initialized - UI has a chance to re-start the process once token is provided
|
||||
return;
|
||||
}
|
||||
log.info("Startup");
|
||||
isInitialized = true;
|
||||
new Thread(() -> {
|
||||
self = new JsonUserDetailsResolver().apply(AuthTokenPanel.getAuthToken());
|
||||
for (Listener listener : listeners)
|
||||
listener.onUserDetails(self);
|
||||
}).start();
|
||||
}
|
||||
|
||||
public static boolean isOurController(int userId) {
|
||||
if (self == null)
|
||||
return false;
|
||||
return self.getUserId() == userId;
|
||||
}
|
||||
|
||||
public interface Listener {
|
||||
void onUserDetails(UserDetails userDetails);
|
||||
}
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
package com.rusefi.ts_plugin.auth;
|
||||
|
||||
import com.rusefi.server.UserDetails;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
|
||||
public class SelfInfo {
|
||||
private final JLabel jLabel = new JLabel();
|
||||
|
||||
public SelfInfo() {
|
||||
InstanceAuthContext.listeners.add(new InstanceAuthContext.Listener() {
|
||||
@Override
|
||||
public void onUserDetails(UserDetails userDetails) {
|
||||
setInfo();
|
||||
}
|
||||
});
|
||||
setInfo();
|
||||
}
|
||||
|
||||
private void setInfo() {
|
||||
UserDetails userDetails = InstanceAuthContext.self;
|
||||
if (userDetails == null) {
|
||||
jLabel.setText("Authorizing...");
|
||||
} else {
|
||||
jLabel.setText("Logged in as " + userDetails.getUserName());
|
||||
}
|
||||
}
|
||||
|
||||
public Component getContent() {
|
||||
return jLabel;
|
||||
}
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
package com.rusefi.ts_plugin.util;
|
||||
|
||||
import com.rusefi.ts_plugin.PluginEntry;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.jar.Attributes;
|
||||
import java.util.jar.Manifest;
|
||||
|
||||
public class ManifestHelper {
|
||||
private static final String BUILT_DATE = "Built-Date";
|
||||
private static final String BUILT_TIMESTAMP = "Built-Timestamp";
|
||||
|
||||
@NotNull
|
||||
public static String getVersion() {
|
||||
return getAttribute(BUILT_DATE);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static String getBuildTimestamp() {
|
||||
return getAttribute(BUILT_TIMESTAMP);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String getAttribute(String attributeName) {
|
||||
// all this magic below to make sure we are reading manifest of the *our* jar file not TS main jar file
|
||||
Class clazz = PluginEntry.class;
|
||||
String className = clazz.getSimpleName() + ".class";
|
||||
String classPath = clazz.getResource(className).toString();
|
||||
if (!classPath.startsWith("jar")) {
|
||||
// Class not from JAR
|
||||
return "Local Run";
|
||||
}
|
||||
String manifestPath = classPath.substring(0, classPath.lastIndexOf("!") + 1) +
|
||||
"/META-INF/MANIFEST.MF";
|
||||
try {
|
||||
Manifest manifest = new Manifest(new URL(manifestPath).openStream());
|
||||
Attributes attributes = manifest.getMainAttributes();
|
||||
|
||||
String result = attributes.getValue(attributeName);
|
||||
System.out.println(BUILT_DATE + " " + result);
|
||||
return result == null ? "Unknown version" : result;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return "Unknown version";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
package com.rusefi;
|
||||
|
||||
import com.rusefi.core.SignatureHelper;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class SignatureHelperTest {
|
||||
@Test
|
||||
public void test() {
|
||||
String url = SignatureHelper.getUrl("rusEFI master.2020.07.06.frankenso_na6.2468827536").first;
|
||||
assertEquals("https://rusefi.com/online/ini/rusefi/master/2020/07/06/frankenso_na6/2468827536.ini", url);
|
||||
}
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
package com.rusefi.ts_plugin;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class ConnectPanelTest {
|
||||
@Test
|
||||
public void testFileName() {
|
||||
assertEquals("1234", ConnectPanel.getLastFour("aaa1234.a"));
|
||||
assertEquals("1234", ConnectPanel.getLastFour("a1234.a"));
|
||||
assertEquals("123", ConnectPanel.getLastFour("123.a"));
|
||||
assertEquals("1", ConnectPanel.getLastFour("1.a"));
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
package com.rusefi.ts_plugin;
|
||||
|
||||
import com.efiAnalytics.plugin.ecu.ControllerAccess;
|
||||
import com.rusefi.MockitoTestHelper;
|
||||
import com.rusefi.core.ui.FrameHelper;
|
||||
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
public class LogUploadSelectorSandbox {
|
||||
public static void main(String[] args) {
|
||||
String projectName = "mre_f4";
|
||||
ControllerAccess controllerAccess = mock(ControllerAccess.class, MockitoTestHelper.NEGATIVE_ANSWER);
|
||||
doReturn(new String[]{projectName}).when(controllerAccess).getEcuConfigurationNames();
|
||||
|
||||
new FrameHelper().showFrame(new LogUploadSelector(() -> controllerAccess).getContent());
|
||||
}
|
||||
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
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;
|
||||
import com.rusefi.core.ui.FrameHelper;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Objects;
|
||||
|
||||
import static com.rusefi.MockitoTestHelper.NEGATIVE_ANSWER;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* Sandbox for plugin body
|
||||
*
|
||||
* @see PluginLauncherSandbox for plugin auto-update launcher
|
||||
*/
|
||||
public class PluginBodySandbox {
|
||||
|
||||
private static final String PROJECT_NAME = "dev";
|
||||
|
||||
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<String> 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());
|
||||
|
||||
ControllerAccess controllerAccess = mock(ControllerAccess.class, NEGATIVE_ANSWER);
|
||||
doReturn(new String[]{PROJECT_NAME}).when(controllerAccess).getEcuConfigurationNames();
|
||||
doReturn(controllerParameterServer).when(controllerAccess).getControllerParameterServer();
|
||||
|
||||
FrameHelper frameHelper = new FrameHelper();
|
||||
frameHelper.getFrame().setDefaultCloseOperation(JDialog.EXIT_ON_CLOSE);
|
||||
frameHelper.showFrame(new PluginEntry(() -> controllerAccess).getContent());
|
||||
}
|
||||
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
package com.rusefi.ts_plugin;
|
||||
|
||||
import com.rusefi.core.ui.FrameHelper;
|
||||
|
||||
/**
|
||||
* @see PluginBodySandbox
|
||||
*/
|
||||
public class RemoteTabSandbox {
|
||||
public static void main(String[] args) {
|
||||
new FrameHelper().showFrame(new RemoteTab().getContent());
|
||||
}
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="module" module-name="inifile" />
|
||||
<orderEntry type="module" module-name="shared_ui" />
|
||||
<orderEntry type="library" name="TunerStudioPluginAPI" level="project" />
|
||||
<orderEntry type="module" module-name="ts_plugin_launcher" />
|
||||
<orderEntry type="library" name="org.mockito:mockito-all:1.10.19" level="project" />
|
||||
<orderEntry type="library" name="json-simple" level="project" />
|
||||
<orderEntry type="library" name="httpclient" level="project" />
|
||||
<orderEntry type="module" module-name="models" />
|
||||
<orderEntry type="module" module-name="io" />
|
||||
</component>
|
||||
</module>
|
Loading…
Reference in New Issue