rusefi/java_tools/ts_plugin/src/main/java/com/rusefi/ts_plugin/RemoteTab.java

359 lines
15 KiB
Java
Raw Normal View History

2020-07-19 08:52:25 -07:00
package com.rusefi.ts_plugin;
import com.rusefi.NamedThreadFactory;
import com.rusefi.core.SignatureHelper;
2020-07-30 18:07:50 -07:00
import com.rusefi.Timeouts;
import com.rusefi.core.ui.AutoupdateUtil;
2020-08-29 21:21:34 -07:00
import com.rusefi.core.Pair;
2020-07-30 18:07:50 -07:00
import com.rusefi.io.serial.StreamStatistics;
2020-07-25 19:22:40 -07:00
import com.rusefi.io.tcp.ServerSocketReference;
2020-07-23 22:15:54 -07:00
import com.rusefi.io.tcp.TcpIoStream;
2020-08-15 21:24:36 -07:00
import com.rusefi.proxy.NetworkConnector;
2020-07-25 18:53:09 -07:00
import com.rusefi.proxy.client.LocalApplicationProxy;
2020-07-25 19:22:40 -07:00
import com.rusefi.proxy.client.LocalApplicationProxyContextImpl;
2020-08-29 22:15:18 -07:00
import com.rusefi.proxy.client.UpdateType;
import com.rusefi.core.rusEFIVersion;
import com.rusefi.server.ApplicationRequest;
2020-07-21 20:24:16 -07:00
import com.rusefi.server.ControllerInfo;
import com.rusefi.server.SessionDetails;
2020-07-25 16:17:28 -07:00
import com.rusefi.server.UserDetails;
2020-07-19 08:52:25 -07:00
import com.rusefi.tools.online.HttpUtil;
import com.rusefi.tools.online.ProxyClient;
2020-07-21 20:24:16 -07:00
import com.rusefi.tools.online.PublicSession;
2020-08-29 21:21:34 -07:00
import com.rusefi.ts_plugin.auth.InstanceAuthContext;
import com.rusefi.ts_plugin.auth.SelfInfo;
import com.rusefi.ui.AuthTokenPanel;
2020-07-21 20:24:16 -07:00
import com.rusefi.ui.util.URLLabel;
2020-08-14 20:46:31 -07:00
import org.jetbrains.annotations.NotNull;
2020-07-21 20:24:16 -07:00
import org.putgemin.VerticalFlowLayout;
2020-07-19 08:52:25 -07:00
import javax.swing.*;
import java.awt.*;
2020-07-30 18:07:50 -07:00
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
2020-07-19 08:52:25 -07:00
import java.io.IOException;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
2020-07-23 22:15:54 -07:00
import java.util.concurrent.atomic.AtomicReference;
2020-07-19 08:52:25 -07:00
import static com.rusefi.core.preferences.storage.PersistentConfiguration.getConfig;
2020-07-21 20:24:16 -07:00
/**
2020-07-26 21:48:04 -07:00
* remote ECU access & control
*
2020-07-26 21:16:38 -07:00
* @see RemoteTabSandbox
2020-07-26 21:48:04 -07:00
* @see PluginEntry
2020-07-21 20:24:16 -07:00
*/
2020-07-19 08:52:25 -07:00
public class RemoteTab {
2020-07-21 20:24:16 -07:00
private static final String APPLICATION_PORT = "application_port";
2020-07-26 21:48:04 -07:00
public static final String HOWTO_REMOTE_TUNING = "https://github.com/rusefi/rusefi/wiki/HOWTO-Remote-Tuning";
2020-07-19 08:52:25 -07:00
private final JComponent content = new JPanel(new BorderLayout());
2020-08-29 21:21:34 -07:00
private final JScrollPane scroll = new JScrollPane(content, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
2020-07-21 20:24:16 -07:00
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);
}
};
2020-07-25 16:17:28 -07:00
2020-07-30 18:07:50 -07:00
private StreamStatusControl streamStatusControl = null;
2020-07-25 16:17:28 -07:00
private final JButton disconnect = new JButton("Disconnect");
2020-08-29 21:21:34 -07:00
private final Executor listDownloadExecutor = Executors.newSingleThreadExecutor(new NamedThreadFactory("online list downloader", true));
2020-07-19 08:52:25 -07:00
public RemoteTab() {
2020-08-29 21:21:34 -07:00
JButton refresh = new JButton("Refresh Remote Controllers List");
refresh.addActionListener(e -> requestControllersList());
2020-07-19 08:52:25 -07:00
disconnect.addActionListener(e -> {
LocalApplicationProxy localApplicationProxy = RemoteTabController.INSTANCE.getLocalApplicationProxy();
if (localApplicationProxy != null)
localApplicationProxy.close();
RemoteTabController.INSTANCE.setState(RemoteTabController.State.NOT_CONNECTED);
2020-08-29 21:21:34 -07:00
requestControllersList();
2020-07-25 16:17:28 -07:00
});
2020-07-30 18:07:50 -07:00
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);
}
};
2020-07-25 00:33:50 -07:00
IntegerDocumentFilter.install(applicationPort);
IntegerDocumentFilter.install(oneTimePasswordControl);
String portProperty = getLocalPort();
2020-07-21 20:24:16 -07:00
applicationPort.setText(portProperty);
2020-07-19 08:52:25 -07:00
2020-07-25 00:33:50 -07:00
JPanel topLines = new JPanel(new VerticalFlowLayout());
2020-07-26 21:48:04 -07:00
topLines.add(new URLLabel(HOWTO_REMOTE_TUNING));
2020-08-29 21:21:34 -07:00
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);
2020-07-25 00:33:50 -07:00
content.add(topLines, BorderLayout.NORTH);
2020-07-21 20:24:16 -07:00
content.add(list, BorderLayout.CENTER);
list.add(new JLabel("Requesting list of ECUs"));
2020-07-25 16:17:28 -07:00
2020-08-29 21:21:34 -07:00
InstanceAuthContext.listeners.add(userDetails -> requestControllersList());
2020-07-25 16:17:28 -07:00
LocalApplicationProxy currentState = RemoteTabController.INSTANCE.getLocalApplicationProxy();
if (currentState == null) {
2020-08-29 21:21:34 -07:00
requestControllersList();
2020-07-25 16:17:28 -07:00
} else {
2020-08-15 12:34:50 -07:00
setConnectedStatus(currentState.getApplicationRequest().getVehicleOwner(), null,
currentState.getApplicationRequest().getSessionDetails().getControllerInfo());
2020-07-25 16:17:28 -07:00
}
2020-07-19 08:52:25 -07:00
}
private String getLocalPort() {
2020-07-23 22:15:54 -07:00
return getConfig().getRoot().getProperty(APPLICATION_PORT, "29001");
}
2020-07-19 08:52:25 -07:00
2020-08-29 21:21:34 -07:00
private void requestControllersList() {
listDownloadExecutor.execute(() -> {
try {
2020-08-29 21:21:34 -07:00
List<PublicSession> userDetails = ProxyClient.getOnlineApplications(HttpUtil.PROXY_JSON_API_HTTP_PORT);
SwingUtilities.invokeLater(() -> showList(userDetails));
} catch (IOException e) {
e.printStackTrace();
2020-07-19 08:52:25 -07:00
}
});
}
2020-07-21 20:24:16 -07:00
private void showList(List<PublicSession> userDetails) {
2020-07-25 16:17:28 -07:00
if (RemoteTabController.INSTANCE.getState() != RemoteTabController.State.NOT_CONNECTED)
return;
2020-07-21 20:24:16 -07:00
list.removeAll();
if (userDetails.isEmpty()) {
list.add(new JLabel("No ECUs are broadcasting at the moment :("));
} else {
2020-07-25 16:17:28 -07:00
JPanel verticalPanel = new JPanel(new VerticalFlowLayout());
2020-08-29 21:21:34 -07:00
list.add(verticalPanel);
2020-07-25 16:17:28 -07:00
for (PublicSession user : userDetails) {
2020-08-29 21:21:34 -07:00
verticalPanel.add(createControllerRow(user));
}
2020-07-21 20:24:16 -07:00
}
AutoupdateUtil.trueLayout(list);
}
2020-08-29 21:21:34 -07:00
private JComponent createControllerRow(PublicSession publicSession) {
ControllerInfo controllerInfo = publicSession.getControllerInfo();
2020-07-25 16:17:28 -07:00
JComponent topLine = new JPanel(new FlowLayout());
2020-08-14 20:34:53 -07:00
topLine.add(new JLabel(publicSession.getVehicleOwner().getUserName()));
2020-07-25 16:17:28 -07:00
topLine.add(new JLabel(controllerInfo.getVehicleName() + " " + controllerInfo.getEngineMake() + " " + controllerInfo.getEngineCode()));
2020-07-21 20:24:16 -07:00
2020-08-29 22:15:18 -07:00
JPanel bottomPanel = new JPanel(new VerticalFlowLayout());
2020-07-21 20:24:16 -07:00
2020-07-24 14:20:13 -07:00
if (publicSession.isUsed()) {
2020-08-14 20:34:53 -07:00
bottomPanel.add(new JLabel(" Used by " + publicSession.getTunerName()));
2020-07-24 14:20:13 -07:00
} else {
2020-08-29 21:39:08 -07:00
JButton connect = new JButton("Connect to " + publicSession.getVehicleOwner().getUserName() + " ECU");
2020-08-14 20:34:53 -07:00
connect.addActionListener(event -> connectToProxy(publicSession));
2020-07-25 16:17:28 -07:00
bottomPanel.add(connect);
2020-08-14 20:46:31 -07:00
2020-08-29 21:21:34 -07:00
if (InstanceAuthContext.isOurController(publicSession.getVehicleOwner().getUserId())) {
2020-08-29 21:39:08 -07:00
if (publicSession.getImplementation().equals(NetworkConnector.Implementation.SBC.name())) {
2020-10-07 16:44:06 -07:00
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() {
2020-08-29 21:39:08 -07:00
@Override
public void actionPerformed(ActionEvent e) {
2020-10-07 16:44:06 -07:00
requestUpdate(publicSession, updateSoftwareRelease, UpdateType.CONTROLLER_RELEASE);
2020-08-29 21:21:34 -07:00
}
2020-08-29 21:39:08 -07:00
});
2020-10-07 16:44:06 -07:00
updateSoftwarePanel.add(updateSoftwareLatest);
updateSoftwarePanel.add(updateSoftwareRelease);
bottomPanel.add(updateSoftwarePanel);
2020-08-29 21:39:08 -07:00
}
2020-08-29 22:15:18 -07:00
2020-10-07 16:44:06 -07:00
JPanel updateFirmwarePanel = createUpdateFirmwarePanel(publicSession);
bottomPanel.add(updateFirmwarePanel);
2020-08-29 21:21:34 -07:00
}
2020-07-25 16:17:28 -07:00
}
2020-07-21 20:24:16 -07:00
2020-07-25 16:17:28 -07:00
JPanel userPanel = new JPanel(new BorderLayout());
2020-08-29 21:21:34 -07:00
JPanel infoLine = new JPanel(new FlowLayout());
infoLine.add(new JLabel("Age " + publicSession.getAge()));
infoLine.add(getSignatureDownload(controllerInfo));
2020-07-25 16:17:28 -07:00
userPanel.add(topLine, BorderLayout.NORTH);
2020-08-29 21:21:34 -07:00
userPanel.add(infoLine, BorderLayout.CENTER);
2020-07-25 16:17:28 -07:00
userPanel.add(bottomPanel, BorderLayout.SOUTH);
userPanel.setBorder(BorderFactory.createLineBorder(Color.BLACK));
2020-07-21 20:24:16 -07:00
return userPanel;
}
2020-10-07 16:44:06 -07:00
@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;
}
2020-08-29 22:15:18 -07:00
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();
}
}
2020-08-29 21:21:34 -07:00
@NotNull
private URLLabel getSignatureDownload(ControllerInfo controllerInfo) {
Pair<String, String> url = SignatureHelper.getUrl(controllerInfo.getSignature());
return new URLLabel(url.second, url.first);
}
2020-08-14 20:34:53 -07:00
private void connectToProxy(PublicSession publicSession) {
2020-07-25 16:17:28 -07:00
RemoteTabController.INSTANCE.setState(RemoteTabController.State.CONNECTING);
2020-08-14 20:34:53 -07:00
setStatus("Connecting to " + publicSession.getVehicleOwner().getUserName());
2020-07-25 16:17:28 -07:00
2020-07-30 18:07:50 -07:00
LocalApplicationProxy.ConnectionListener connectionListener = (localApplicationProxy, authenticatorToProxyStream) -> {
RemoteTabController.INSTANCE.setConnected(localApplicationProxy);
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
2020-08-15 12:34:50 -07:00
setConnectedStatus(publicSession.getVehicleOwner(), authenticatorToProxyStream, publicSession.getControllerInfo());
}
});
2020-07-25 16:17:28 -07:00
};
new Thread(() -> {
2020-08-14 20:34:53 -07:00
runAuthenticator(publicSession, connectionListener);
2020-07-25 16:17:28 -07:00
}, "Authenticator").start();
}
2020-08-15 12:34:50 -07:00
private void setConnectedStatus(UserDetails userDetails, StreamStatistics authenticatorToProxyStream, ControllerInfo controllerInfo) {
2020-07-30 18:07:50 -07:00
if (authenticatorToProxyStream != null) {
streamStatusControl = new StreamStatusControl(authenticatorToProxyStream);
}
2020-07-25 16:17:28 -07:00
setStatus("Connected to " + userDetails.getUserName(),
new JLabel("You can now connect your TunerStudio to IP address localhost and port " + getLocalPort()),
2020-08-29 21:21:34 -07:00
new URLLabel(SignatureHelper.getUrl(controllerInfo.getSignature()).first),
2020-07-30 18:07:50 -07:00
disconnect, streamStatusControl == null ? null : streamStatusControl.getContent());
2020-07-25 16:17:28 -07:00
}
private void setStatus(String text, JComponent... extra) {
2020-07-23 21:59:32 -07:00
list.removeAll();
list.add(new JLabel(text));
2020-07-30 18:07:50 -07:00
for (JComponent component : extra) {
if (component != null) {
list.add(component);
}
}
2020-07-23 21:59:32 -07:00
AutoupdateUtil.trueLayout(list);
}
2020-08-14 20:34:53 -07:00
private void runAuthenticator(PublicSession publicSession, LocalApplicationProxy.ConnectionListener connectionListener) {
2020-08-14 20:46:31 -07:00
ApplicationRequest applicationRequest = getApplicationRequest(publicSession);
try {
2020-07-25 19:22:40 -07:00
AtomicReference<ServerSocketReference> serverHolderAtomicReference = new AtomicReference<>();
2020-07-23 22:15:54 -07:00
TcpIoStream.DisconnectListener disconnectListener = message -> SwingUtilities.invokeLater(() -> {
2020-07-29 16:25:25 -07:00
System.out.println("Disconnected " + message);
setStatus("Disconnected: " + message);
2020-07-25 16:17:28 -07:00
RemoteTabController.INSTANCE.setState(RemoteTabController.State.NOT_CONNECTED);
2020-07-25 19:22:40 -07:00
ServerSocketReference serverHolder = serverHolderAtomicReference.get();
2020-07-23 22:15:54 -07:00
if (serverHolder != null)
serverHolder.close();
});
2020-07-25 19:35:10 -07:00
LocalApplicationProxyContextImpl context = new LocalApplicationProxyContextImpl() {
@Override
public int authenticatorPort() {
return Integer.parseInt(getLocalPort());
}
};
2020-07-25 19:22:40 -07:00
ServerSocketReference serverHolder = LocalApplicationProxy.startAndRun(
2020-07-25 19:35:10 -07:00
context,
applicationRequest,
2020-07-25 16:17:28 -07:00
HttpUtil.PROXY_JSON_API_HTTP_PORT, disconnectListener, connectionListener);
2020-07-23 22:15:54 -07:00
serverHolderAtomicReference.set(serverHolder);
} catch (IOException e) {
2020-07-23 21:59:32 -07:00
setStatus("IO error: " + e);
}
}
2020-08-14 20:46:31 -07:00
@NotNull
private ApplicationRequest getApplicationRequest(PublicSession publicSession) {
2020-08-15 21:24:36 -07:00
SessionDetails sessionDetails = new SessionDetails(NetworkConnector.Implementation.Plugin,
publicSession.getControllerInfo(), AuthTokenPanel.getAuthToken(),
2020-08-14 20:46:31 -07:00
Integer.parseInt(oneTimePasswordControl.getText()), rusEFIVersion.CONSOLE_VERSION);
2020-08-29 21:21:34 -07:00
return new ApplicationRequest(sessionDetails, publicSession.getVehicleOwner());
2020-08-14 20:46:31 -07:00
}
2020-07-19 08:52:25 -07:00
public JComponent getContent() {
2020-08-29 21:21:34 -07:00
return scroll;
2020-07-19 08:52:25 -07:00
}
2020-09-30 20:40:22 -07:00
interface Listener {
void onConnected();
}
2020-07-19 08:52:25 -07:00
}