From 98404eb69be2a820d9f2db4246329e1f64274094 Mon Sep 17 00:00:00 2001 From: rusefi Date: Sat, 25 Jul 2020 19:17:28 -0400 Subject: [PATCH] proxy progress - backend and frontend --- .../com/rusefi/LocalApplicationProxy.java | 45 ++++++-- .../com/rusefi/server/ApplicationRequest.java | 23 ++-- .../java/com/rusefi/server/UserDetails.java | 5 + .../test/java/com/rusefi/FullServerTest.java | 6 +- .../src/test/java/com/rusefi/ServerTest.java | 5 +- .../main/java/com/rusefi/server/Backend.java | 29 +++-- .../server/ControllerConnectionState.java | 9 ++ .../com/rusefi/server/TwoKindSemaphore.java | 8 ++ .../com/rusefi/server/SessionDetailsTest.java | 2 +- .../com/rusefi/ts_plugin/PluginEntry.java | 5 +- .../java/com/rusefi/ts_plugin/RemoteTab.java | 105 ++++++++++++++---- .../rusefi/ts_plugin/RemoteTabController.java | 37 ++++++ .../rusefi/ts_plugin/TsPluginLauncher.java | 8 +- 13 files changed, 223 insertions(+), 64 deletions(-) create mode 100644 java_tools/ts_plugin/src/main/java/com/rusefi/ts_plugin/RemoteTabController.java diff --git a/java_console/io/src/main/java/com/rusefi/LocalApplicationProxy.java b/java_console/io/src/main/java/com/rusefi/LocalApplicationProxy.java index aebc254f2c..d282fb1daa 100644 --- a/java_console/io/src/main/java/com/rusefi/LocalApplicationProxy.java +++ b/java_console/io/src/main/java/com/rusefi/LocalApplicationProxy.java @@ -1,8 +1,6 @@ package com.rusefi; import com.devexperts.logging.Logging; -import com.opensr5.Logger; -import com.rusefi.core.MessagesCentral; import com.rusefi.io.IoStream; import com.rusefi.io.commands.HelloCommand; import com.rusefi.io.tcp.BinaryProtocolProxy; @@ -13,45 +11,70 @@ import com.rusefi.server.rusEFISSLContext; import com.rusefi.tools.online.HttpUtil; import com.rusefi.tools.online.ProxyClient; +import java.io.Closeable; import java.io.IOException; import static com.devexperts.logging.Logging.getLogging; -public class LocalApplicationProxy { +public class LocalApplicationProxy implements Closeable { private static final Logging log = getLogging(LocalApplicationProxy.class); public static final int SERVER_PORT_FOR_APPLICATIONS = HttpUtil.getIntProperty("applications.port", 8002); private final ApplicationRequest applicationRequest; + private final ServerHolder serverHolder; + private final IoStream authenticatorToProxyStream; - public LocalApplicationProxy(ApplicationRequest applicationRequest) { + public LocalApplicationProxy(ApplicationRequest applicationRequest, ServerHolder serverHolder, IoStream authenticatorToProxyStream) { this.applicationRequest = applicationRequest; + this.serverHolder = serverHolder; + this.authenticatorToProxyStream = authenticatorToProxyStream; + } + + public ApplicationRequest getApplicationRequest() { + return applicationRequest; } /** * @param serverPortForRemoteUsers port on which rusEFI proxy accepts authenticator connections * @param applicationRequest remote session we want to connect to - * @param localApplicationPort local port we would bind for TunerStudio to connect to + * @param localApplicationPort local port we would bind for TunerStudio to connect to * @param jsonHttpPort * @param disconnectListener + * @param connectionListener */ - public static ServerHolder startAndRun(int serverPortForRemoteUsers, ApplicationRequest applicationRequest, int localApplicationPort, int jsonHttpPort, TcpIoStream.DisconnectListener disconnectListener) throws IOException { + public static ServerHolder startAndRun(int serverPortForRemoteUsers, ApplicationRequest applicationRequest, int localApplicationPort, int jsonHttpPort, TcpIoStream.DisconnectListener disconnectListener, ConnectionListener connectionListener) throws IOException { String version = HttpUtil.executeGet(ProxyClient.getHttpAddress(jsonHttpPort) + ProxyClient.VERSION_PATH); log.info("Server says version=" + version); if (!version.contains(ProxyClient.BACKEND_VERSION)) throw new IOException("Unexpected backend version " + version + " while we want " + ProxyClient.BACKEND_VERSION); IoStream authenticatorToProxyStream = new TcpIoStream("authenticatorToProxyStream ", rusEFISSLContext.getSSLSocket(HttpUtil.RUSEFI_PROXY_HOSTNAME, serverPortForRemoteUsers), disconnectListener); - LocalApplicationProxy localApplicationProxy = new LocalApplicationProxy(applicationRequest); - log.info("Pushing " + applicationRequest); - localApplicationProxy.run(authenticatorToProxyStream); + LocalApplicationProxy.sendHello(authenticatorToProxyStream, applicationRequest); - return BinaryProtocolProxy.createProxy(authenticatorToProxyStream, localApplicationPort); + ServerHolder serverHolder = BinaryProtocolProxy.createProxy(authenticatorToProxyStream, localApplicationPort); + LocalApplicationProxy localApplicationProxy = new LocalApplicationProxy(applicationRequest, serverHolder, authenticatorToProxyStream); + connectionListener.onConnected(localApplicationProxy); + return serverHolder; } - public void run(IoStream authenticatorToProxyStream) throws IOException { + public static void sendHello(IoStream authenticatorToProxyStream, ApplicationRequest applicationRequest) throws IOException { + log.info("Pushing " + applicationRequest); // right from connection push session authentication data new HelloCommand(applicationRequest.toJson()).handle(authenticatorToProxyStream); } public static void start(String[] strings) { } + + @Override + public void close() { + serverHolder.close(); + authenticatorToProxyStream.close(); + } + + public interface ConnectionListener { + ConnectionListener VOID = localApplicationProxy -> { + }; + + void onConnected(LocalApplicationProxy localApplicationProxy); + } } diff --git a/java_console/io/src/main/java/com/rusefi/server/ApplicationRequest.java b/java_console/io/src/main/java/com/rusefi/server/ApplicationRequest.java index dc8e8a73a6..11a269d22d 100644 --- a/java_console/io/src/main/java/com/rusefi/server/ApplicationRequest.java +++ b/java_console/io/src/main/java/com/rusefi/server/ApplicationRequest.java @@ -7,38 +7,37 @@ import java.util.Objects; public class ApplicationRequest { private static final String SESSION = "session"; - private static final String USER_ID = "user_id"; private final SessionDetails sessionDetails; - private final int targetUserId; + private final UserDetails targetUser; - public ApplicationRequest(SessionDetails sessionDetails, int targetUserId) { + public ApplicationRequest(SessionDetails sessionDetails, UserDetails targetUser) { this.sessionDetails = sessionDetails; - this.targetUserId = targetUserId; + this.targetUser = targetUser; } public SessionDetails getSessionDetails() { return sessionDetails; } - public int getTargetUserId() { - return targetUserId; + public UserDetails getTargetUser() { + return targetUser; } public String toJson() { JSONObject jsonObject = new JSONObject(); jsonObject.put(SESSION, sessionDetails.toJson()); - jsonObject.put(USER_ID, targetUserId); + targetUser.put(jsonObject); return jsonObject.toJSONString(); } public static ApplicationRequest valueOf(String jsonString) { JSONObject jsonObject = HttpUtil.parse(jsonString); - long targetUserId = (Long) jsonObject.get(USER_ID); + UserDetails userDetails = UserDetails.valueOf(jsonObject); SessionDetails session = SessionDetails.valueOf((String) jsonObject.get(SESSION)); - return new ApplicationRequest(session, (int)targetUserId); + return new ApplicationRequest(session, userDetails); } @Override @@ -46,20 +45,20 @@ public class ApplicationRequest { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ApplicationRequest that = (ApplicationRequest) o; - return targetUserId == that.targetUserId && + return targetUser == that.targetUser && sessionDetails.equals(that.sessionDetails); } @Override public int hashCode() { - return Objects.hash(sessionDetails, targetUserId); + return Objects.hash(sessionDetails, targetUser); } @Override public String toString() { return "ApplicationRequest{" + "sessionDetails=" + sessionDetails + - ", targetUserId=" + targetUserId + + ", targetUserId=" + targetUser + '}'; } } diff --git a/java_console/io/src/main/java/com/rusefi/server/UserDetails.java b/java_console/io/src/main/java/com/rusefi/server/UserDetails.java index 5d6a778d4b..fe89fe6087 100644 --- a/java_console/io/src/main/java/com/rusefi/server/UserDetails.java +++ b/java_console/io/src/main/java/com/rusefi/server/UserDetails.java @@ -19,6 +19,11 @@ public class UserDetails { return new UserDetails(userName, (int) userId); } + void put(JSONObject jsonObject) { + jsonObject.put(USER_ID, getUserId()); + jsonObject.put(USERNAME, getUserName()); + } + public String getUserName() { return userName; } diff --git a/java_console/ui/src/test/java/com/rusefi/FullServerTest.java b/java_console/ui/src/test/java/com/rusefi/FullServerTest.java index 404262060d..644ecfd7f1 100644 --- a/java_console/ui/src/test/java/com/rusefi/FullServerTest.java +++ b/java_console/ui/src/test/java/com/rusefi/FullServerTest.java @@ -78,11 +78,13 @@ public class FullServerTest { assertTrue("controllerRegistered", controllerRegistered.await(READ_IMAGE_TIMEOUT, TimeUnit.MILLISECONDS)); SessionDetails authenticatorSessionDetails = new SessionDetails(controllerInfo, MockRusEfiDevice.TEST_TOKEN_3, networkConnectorResult.getOneTimeToken()); - ApplicationRequest applicationRequest = new ApplicationRequest(authenticatorSessionDetails, userId); + ApplicationRequest applicationRequest = new ApplicationRequest(authenticatorSessionDetails, userDetailsResolver.apply(MockRusEfiDevice.TEST_TOKEN_1)); // start authenticator int authenticatorPort = 7004; // local port on which authenticator accepts connections from Tuner Studio - LocalApplicationProxy.startAndRun(serverPortForRemoteUsers, applicationRequest, authenticatorPort, httpPort, TcpIoStream.DisconnectListener.VOID); + LocalApplicationProxy.startAndRun(serverPortForRemoteUsers, applicationRequest, authenticatorPort, httpPort, + TcpIoStream.DisconnectListener.VOID, + LocalApplicationProxy.ConnectionListener.VOID); CountDownLatch connectionEstablishedCountDownLatch = new CountDownLatch(1); diff --git a/java_console/ui/src/test/java/com/rusefi/ServerTest.java b/java_console/ui/src/test/java/com/rusefi/ServerTest.java index 89328925eb..8b9be40d63 100644 --- a/java_console/ui/src/test/java/com/rusefi/ServerTest.java +++ b/java_console/ui/src/test/java/com/rusefi/ServerTest.java @@ -185,12 +185,11 @@ covered by FullServerTest TestHelper.runApplicationConnectorBlocking(backend, serverPortForRemoteUsers); SessionDetails sessionDetails = MockRusEfiDevice.createTestSession(MockRusEfiDevice.TEST_TOKEN_1, Fields.TS_SIGNATURE); - ApplicationRequest applicationRequest = new ApplicationRequest(sessionDetails, 123); + ApplicationRequest applicationRequest = new ApplicationRequest(sessionDetails, createTestUserResolver().apply(MockRusEfiDevice.TEST_TOKEN_1)); // start authenticator IoStream authenticatorToProxyStream = TestHelper.secureConnectToLocalhost(serverPortForRemoteUsers, logger); - LocalApplicationProxy localApplicationProxy = new LocalApplicationProxy(applicationRequest); - localApplicationProxy.run(authenticatorToProxyStream); + LocalApplicationProxy.sendHello(authenticatorToProxyStream, applicationRequest); assertTrue(disconnectedCountDownLatch.await(30, TimeUnit.SECONDS)); } diff --git a/java_tools/proxy_server/src/main/java/com/rusefi/server/Backend.java b/java_tools/proxy_server/src/main/java/com/rusefi/server/Backend.java index 294ef21790..9a414e8ab7 100644 --- a/java_tools/proxy_server/src/main/java/com/rusefi/server/Backend.java +++ b/java_tools/proxy_server/src/main/java/com/rusefi/server/Backend.java @@ -184,7 +184,7 @@ public class Backend implements Closeable { return; } - ControllerKey controllerKey = new ControllerKey(applicationRequest.getTargetUserId(), applicationRequest.getSessionDetails().getControllerInfo()); + ControllerKey controllerKey = new ControllerKey(applicationRequest.getTargetUser().getUserId(), applicationRequest.getSessionDetails().getControllerInfo()); ControllerConnectionState state; synchronized (lock) { state = acquire(controllerKey, userDetails); @@ -318,19 +318,28 @@ public class Backend implements Closeable { * that's different from controllers since we periodically pull outputs from controllers which allows us to detect disconnects */ private void runApplicationConnectionsCleanup() { - List inactiveApplications = new ArrayList<>(); - + List applications; synchronized (lock) { - long now = System.currentTimeMillis(); - for (ApplicationConnectionState client : applications) { - if (now - client.getClientStream().getStreamStats().getPreviousPacketArrivalTime() > applicationTimeout) - inactiveApplications.add(client); + applications = new ArrayList<>(this.applications); + } + + long now = System.currentTimeMillis(); + for (ApplicationConnectionState client : applications) { + if (now - client.getClientStream().getStreamStats().getPreviousPacketArrivalTime() > applicationTimeout) { + log.error("Kicking out application " + client); + close(client); + } else { + log.info("Looks alive " + client); } } - for (ApplicationConnectionState inactiveClient : inactiveApplications) { - log.error("Kicking out application " + inactiveClient); - close(inactiveClient); + List controllers; + synchronized (lock) { + controllers = new ArrayList<>(this.controllers); + } + + for (ControllerConnectionState controllerConnectionState : controllers) { + log.info("State: " + controllerConnectionState); } } diff --git a/java_tools/proxy_server/src/main/java/com/rusefi/server/ControllerConnectionState.java b/java_tools/proxy_server/src/main/java/com/rusefi/server/ControllerConnectionState.java index 15f37c4fdb..9d9dfbfa79 100644 --- a/java_tools/proxy_server/src/main/java/com/rusefi/server/ControllerConnectionState.java +++ b/java_tools/proxy_server/src/main/java/com/rusefi/server/ControllerConnectionState.java @@ -75,6 +75,15 @@ public class ControllerConnectionState { FileUtil.close(clientSocket); } + @Override + public String toString() { + return "ControllerConnectionState{" + + "userDetails=" + userDetails + + ", isClosed=" + isClosed + + ", twoKindSemaphore=" + twoKindSemaphore + + '}'; + } + public void requestControllerInfo() throws IOException { HelloCommand.send(stream); String jsonString = HelloCommand.getHelloResponse(incomingData); diff --git a/java_tools/proxy_server/src/main/java/com/rusefi/server/TwoKindSemaphore.java b/java_tools/proxy_server/src/main/java/com/rusefi/server/TwoKindSemaphore.java index 27413cae75..5cc695786b 100644 --- a/java_tools/proxy_server/src/main/java/com/rusefi/server/TwoKindSemaphore.java +++ b/java_tools/proxy_server/src/main/java/com/rusefi/server/TwoKindSemaphore.java @@ -52,4 +52,12 @@ public class TwoKindSemaphore { public UserDetails getOwner() { return owner; } + + @Override + public String toString() { + return "TwoKindSemaphore{" + + "semaphore=" + semaphore + + ", owner=" + owner + + '}'; + } } diff --git a/java_tools/proxy_server/src/test/java/com/rusefi/server/SessionDetailsTest.java b/java_tools/proxy_server/src/test/java/com/rusefi/server/SessionDetailsTest.java index 43247f691f..5692c71883 100644 --- a/java_tools/proxy_server/src/test/java/com/rusefi/server/SessionDetailsTest.java +++ b/java_tools/proxy_server/src/test/java/com/rusefi/server/SessionDetailsTest.java @@ -19,7 +19,7 @@ public class SessionDetailsTest { public void testApplicationRequest() { ControllerInfo ci = new ControllerInfo("name", "make", "code", "sign"); SessionDetails sd = new SessionDetails(ci, "auth", 123); - ApplicationRequest ar = new ApplicationRequest(sd, 321); + ApplicationRequest ar = new ApplicationRequest(sd, new UserDetails("", 321)); String json = ar.toJson(); ApplicationRequest fromJson = ApplicationRequest.valueOf(json); diff --git a/java_tools/ts_plugin/src/main/java/com/rusefi/ts_plugin/PluginEntry.java b/java_tools/ts_plugin/src/main/java/com/rusefi/ts_plugin/PluginEntry.java index 523f560175..9f9a011387 100644 --- a/java_tools/ts_plugin/src/main/java/com/rusefi/ts_plugin/PluginEntry.java +++ b/java_tools/ts_plugin/src/main/java/com/rusefi/ts_plugin/PluginEntry.java @@ -10,15 +10,15 @@ import java.lang.reflect.Field; import java.util.function.Supplier; /** - * TsPlugin launcher creates an instance of this class via reflection. + * {@link TsPluginLauncher} creates an instance of this class via reflection. */ public class PluginEntry implements TsPluginBody { private final JPanel content = new JPanel(new BorderLayout()); - private final JTabbedPane tabbedPane = new JTabbedPane(); /** * the real constructor - this one is invoked via reflection */ + @SuppressWarnings("unused") public PluginEntry() { this(ControllerAccess::getInstance); } @@ -35,6 +35,7 @@ public class PluginEntry implements TsPluginBody { BroadcastTab broadcastTab = new BroadcastTab(); RemoteTab remoteTab = new RemoteTab(); + JTabbedPane tabbedPane = new JTabbedPane(); tabbedPane.addTab("Upload", uploadTab.getContent()); tabbedPane.addTab("Broadcast", broadcastTab.getContent()); tabbedPane.addTab("Remote ECU", remoteTab.getContent()); diff --git a/java_tools/ts_plugin/src/main/java/com/rusefi/ts_plugin/RemoteTab.java b/java_tools/ts_plugin/src/main/java/com/rusefi/ts_plugin/RemoteTab.java index fb85388755..bdd249c368 100644 --- a/java_tools/ts_plugin/src/main/java/com/rusefi/ts_plugin/RemoteTab.java +++ b/java_tools/ts_plugin/src/main/java/com/rusefi/ts_plugin/RemoteTab.java @@ -9,6 +9,7 @@ import com.rusefi.io.tcp.TcpIoStream; 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; @@ -18,6 +19,8 @@ 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; @@ -43,12 +46,27 @@ public class RemoteTab { } }; + + private final JButton disconnect = new JButton("Disconnect"); + private final Executor listDownloadExecutor = Executors.newSingleThreadExecutor(new NamedThreadFactory("online list downloader")); public RemoteTab() { JButton refresh = new JButton("Refresh List"); refresh.addActionListener(e -> requestListDownload()); + disconnect.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + LocalApplicationProxy localApplicationProxy = RemoteTabController.INSTANCE.getLocalApplicationProxy(); + if (localApplicationProxy != null) + localApplicationProxy.close(); + RemoteTabController.INSTANCE.setState(RemoteTabController.State.NOT_CONNECTED); + requestListDownload(); + } + }); + + JTextField applicationPort = new JTextField() { @Override public Dimension getPreferredSize() { @@ -78,7 +96,13 @@ public class RemoteTab { content.add(topLines, BorderLayout.NORTH); content.add(list, BorderLayout.CENTER); list.add(new JLabel("Requesting list of ECUs")); - requestListDownload(); + + LocalApplicationProxy currentState = RemoteTabController.INSTANCE.getLocalApplicationProxy(); + if (currentState == null) { + requestListDownload(); + } else { + setConnectedStatus(currentState.getApplicationRequest().getTargetUser()); + } } private String getLocalPort() { @@ -99,58 +123,99 @@ public class RemoteTab { } private void showList(List 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()); + JScrollPane scroll = new JScrollPane(verticalPanel, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + list.add(scroll); + for (PublicSession user : userDetails) { - list.add(createPanel(user)); + verticalPanel.add(createSessionControl(user)); } } AutoupdateUtil.trueLayout(list); } - private JComponent createPanel(PublicSession publicSession) { - JComponent userPanel = new JPanel(new FlowLayout()); - userPanel.add(new JLabel(publicSession.getUserDetails().getUserName())); + private JComponent createSessionControl(PublicSession publicSession) { + JComponent topLine = new JPanel(new FlowLayout()); + topLine.add(new JLabel(publicSession.getUserDetails().getUserName())); ControllerInfo controllerInfo = publicSession.getControllerInfo(); - userPanel.add(new JLabel(controllerInfo.getVehicleName() + " " + controllerInfo.getEngineMake() + " " + controllerInfo.getEngineCode())); + topLine.add(new JLabel(controllerInfo.getVehicleName() + " " + controllerInfo.getEngineMake() + " " + controllerInfo.getEngineCode())); - userPanel.add(new URLLabel(SignatureHelper.getUrl(controllerInfo.getSignature()))); + JPanel bottomPanel = new JPanel(new FlowLayout()); if (publicSession.isUsed()) { - userPanel.add(new JLabel(" used by " + publicSession.getOwnerName())); + bottomPanel.add(new JLabel(" Used by " + publicSession.getOwnerName())); } else { - JButton connect = new JButton("Connect"); - connect.addActionListener(event -> { - - setStatus("Connecting to " + publicSession.getUserDetails().getUserName()); - - new Thread(() -> runAuthenticator(publicSession, controllerInfo), "Authenticator").start(); - }); - userPanel.add(connect); + JButton connect = new JButton("Connect to " + publicSession.getUserDetails().getUserName()); + connect.addActionListener(event -> connecToToProxy(publicSession, controllerInfo)); + bottomPanel.add(connect); } + JPanel userPanel = new JPanel(new BorderLayout()); + + userPanel.add(topLine, BorderLayout.NORTH); + userPanel.add(new URLLabel(SignatureHelper.getUrl(controllerInfo.getSignature())), BorderLayout.CENTER); + userPanel.add(bottomPanel, BorderLayout.SOUTH); + + userPanel.setBorder(BorderFactory.createLineBorder(Color.BLACK)); + return userPanel; } - private void setStatus(String text) { + private void connecToToProxy(PublicSession publicSession, ControllerInfo controllerInfo) { + RemoteTabController.INSTANCE.setState(RemoteTabController.State.CONNECTING); + setStatus("Connecting to " + publicSession.getUserDetails().getUserName()); + + LocalApplicationProxy.ConnectionListener connectionListener = new LocalApplicationProxy.ConnectionListener() { + @Override + public void onConnected(LocalApplicationProxy localApplicationProxy) { + RemoteTabController.INSTANCE.setConnected(localApplicationProxy); + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + setConnectedStatus(publicSession.getUserDetails()); + } + }); + } + }; + + new Thread(() -> { + runAuthenticator(publicSession, controllerInfo, connectionListener); + }, "Authenticator").start(); + } + + private void setConnectedStatus(UserDetails userDetails) { + setStatus("Connected to " + userDetails.getUserName(), + new JLabel("You can now connect your TunerStudio to IP address localhost and port " + getLocalPort()), + disconnect); + } + + private void setStatus(String text, JComponent... extra) { list.removeAll(); list.add(new JLabel(text)); + for (JComponent component : extra) + list.add(component); AutoupdateUtil.trueLayout(list); } - private void runAuthenticator(PublicSession publicSession, ControllerInfo controllerInfo) { + private void runAuthenticator(PublicSession publicSession, ControllerInfo controllerInfo, LocalApplicationProxy.ConnectionListener connectionListener) { SessionDetails sessionDetails = new SessionDetails(controllerInfo, AuthTokenPanel.getAuthToken(), Integer.parseInt(oneTimePasswordControl.getText())); - ApplicationRequest applicationRequest = new ApplicationRequest(sessionDetails, publicSession.getUserDetails().getUserId()); + ApplicationRequest applicationRequest = new ApplicationRequest(sessionDetails, publicSession.getUserDetails()); try { AtomicReference serverHolderAtomicReference = new AtomicReference<>(); TcpIoStream.DisconnectListener disconnectListener = () -> SwingUtilities.invokeLater(() -> { setStatus("Disconnected"); + RemoteTabController.INSTANCE.setState(RemoteTabController.State.NOT_CONNECTED); ServerHolder serverHolder = serverHolderAtomicReference.get(); if (serverHolder != null) serverHolder.close(); @@ -160,7 +225,7 @@ public class RemoteTab { LocalApplicationProxy.SERVER_PORT_FOR_APPLICATIONS, applicationRequest, Integer.parseInt(getLocalPort()), - HttpUtil.PROXY_JSON_API_HTTP_PORT, disconnectListener); + HttpUtil.PROXY_JSON_API_HTTP_PORT, disconnectListener, connectionListener); serverHolderAtomicReference.set(serverHolder); } catch (IOException e) { setStatus("IO error: " + e); diff --git a/java_tools/ts_plugin/src/main/java/com/rusefi/ts_plugin/RemoteTabController.java b/java_tools/ts_plugin/src/main/java/com/rusefi/ts_plugin/RemoteTabController.java new file mode 100644 index 0000000000..b623c22137 --- /dev/null +++ b/java_tools/ts_plugin/src/main/java/com/rusefi/ts_plugin/RemoteTabController.java @@ -0,0 +1,37 @@ +package com.rusefi.ts_plugin; + +import com.rusefi.LocalApplicationProxy; + +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 synchronized void setState(State state) { + this.state = state; + localApplicationProxy = null; + } + + 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 + } +} diff --git a/java_tools/ts_plugin_launcher/src/com/rusefi/ts_plugin/TsPluginLauncher.java b/java_tools/ts_plugin_launcher/src/com/rusefi/ts_plugin/TsPluginLauncher.java index ab4e3a5ee1..0f47aca0a2 100644 --- a/java_tools/ts_plugin_launcher/src/com/rusefi/ts_plugin/TsPluginLauncher.java +++ b/java_tools/ts_plugin_launcher/src/com/rusefi/ts_plugin/TsPluginLauncher.java @@ -17,7 +17,7 @@ public class TsPluginLauncher implements ApplicationPlugin { private final JPanel content = new JPanel(new VerticalFlowLayout()); public TsPluginLauncher() { - System.out.println("TsPluginLauncher " + this); + System.out.println("init " + this); } @Override @@ -46,6 +46,7 @@ public class TsPluginLauncher implements ApplicationPlugin { @Override public boolean displayPlugin(String signature) { + System.out.println("displayPlugin " + signature); // todo: smarter implementation one day return true; } @@ -63,7 +64,8 @@ public class TsPluginLauncher implements ApplicationPlugin { @Override public JComponent getPluginPanel() { synchronized (this) { - // only create content if TS is actually planning to display this plugin instance + // lazy initialization since TunerStudio creates one instance only to get version information without any + // intentions to display the UI if (content.getComponents().length == 0) { System.out.println("Create Updater " + this); Updater updater = new Updater(); @@ -75,7 +77,7 @@ public class TsPluginLauncher implements ApplicationPlugin { @Override public void close() { - System.out.printf("TsPlugin#close"); + System.out.println("close " + this); } @Override