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