proxy progress - backend and frontend
This commit is contained in:
parent
f42e08c8e5
commit
98404eb69b
|
@ -1,8 +1,6 @@
|
||||||
package com.rusefi;
|
package com.rusefi;
|
||||||
|
|
||||||
import com.devexperts.logging.Logging;
|
import com.devexperts.logging.Logging;
|
||||||
import com.opensr5.Logger;
|
|
||||||
import com.rusefi.core.MessagesCentral;
|
|
||||||
import com.rusefi.io.IoStream;
|
import com.rusefi.io.IoStream;
|
||||||
import com.rusefi.io.commands.HelloCommand;
|
import com.rusefi.io.commands.HelloCommand;
|
||||||
import com.rusefi.io.tcp.BinaryProtocolProxy;
|
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.HttpUtil;
|
||||||
import com.rusefi.tools.online.ProxyClient;
|
import com.rusefi.tools.online.ProxyClient;
|
||||||
|
|
||||||
|
import java.io.Closeable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import static com.devexperts.logging.Logging.getLogging;
|
import static com.devexperts.logging.Logging.getLogging;
|
||||||
|
|
||||||
public class LocalApplicationProxy {
|
public class LocalApplicationProxy implements Closeable {
|
||||||
private static final Logging log = getLogging(LocalApplicationProxy.class);
|
private static final Logging log = getLogging(LocalApplicationProxy.class);
|
||||||
public static final int SERVER_PORT_FOR_APPLICATIONS = HttpUtil.getIntProperty("applications.port", 8002);
|
public static final int SERVER_PORT_FOR_APPLICATIONS = HttpUtil.getIntProperty("applications.port", 8002);
|
||||||
private final ApplicationRequest applicationRequest;
|
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.applicationRequest = applicationRequest;
|
||||||
|
this.serverHolder = serverHolder;
|
||||||
|
this.authenticatorToProxyStream = authenticatorToProxyStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ApplicationRequest getApplicationRequest() {
|
||||||
|
return applicationRequest;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param serverPortForRemoteUsers port on which rusEFI proxy accepts authenticator connections
|
* @param serverPortForRemoteUsers port on which rusEFI proxy accepts authenticator connections
|
||||||
* @param applicationRequest remote session we want to connect to
|
* @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 jsonHttpPort
|
||||||
* @param disconnectListener
|
* @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);
|
String version = HttpUtil.executeGet(ProxyClient.getHttpAddress(jsonHttpPort) + ProxyClient.VERSION_PATH);
|
||||||
log.info("Server says version=" + version);
|
log.info("Server says version=" + version);
|
||||||
if (!version.contains(ProxyClient.BACKEND_VERSION))
|
if (!version.contains(ProxyClient.BACKEND_VERSION))
|
||||||
throw new IOException("Unexpected backend version " + version + " while we want " + 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);
|
IoStream authenticatorToProxyStream = new TcpIoStream("authenticatorToProxyStream ", rusEFISSLContext.getSSLSocket(HttpUtil.RUSEFI_PROXY_HOSTNAME, serverPortForRemoteUsers), disconnectListener);
|
||||||
LocalApplicationProxy localApplicationProxy = new LocalApplicationProxy(applicationRequest);
|
LocalApplicationProxy.sendHello(authenticatorToProxyStream, applicationRequest);
|
||||||
log.info("Pushing " + applicationRequest);
|
|
||||||
localApplicationProxy.run(authenticatorToProxyStream);
|
|
||||||
|
|
||||||
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
|
// right from connection push session authentication data
|
||||||
new HelloCommand(applicationRequest.toJson()).handle(authenticatorToProxyStream);
|
new HelloCommand(applicationRequest.toJson()).handle(authenticatorToProxyStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void start(String[] strings) {
|
public static void start(String[] strings) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
serverHolder.close();
|
||||||
|
authenticatorToProxyStream.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface ConnectionListener {
|
||||||
|
ConnectionListener VOID = localApplicationProxy -> {
|
||||||
|
};
|
||||||
|
|
||||||
|
void onConnected(LocalApplicationProxy localApplicationProxy);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,38 +7,37 @@ import java.util.Objects;
|
||||||
|
|
||||||
public class ApplicationRequest {
|
public class ApplicationRequest {
|
||||||
private static final String SESSION = "session";
|
private static final String SESSION = "session";
|
||||||
private static final String USER_ID = "user_id";
|
|
||||||
|
|
||||||
private final SessionDetails sessionDetails;
|
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.sessionDetails = sessionDetails;
|
||||||
this.targetUserId = targetUserId;
|
this.targetUser = targetUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SessionDetails getSessionDetails() {
|
public SessionDetails getSessionDetails() {
|
||||||
return sessionDetails;
|
return sessionDetails;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getTargetUserId() {
|
public UserDetails getTargetUser() {
|
||||||
return targetUserId;
|
return targetUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toJson() {
|
public String toJson() {
|
||||||
JSONObject jsonObject = new JSONObject();
|
JSONObject jsonObject = new JSONObject();
|
||||||
jsonObject.put(SESSION, sessionDetails.toJson());
|
jsonObject.put(SESSION, sessionDetails.toJson());
|
||||||
jsonObject.put(USER_ID, targetUserId);
|
targetUser.put(jsonObject);
|
||||||
return jsonObject.toJSONString();
|
return jsonObject.toJSONString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ApplicationRequest valueOf(String jsonString) {
|
public static ApplicationRequest valueOf(String jsonString) {
|
||||||
JSONObject jsonObject = HttpUtil.parse(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));
|
SessionDetails session = SessionDetails.valueOf((String) jsonObject.get(SESSION));
|
||||||
return new ApplicationRequest(session, (int)targetUserId);
|
return new ApplicationRequest(session, userDetails);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -46,20 +45,20 @@ public class ApplicationRequest {
|
||||||
if (this == o) return true;
|
if (this == o) return true;
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
ApplicationRequest that = (ApplicationRequest) o;
|
ApplicationRequest that = (ApplicationRequest) o;
|
||||||
return targetUserId == that.targetUserId &&
|
return targetUser == that.targetUser &&
|
||||||
sessionDetails.equals(that.sessionDetails);
|
sessionDetails.equals(that.sessionDetails);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hash(sessionDetails, targetUserId);
|
return Objects.hash(sessionDetails, targetUser);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "ApplicationRequest{" +
|
return "ApplicationRequest{" +
|
||||||
"sessionDetails=" + sessionDetails +
|
"sessionDetails=" + sessionDetails +
|
||||||
", targetUserId=" + targetUserId +
|
", targetUserId=" + targetUser +
|
||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,11 @@ public class UserDetails {
|
||||||
return new UserDetails(userName, (int) userId);
|
return new UserDetails(userName, (int) userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void put(JSONObject jsonObject) {
|
||||||
|
jsonObject.put(USER_ID, getUserId());
|
||||||
|
jsonObject.put(USERNAME, getUserName());
|
||||||
|
}
|
||||||
|
|
||||||
public String getUserName() {
|
public String getUserName() {
|
||||||
return userName;
|
return userName;
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,11 +78,13 @@ public class FullServerTest {
|
||||||
assertTrue("controllerRegistered", controllerRegistered.await(READ_IMAGE_TIMEOUT, TimeUnit.MILLISECONDS));
|
assertTrue("controllerRegistered", controllerRegistered.await(READ_IMAGE_TIMEOUT, TimeUnit.MILLISECONDS));
|
||||||
|
|
||||||
SessionDetails authenticatorSessionDetails = new SessionDetails(controllerInfo, MockRusEfiDevice.TEST_TOKEN_3, networkConnectorResult.getOneTimeToken());
|
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
|
// start authenticator
|
||||||
int authenticatorPort = 7004; // local port on which authenticator accepts connections from Tuner Studio
|
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);
|
CountDownLatch connectionEstablishedCountDownLatch = new CountDownLatch(1);
|
||||||
|
|
|
@ -185,12 +185,11 @@ covered by FullServerTest
|
||||||
TestHelper.runApplicationConnectorBlocking(backend, serverPortForRemoteUsers);
|
TestHelper.runApplicationConnectorBlocking(backend, serverPortForRemoteUsers);
|
||||||
|
|
||||||
SessionDetails sessionDetails = MockRusEfiDevice.createTestSession(MockRusEfiDevice.TEST_TOKEN_1, Fields.TS_SIGNATURE);
|
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
|
// start authenticator
|
||||||
IoStream authenticatorToProxyStream = TestHelper.secureConnectToLocalhost(serverPortForRemoteUsers, logger);
|
IoStream authenticatorToProxyStream = TestHelper.secureConnectToLocalhost(serverPortForRemoteUsers, logger);
|
||||||
LocalApplicationProxy localApplicationProxy = new LocalApplicationProxy(applicationRequest);
|
LocalApplicationProxy.sendHello(authenticatorToProxyStream, applicationRequest);
|
||||||
localApplicationProxy.run(authenticatorToProxyStream);
|
|
||||||
|
|
||||||
assertTrue(disconnectedCountDownLatch.await(30, TimeUnit.SECONDS));
|
assertTrue(disconnectedCountDownLatch.await(30, TimeUnit.SECONDS));
|
||||||
}
|
}
|
||||||
|
|
|
@ -184,7 +184,7 @@ public class Backend implements Closeable {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ControllerKey controllerKey = new ControllerKey(applicationRequest.getTargetUserId(), applicationRequest.getSessionDetails().getControllerInfo());
|
ControllerKey controllerKey = new ControllerKey(applicationRequest.getTargetUser().getUserId(), applicationRequest.getSessionDetails().getControllerInfo());
|
||||||
ControllerConnectionState state;
|
ControllerConnectionState state;
|
||||||
synchronized (lock) {
|
synchronized (lock) {
|
||||||
state = acquire(controllerKey, userDetails);
|
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
|
* that's different from controllers since we periodically pull outputs from controllers which allows us to detect disconnects
|
||||||
*/
|
*/
|
||||||
private void runApplicationConnectionsCleanup() {
|
private void runApplicationConnectionsCleanup() {
|
||||||
List<ApplicationConnectionState> inactiveApplications = new ArrayList<>();
|
List<ApplicationConnectionState> applications;
|
||||||
|
|
||||||
synchronized (lock) {
|
synchronized (lock) {
|
||||||
long now = System.currentTimeMillis();
|
applications = new ArrayList<>(this.applications);
|
||||||
for (ApplicationConnectionState client : applications) {
|
}
|
||||||
if (now - client.getClientStream().getStreamStats().getPreviousPacketArrivalTime() > applicationTimeout)
|
|
||||||
inactiveApplications.add(client);
|
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) {
|
List<ControllerConnectionState> controllers;
|
||||||
log.error("Kicking out application " + inactiveClient);
|
synchronized (lock) {
|
||||||
close(inactiveClient);
|
controllers = new ArrayList<>(this.controllers);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ControllerConnectionState controllerConnectionState : controllers) {
|
||||||
|
log.info("State: " + controllerConnectionState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -75,6 +75,15 @@ public class ControllerConnectionState {
|
||||||
FileUtil.close(clientSocket);
|
FileUtil.close(clientSocket);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "ControllerConnectionState{" +
|
||||||
|
"userDetails=" + userDetails +
|
||||||
|
", isClosed=" + isClosed +
|
||||||
|
", twoKindSemaphore=" + twoKindSemaphore +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
|
||||||
public void requestControllerInfo() throws IOException {
|
public void requestControllerInfo() throws IOException {
|
||||||
HelloCommand.send(stream);
|
HelloCommand.send(stream);
|
||||||
String jsonString = HelloCommand.getHelloResponse(incomingData);
|
String jsonString = HelloCommand.getHelloResponse(incomingData);
|
||||||
|
|
|
@ -52,4 +52,12 @@ public class TwoKindSemaphore {
|
||||||
public UserDetails getOwner() {
|
public UserDetails getOwner() {
|
||||||
return owner;
|
return owner;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "TwoKindSemaphore{" +
|
||||||
|
"semaphore=" + semaphore +
|
||||||
|
", owner=" + owner +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ public class SessionDetailsTest {
|
||||||
public void testApplicationRequest() {
|
public void testApplicationRequest() {
|
||||||
ControllerInfo ci = new ControllerInfo("name", "make", "code", "sign");
|
ControllerInfo ci = new ControllerInfo("name", "make", "code", "sign");
|
||||||
SessionDetails sd = new SessionDetails(ci, "auth", 123);
|
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();
|
String json = ar.toJson();
|
||||||
ApplicationRequest fromJson = ApplicationRequest.valueOf(json);
|
ApplicationRequest fromJson = ApplicationRequest.valueOf(json);
|
||||||
|
|
|
@ -10,15 +10,15 @@ import java.lang.reflect.Field;
|
||||||
import java.util.function.Supplier;
|
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 {
|
public class PluginEntry implements TsPluginBody {
|
||||||
private final JPanel content = new JPanel(new BorderLayout());
|
private final JPanel content = new JPanel(new BorderLayout());
|
||||||
private final JTabbedPane tabbedPane = new JTabbedPane();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* the real constructor - this one is invoked via reflection
|
* the real constructor - this one is invoked via reflection
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("unused")
|
||||||
public PluginEntry() {
|
public PluginEntry() {
|
||||||
this(ControllerAccess::getInstance);
|
this(ControllerAccess::getInstance);
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,7 @@ public class PluginEntry implements TsPluginBody {
|
||||||
BroadcastTab broadcastTab = new BroadcastTab();
|
BroadcastTab broadcastTab = new BroadcastTab();
|
||||||
RemoteTab remoteTab = new RemoteTab();
|
RemoteTab remoteTab = new RemoteTab();
|
||||||
|
|
||||||
|
JTabbedPane tabbedPane = new JTabbedPane();
|
||||||
tabbedPane.addTab("Upload", uploadTab.getContent());
|
tabbedPane.addTab("Upload", uploadTab.getContent());
|
||||||
tabbedPane.addTab("Broadcast", broadcastTab.getContent());
|
tabbedPane.addTab("Broadcast", broadcastTab.getContent());
|
||||||
tabbedPane.addTab("Remote ECU", remoteTab.getContent());
|
tabbedPane.addTab("Remote ECU", remoteTab.getContent());
|
||||||
|
|
|
@ -9,6 +9,7 @@ import com.rusefi.io.tcp.TcpIoStream;
|
||||||
import com.rusefi.server.ApplicationRequest;
|
import com.rusefi.server.ApplicationRequest;
|
||||||
import com.rusefi.server.ControllerInfo;
|
import com.rusefi.server.ControllerInfo;
|
||||||
import com.rusefi.server.SessionDetails;
|
import com.rusefi.server.SessionDetails;
|
||||||
|
import com.rusefi.server.UserDetails;
|
||||||
import com.rusefi.tools.online.HttpUtil;
|
import com.rusefi.tools.online.HttpUtil;
|
||||||
import com.rusefi.tools.online.ProxyClient;
|
import com.rusefi.tools.online.ProxyClient;
|
||||||
import com.rusefi.tools.online.PublicSession;
|
import com.rusefi.tools.online.PublicSession;
|
||||||
|
@ -18,6 +19,8 @@ import org.putgemin.VerticalFlowLayout;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.awt.event.ActionListener;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.Executor;
|
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"));
|
private final Executor listDownloadExecutor = Executors.newSingleThreadExecutor(new NamedThreadFactory("online list downloader"));
|
||||||
|
|
||||||
public RemoteTab() {
|
public RemoteTab() {
|
||||||
JButton refresh = new JButton("Refresh List");
|
JButton refresh = new JButton("Refresh List");
|
||||||
refresh.addActionListener(e -> requestListDownload());
|
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() {
|
JTextField applicationPort = new JTextField() {
|
||||||
@Override
|
@Override
|
||||||
public Dimension getPreferredSize() {
|
public Dimension getPreferredSize() {
|
||||||
|
@ -78,7 +96,13 @@ public class RemoteTab {
|
||||||
content.add(topLines, BorderLayout.NORTH);
|
content.add(topLines, BorderLayout.NORTH);
|
||||||
content.add(list, BorderLayout.CENTER);
|
content.add(list, BorderLayout.CENTER);
|
||||||
list.add(new JLabel("Requesting list of ECUs"));
|
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() {
|
private String getLocalPort() {
|
||||||
|
@ -99,58 +123,99 @@ public class RemoteTab {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showList(List<PublicSession> userDetails) {
|
private void showList(List<PublicSession> userDetails) {
|
||||||
|
if (RemoteTabController.INSTANCE.getState() != RemoteTabController.State.NOT_CONNECTED)
|
||||||
|
return;
|
||||||
list.removeAll();
|
list.removeAll();
|
||||||
if (userDetails.isEmpty()) {
|
if (userDetails.isEmpty()) {
|
||||||
list.add(new JLabel("No ECUs are broadcasting at the moment :("));
|
list.add(new JLabel("No ECUs are broadcasting at the moment :("));
|
||||||
} else {
|
} 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) {
|
for (PublicSession user : userDetails) {
|
||||||
list.add(createPanel(user));
|
verticalPanel.add(createSessionControl(user));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AutoupdateUtil.trueLayout(list);
|
AutoupdateUtil.trueLayout(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
private JComponent createPanel(PublicSession publicSession) {
|
private JComponent createSessionControl(PublicSession publicSession) {
|
||||||
JComponent userPanel = new JPanel(new FlowLayout());
|
JComponent topLine = new JPanel(new FlowLayout());
|
||||||
userPanel.add(new JLabel(publicSession.getUserDetails().getUserName()));
|
topLine.add(new JLabel(publicSession.getUserDetails().getUserName()));
|
||||||
ControllerInfo controllerInfo = publicSession.getControllerInfo();
|
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()) {
|
if (publicSession.isUsed()) {
|
||||||
userPanel.add(new JLabel(" used by " + publicSession.getOwnerName()));
|
bottomPanel.add(new JLabel(" Used by " + publicSession.getOwnerName()));
|
||||||
} else {
|
} else {
|
||||||
JButton connect = new JButton("Connect");
|
JButton connect = new JButton("Connect to " + publicSession.getUserDetails().getUserName());
|
||||||
connect.addActionListener(event -> {
|
connect.addActionListener(event -> connecToToProxy(publicSession, controllerInfo));
|
||||||
|
bottomPanel.add(connect);
|
||||||
setStatus("Connecting to " + publicSession.getUserDetails().getUserName());
|
|
||||||
|
|
||||||
new Thread(() -> runAuthenticator(publicSession, controllerInfo), "Authenticator").start();
|
|
||||||
});
|
|
||||||
userPanel.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;
|
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.removeAll();
|
||||||
list.add(new JLabel(text));
|
list.add(new JLabel(text));
|
||||||
|
for (JComponent component : extra)
|
||||||
|
list.add(component);
|
||||||
AutoupdateUtil.trueLayout(list);
|
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(),
|
SessionDetails sessionDetails = new SessionDetails(controllerInfo, AuthTokenPanel.getAuthToken(),
|
||||||
Integer.parseInt(oneTimePasswordControl.getText()));
|
Integer.parseInt(oneTimePasswordControl.getText()));
|
||||||
|
|
||||||
ApplicationRequest applicationRequest = new ApplicationRequest(sessionDetails, publicSession.getUserDetails().getUserId());
|
ApplicationRequest applicationRequest = new ApplicationRequest(sessionDetails, publicSession.getUserDetails());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
AtomicReference<ServerHolder> serverHolderAtomicReference = new AtomicReference<>();
|
AtomicReference<ServerHolder> serverHolderAtomicReference = new AtomicReference<>();
|
||||||
|
|
||||||
TcpIoStream.DisconnectListener disconnectListener = () -> SwingUtilities.invokeLater(() -> {
|
TcpIoStream.DisconnectListener disconnectListener = () -> SwingUtilities.invokeLater(() -> {
|
||||||
setStatus("Disconnected");
|
setStatus("Disconnected");
|
||||||
|
RemoteTabController.INSTANCE.setState(RemoteTabController.State.NOT_CONNECTED);
|
||||||
ServerHolder serverHolder = serverHolderAtomicReference.get();
|
ServerHolder serverHolder = serverHolderAtomicReference.get();
|
||||||
if (serverHolder != null)
|
if (serverHolder != null)
|
||||||
serverHolder.close();
|
serverHolder.close();
|
||||||
|
@ -160,7 +225,7 @@ public class RemoteTab {
|
||||||
LocalApplicationProxy.SERVER_PORT_FOR_APPLICATIONS,
|
LocalApplicationProxy.SERVER_PORT_FOR_APPLICATIONS,
|
||||||
applicationRequest,
|
applicationRequest,
|
||||||
Integer.parseInt(getLocalPort()),
|
Integer.parseInt(getLocalPort()),
|
||||||
HttpUtil.PROXY_JSON_API_HTTP_PORT, disconnectListener);
|
HttpUtil.PROXY_JSON_API_HTTP_PORT, disconnectListener, connectionListener);
|
||||||
serverHolderAtomicReference.set(serverHolder);
|
serverHolderAtomicReference.set(serverHolder);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
setStatus("IO error: " + e);
|
setStatus("IO error: " + e);
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,7 +17,7 @@ public class TsPluginLauncher implements ApplicationPlugin {
|
||||||
private final JPanel content = new JPanel(new VerticalFlowLayout());
|
private final JPanel content = new JPanel(new VerticalFlowLayout());
|
||||||
|
|
||||||
public TsPluginLauncher() {
|
public TsPluginLauncher() {
|
||||||
System.out.println("TsPluginLauncher " + this);
|
System.out.println("init " + this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -46,6 +46,7 @@ public class TsPluginLauncher implements ApplicationPlugin {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean displayPlugin(String signature) {
|
public boolean displayPlugin(String signature) {
|
||||||
|
System.out.println("displayPlugin " + signature);
|
||||||
// todo: smarter implementation one day
|
// todo: smarter implementation one day
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -63,7 +64,8 @@ public class TsPluginLauncher implements ApplicationPlugin {
|
||||||
@Override
|
@Override
|
||||||
public JComponent getPluginPanel() {
|
public JComponent getPluginPanel() {
|
||||||
synchronized (this) {
|
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) {
|
if (content.getComponents().length == 0) {
|
||||||
System.out.println("Create Updater " + this);
|
System.out.println("Create Updater " + this);
|
||||||
Updater updater = new Updater();
|
Updater updater = new Updater();
|
||||||
|
@ -75,7 +77,7 @@ public class TsPluginLauncher implements ApplicationPlugin {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
System.out.printf("TsPlugin#close");
|
System.out.println("close " + this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
Loading…
Reference in New Issue