remote SD card access

This commit is contained in:
rusefi 2020-09-30 23:40:22 -04:00
parent 609fe10a93
commit 70e23dafbf
8 changed files with 305 additions and 178 deletions

View File

@ -36,11 +36,17 @@ import java.util.concurrent.atomic.AtomicInteger;
import static com.devexperts.logging.Logging.getLogging; import static com.devexperts.logging.Logging.getLogging;
import static com.rusefi.binaryprotocol.BinaryProtocol.sleep; import static com.rusefi.binaryprotocol.BinaryProtocol.sleep;
/**
* Remote user process which facilitates connection between local tuning application and real ECU via rusEFI proxy service
*/
public class LocalApplicationProxy implements Closeable { public class LocalApplicationProxy implements Closeable {
private static final ThreadFactory THREAD_FACTORY = new NamedThreadFactory("gauge poking"); private static final ThreadFactory THREAD_FACTORY = new NamedThreadFactory("gauge poking");
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;
/**
* local TCP server socket which local tuning application connects to
*/
private final ServerSocketReference serverHolder; private final ServerSocketReference serverHolder;
private final IoStream authenticatorToProxyStream; private final IoStream authenticatorToProxyStream;
@ -50,6 +56,10 @@ public class LocalApplicationProxy implements Closeable {
this.authenticatorToProxyStream = authenticatorToProxyStream; this.authenticatorToProxyStream = authenticatorToProxyStream;
} }
public IoStream getAuthenticatorToProxyStream() {
return authenticatorToProxyStream;
}
public static HttpResponse requestSoftwareUpdate(int httpPort, ApplicationRequest applicationRequest, UpdateType type) throws IOException { public static HttpResponse requestSoftwareUpdate(int httpPort, ApplicationRequest applicationRequest, UpdateType type) throws IOException {
HttpPost httpPost = new HttpPost(ProxyClient.getHttpAddress(httpPort) + ProxyClient.UPDATE_CONNECTOR_SOFTWARE); HttpPost httpPost = new HttpPost(ProxyClient.getHttpAddress(httpPort) + ProxyClient.UPDATE_CONNECTOR_SOFTWARE);

View File

@ -0,0 +1,44 @@
package com.rusefi.ts_plugin;
import com.efiAnalytics.plugin.ecu.ControllerAccess;
import com.rusefi.io.ConnectionStateListener;
import com.rusefi.io.IoStream;
import javax.swing.*;
import java.awt.*;
import java.util.function.Supplier;
public class LocalSdCardReader {
private final JPanel content = new JPanel(new BorderLayout());
private final SdCardReaderPanel sdCardReaderPanel;
public LocalSdCardReader(Supplier<ControllerAccess> controllerAccessSupplier) {
JPanel topPanel = new JPanel(new BorderLayout());
ConnectPanel connectPanel = new ConnectPanel(new ConnectionStateListener() {
public void onConnectionEstablished() {
sdCardReaderPanel.onConnectionEstablished();
}
public void onConnectionFailed() {
}
});
topPanel.add(connectPanel.getContent(), BorderLayout.NORTH);
sdCardReaderPanel = new SdCardReaderPanel(controllerAccessSupplier, new Supplier<IoStream>() {
@Override
public IoStream get() {
return connectPanel.getControllerConnector().getConnector().getBinaryProtocol().getStream();
}
}, content.getParent());
content.add(topPanel, BorderLayout.NORTH);
content.add(sdCardReaderPanel.getContent(), BorderLayout.CENTER);
content.add(new JLabel("<html>This tab allows direct access to SD card<br/>Please be sure to disconnect Tuner Studio from ECU while downloading files using this tab"), BorderLayout.SOUTH);
}
public Component getContent() {
return new JScrollPane(content, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
}
}

View File

@ -20,10 +20,13 @@ import java.util.function.Supplier;
* @see PluginBodySandbox * @see PluginBodySandbox
*/ */
public class PluginEntry implements TsPluginBody { public class PluginEntry implements TsPluginBody {
private static final String LOCAL_SD_CARD = "Local SD Card";
private static final String REMOTE_SD_CARD = "Remote SD Card";
private final JPanel content = new JPanel(new BorderLayout()); private final JPanel content = new JPanel(new BorderLayout());
static final ImageIcon LOGO = AutoupdateUtil.loadIcon("/rusefi_online_color_300.png"); 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 * the real constructor - this one is invoked via reflection
@ -45,15 +48,32 @@ public class PluginEntry implements TsPluginBody {
TuneUploadTab tuneUploadTab = new TuneUploadTab(controllerAccessSupplier); TuneUploadTab tuneUploadTab = new TuneUploadTab(controllerAccessSupplier);
LogUploadSelector logUploadTab = new LogUploadSelector(controllerAccessSupplier); LogUploadSelector logUploadTab = new LogUploadSelector(controllerAccessSupplier);
BroadcastTab broadcastTab = new BroadcastTab(); BroadcastTab broadcastTab = new BroadcastTab();
RemoteTab remoteTab = new RemoteTab(); Component localSdCard = new LocalSdCardReader(controllerAccessSupplier).getContent();
Component remoteSdCard = new RemoteSdCardReader(controllerAccessSupplier).getContent();
RemoteTab remoteTab = new RemoteTab(new RemoteTab.Listener() {
@Override
public void onConnected() {
tabbedPane.remove(localSdCard);
tabbedPane.addTab(REMOTE_SD_CARD, remoteSdCard);
}
});
RemoteTabController.INSTANCE.listeners.add(new RemoteTabController.Listener() {
@Override
public void onChange(RemoteTabController.State state) {
if (state == RemoteTabController.State.NOT_CONNECTED) {
tabbedPane.remove(remoteSdCard);
tabbedPane.addTab(LOCAL_SD_CARD, localSdCard);
}
}
});
JTabbedPane tabbedPane = new JTabbedPane();
tabbedPane.addTab("Tune Upload", tuneUploadTab.getContent()); tabbedPane.addTab("Tune Upload", tuneUploadTab.getContent());
tabbedPane.addTab("Log Upload", logUploadTab.getContent()); tabbedPane.addTab("Log Upload", logUploadTab.getContent());
tabbedPane.addTab("Broadcast", broadcastTab.getContent()); tabbedPane.addTab("Broadcast", broadcastTab.getContent());
tabbedPane.addTab("Remote ECU", remoteTab.getContent()); tabbedPane.addTab("Remote ECU", remoteTab.getContent());
tabbedPane.addTab("Read SD Card", new SdCardReader(controllerAccessSupplier).getContent()); tabbedPane.addTab(LOCAL_SD_CARD, localSdCard);
content.add(tabbedPane); this.content.add(tabbedPane);
InstanceAuthContext.startup(); InstanceAuthContext.startup();
} }

View File

@ -0,0 +1,36 @@
package com.rusefi.ts_plugin;
import com.efiAnalytics.plugin.ecu.ControllerAccess;
import com.rusefi.io.IoStream;
import com.rusefi.proxy.client.LocalApplicationProxy;
import javax.swing.*;
import java.awt.*;
import java.util.function.Supplier;
public class RemoteSdCardReader {
private final JPanel content = new JPanel(new BorderLayout());
private final SdCardReaderPanel sdCardReaderPanel;
public RemoteSdCardReader(Supplier<ControllerAccess> controllerAccessSupplier) {
sdCardReaderPanel = new SdCardReaderPanel(controllerAccessSupplier, new Supplier<IoStream>() {
@Override
public IoStream get() {
LocalApplicationProxy localApplicationProxy = RemoteTabController.INSTANCE.getLocalApplicationProxy();
if (localApplicationProxy == null)
throw new NullPointerException("Not connected");
return localApplicationProxy.getAuthenticatorToProxyStream();
}
}, content.getParent());
content.add(sdCardReaderPanel.getContent(), BorderLayout.CENTER);
}
public Component getContent() {
return new JScrollPane(content, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
}
}

View File

@ -61,6 +61,7 @@ public class RemoteTab {
return new Dimension(100, size.height); return new Dimension(100, size.height);
} }
}; };
private final Listener listener;
private StreamStatusControl streamStatusControl = null; private StreamStatusControl streamStatusControl = null;
@ -69,7 +70,8 @@ public class RemoteTab {
private final Executor listDownloadExecutor = Executors.newSingleThreadExecutor(new NamedThreadFactory("online list downloader", true)); private final Executor listDownloadExecutor = Executors.newSingleThreadExecutor(new NamedThreadFactory("online list downloader", true));
public RemoteTab() { public RemoteTab(Listener listener) {
this.listener = listener;
JButton refresh = new JButton("Refresh Remote Controllers List"); JButton refresh = new JButton("Refresh Remote Controllers List");
refresh.addActionListener(e -> requestControllersList()); refresh.addActionListener(e -> requestControllersList());
@ -259,6 +261,7 @@ public class RemoteTab {
streamStatusControl = new StreamStatusControl(authenticatorToProxyStream); streamStatusControl = new StreamStatusControl(authenticatorToProxyStream);
} }
listener.onConnected();
setStatus("Connected to " + userDetails.getUserName(), setStatus("Connected to " + userDetails.getUserName(),
new JLabel("You can now connect your TunerStudio to IP address localhost and port " + getLocalPort()), new JLabel("You can now connect your TunerStudio to IP address localhost and port " + getLocalPort()),
new URLLabel(SignatureHelper.getUrl(controllerInfo.getSignature()).first), new URLLabel(SignatureHelper.getUrl(controllerInfo.getSignature()).first),
@ -319,4 +322,8 @@ public class RemoteTab {
public JComponent getContent() { public JComponent getContent() {
return scroll; return scroll;
} }
interface Listener {
void onConnected();
}
} }

View File

@ -2,6 +2,8 @@ package com.rusefi.ts_plugin;
import com.rusefi.proxy.client.LocalApplicationProxy; import com.rusefi.proxy.client.LocalApplicationProxy;
import java.util.concurrent.CopyOnWriteArrayList;
public enum RemoteTabController { public enum RemoteTabController {
/** /**
* TunerStudio likes to close plugin panel, we need a singleton to preserve the state * TunerStudio likes to close plugin panel, we need a singleton to preserve the state
@ -11,9 +13,15 @@ public enum RemoteTabController {
private State state = State.NOT_CONNECTED; private State state = State.NOT_CONNECTED;
private LocalApplicationProxy localApplicationProxy; private LocalApplicationProxy localApplicationProxy;
public final CopyOnWriteArrayList<Listener> listeners = new CopyOnWriteArrayList<>();
public synchronized void setState(State state) { public synchronized void setState(State state) {
this.state = state;
localApplicationProxy = null; localApplicationProxy = null;
if (state != this.state) {
this.state = state;
for (Listener listener : listeners)
listener.onChange(state);
}
} }
public synchronized State getState() { public synchronized State getState() {
@ -34,4 +42,8 @@ public enum RemoteTabController {
CONNECTING, CONNECTING,
CONNECTED CONNECTED
} }
interface Listener {
void onChange(State state);
}
} }

View File

@ -4,7 +4,6 @@ import com.devexperts.logging.Logging;
import com.efiAnalytics.plugin.ecu.ControllerAccess; import com.efiAnalytics.plugin.ecu.ControllerAccess;
import com.rusefi.autoupdate.AutoupdateUtil; import com.rusefi.autoupdate.AutoupdateUtil;
import com.rusefi.config.generated.Fields; import com.rusefi.config.generated.Fields;
import com.rusefi.io.ConnectionStateListener;
import com.rusefi.io.IoStream; import com.rusefi.io.IoStream;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.putgemin.VerticalFlowLayout; import org.putgemin.VerticalFlowLayout;
@ -25,66 +24,199 @@ import static com.devexperts.logging.Logging.getLogging;
import static com.rusefi.config.generated.Fields.TS_SD_PROTOCOL_FETCH_INFO; import static com.rusefi.config.generated.Fields.TS_SD_PROTOCOL_FETCH_INFO;
import static com.rusefi.shared.FileUtil.close; import static com.rusefi.shared.FileUtil.close;
public class SdCardReader { public class SdCardReaderPanel {
private static final Logging log = getLogging(SdCardReader.class);
private final static int TRANSFER_HEADER_SIZE = 3; private final static int TRANSFER_HEADER_SIZE = 3;
private final JPanel content = new JPanel(new BorderLayout()); private static final Logging log = getLogging(SdCardReaderPanel.class);
private final JPanel fileList = new JPanel(new VerticalFlowLayout()); private final JPanel fileList = new JPanel(new VerticalFlowLayout());
private final JLabel status = new JLabel(); private final JLabel status = new JLabel();
private final ConnectPanel connectPanel = new ConnectPanel(new ConnectionStateListener() {
public void onConnectionEstablished() {
ConnectPanel.IO_THREAD.execute(() -> requestFileList());
}
public void onConnectionFailed() {
}
});
private final Supplier<ControllerAccess> controllerAccessSupplier; private final Supplier<ControllerAccess> controllerAccessSupplier;
private final Supplier<IoStream> ioStreamSupplier;
private final Container parent;
public SdCardReader(Supplier<ControllerAccess> controllerAccessSupplier) { private final JPanel content = new JPanel(new BorderLayout());
public SdCardReaderPanel(Supplier<ControllerAccess> controllerAccessSupplier,
Supplier<IoStream> ioStreamSupplier, Container parent) {
this.controllerAccessSupplier = controllerAccessSupplier; this.controllerAccessSupplier = controllerAccessSupplier;
this.ioStreamSupplier = ioStreamSupplier;
this.parent = parent;
JButton refresh = new JButton("Refresh"); JButton refresh = new JButton("Refresh");
refresh.addActionListener(e -> ConnectPanel.IO_THREAD.execute(this::requestFileList)); refresh.addActionListener(e -> ConnectPanel.IO_THREAD.execute(this::requestFileList));
JPanel topPanel = new JPanel(new BorderLayout());
JPanel lowPanel = new JPanel(new FlowLayout());
JButton open = new JButton("Open Destination Folder"); JButton open = new JButton("Open Destination Folder");
JPanel lowPanel = new JPanel(new FlowLayout());
lowPanel.add(refresh); lowPanel.add(refresh);
lowPanel.add(open); lowPanel.add(open);
JPanel topPanel = new JPanel(new BorderLayout());
topPanel.add(status, BorderLayout.CENTER);
topPanel.add(lowPanel, BorderLayout.SOUTH);
content.add(topPanel, BorderLayout.NORTH);
content.add(fileList, BorderLayout.CENTER);
open.addActionListener(new AbstractAction() { open.addActionListener(new AbstractAction() {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
try { try {
String folder = getDestinationFolder(controllerAccessSupplier); String folder = getDestinationFolder();
Runtime.getRuntime().exec("explorer.exe /select," + folder + File.separator); Runtime.getRuntime().exec("explorer.exe /select," + folder + File.separator);
} catch (IOException ex) { } catch (IOException ex) {
log.error("Error", ex); log.error("Error", ex);
} }
} }
}); });
}
topPanel.add(connectPanel.getContent(), BorderLayout.NORTH); public JPanel getContent() {
topPanel.add(status, BorderLayout.CENTER); return content;
topPanel.add(lowPanel, BorderLayout.SOUTH);
content.add(topPanel, BorderLayout.NORTH);
content.add(fileList, BorderLayout.CENTER);
content.add(new JLabel("<html>This tab allows direct access to SD card<br/>Please be sure to disconnect Tuner Studio from ECU while downloading files using this tab"), BorderLayout.SOUTH);
} }
@NotNull @NotNull
private String getDestinationFolder(Supplier<ControllerAccess> controllerAccessSupplier) { private byte[] getDirContent() throws IOException {
byte[] packet;
byte[] response;
IoStream stream = ioStreamSupplier.get();
packet = new byte[3];
packet[0] = Fields.TS_SD_R_COMMAND;
packet[2] = Fields.TS_SD_PROTOCOL_RTC;
stream.sendPacket(packet);
response = stream.getDataBuffer().getPacket("RTC status");
log.info("RTC response " + IoStream.printHexBinary(response));
if (response == null)
throw new IOException("RTC No packet");
packet = new byte[17];
packet[0] = Fields.TS_SD_W_COMMAND;
packet[2] = TS_SD_PROTOCOL_FETCH_INFO;
packet[6] = Fields.TS_SD_PROTOCOL_READ_DIR;
stream.sendPacket(packet);
response = stream.getDataBuffer().getPacket("read dir command");
if (response == null)
throw new IOException("Read Dir No packet");
log.info("read dir command " + IoStream.printHexBinary(response));
packet = new byte[8];
packet[0] = Fields.TS_SD_R_COMMAND;
packet[1] = 0;
packet[2] = TS_SD_PROTOCOL_FETCH_INFO;
packet[5] = 0x02;
packet[6] = 0x02;
stream.sendPacket(packet);
response = stream.getDataBuffer().getPacket("read command", true);
if (response == null)
throw new IOException("No packet");
log.info("read command " + IoStream.printHexBinary(response));
return response;
}
@NotNull
public String getDestinationFolder() {
return LogUploadSelector.getLogsFolderDir(controllerAccessSupplier.get().getEcuConfigurationNames()[0]); return LogUploadSelector.getLogsFolderDir(controllerAccessSupplier.get().getEcuConfigurationNames()[0]);
} }
public Component getContent() { private void downloadFile(String fileName) {
return new JScrollPane(content, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); String lastFour = ConnectPanel.getLastFour(fileName);
byte[] packet = new byte[17];
packet[0] = Fields.TS_SD_W_COMMAND;
packet[2] = TS_SD_PROTOCOL_FETCH_INFO;
packet[6] = Fields.TS_SD_PROTOCOL_FETCH_COMPRESSED;
applyLastFour(lastFour, packet);
IoStream stream = ioStreamSupplier.get();
FileOutputStream fos = null;
try {
stream.sendPacket(packet);
byte[] response = stream.getDataBuffer().getPacket("Download file");
log.info("Download file " + IoStream.printHexBinary(response));
setStatus("Downloading " + fileName);
fos = new FileOutputStream(getDestinationFolder() + File.separator + fileName, false);
int chunk = 0;
int totalSize = 0;
long start = System.currentTimeMillis();
while (true) {
packet = new byte[17];
packet[0] = Fields.TS_SD_R_COMMAND;
packet[2] = Fields.TS_SD_PROTOCOL_FETCH_DATA;
packet[3] = (byte) chunk;
packet[4] = (byte) (chunk >> 8);
stream.sendPacket(packet);
response = stream.getDataBuffer().getPacket("Get file", true);
if (response == null) {
log.info("No content response");
break;
}
int dataBytes = response.length - TRANSFER_HEADER_SIZE;
totalSize += dataBytes;
if (chunk % 10 == 0)
log.info("Got content package size " + response.length + "/total=" + totalSize);
fos.write(response, TRANSFER_HEADER_SIZE, dataBytes);
if (dataBytes != 2048) {
log.info(response.length + " must be the last packet");
long duration = System.currentTimeMillis() - start;
setStatus(fileName + " downloaded " + humanReadableByteCountBin(totalSize) + " in " + duration + " ms");
break;
}
if (chunk % 10 == 0)
setStatus(humanReadableByteCountBin(totalSize) + " so far");
chunk++;
}
} catch (IOException e) {
throw new IllegalStateException(e);
} finally {
close(fos);
}
}
private void applyLastFour(String lastFour, byte[] packet) {
for (int i = 0; i < 4; i++)
packet[7 + i] = (byte) lastFour.charAt(i);
}
private static String humanReadableByteCountBin(long bytes) {
long absB = bytes == Long.MIN_VALUE ? Long.MAX_VALUE : Math.abs(bytes);
if (absB < 1024) {
return bytes + " B";
}
long value = absB;
CharacterIterator ci = new StringCharacterIterator("KMGTPE");
for (int i = 40; i >= 0 && absB > 0xfffccccccccccccL >> i; i -= 10) {
value >>= 10;
ci.next();
}
value *= Long.signum(bytes);
return String.format("%.1f %ciB", value / 1024.0, ci.current());
}
private void deleteFile(String fileName) {
String lastFour = ConnectPanel.getLastFour(fileName);
byte[] packet = new byte[17];
packet[0] = Fields.TS_SD_W_COMMAND;
packet[2] = TS_SD_PROTOCOL_FETCH_INFO;
packet[6] = Fields.TS_SD_PROTOCOL_REMOVE_FILE;
applyLastFour(lastFour, packet);
IoStream stream = ioStreamSupplier.get();
try {
stream.sendPacket(packet);
byte[] response = stream.getDataBuffer().getPacket("delete file");
log.info("Delete file " + IoStream.printHexBinary(response));
} catch (IOException e) {
throw new IllegalStateException(e);
}
} }
private void requestFileList() { private void requestFileList() {
@ -135,7 +267,7 @@ public class SdCardReader {
log.info("Filename " + fileName + " size " + fileSize); log.info("Filename " + fileName + " size " + fileSize);
AutoupdateUtil.trueLayout(content.getParent()); AutoupdateUtil.trueLayout(parent);
} }
} catch (IOException ioException) { } catch (IOException ioException) {
@ -147,148 +279,11 @@ public class SdCardReader {
} }
} }
@NotNull public void setStatus(String message) {
private byte[] getDirContent() throws IOException {
byte[] packet;
byte[] response;
IoStream stream = connectPanel.getControllerConnector().getConnector().getBinaryProtocol().getStream();
packet = new byte[3];
packet[0] = Fields.TS_SD_R_COMMAND;
packet[2] = Fields.TS_SD_PROTOCOL_RTC;
stream.sendPacket(packet);
response = stream.getDataBuffer().getPacket("RTC status");
log.info("RTC response " + IoStream.printHexBinary(response));
if (response == null)
throw new IOException("RTC No packet");
packet = new byte[17];
packet[0] = Fields.TS_SD_W_COMMAND;
packet[2] = TS_SD_PROTOCOL_FETCH_INFO;
packet[6] = Fields.TS_SD_PROTOCOL_READ_DIR;
stream.sendPacket(packet);
response = stream.getDataBuffer().getPacket("read dir command");
if (response == null)
throw new IOException("Read Dir No packet");
log.info("read dir command " + IoStream.printHexBinary(response));
packet = new byte[8];
packet[0] = Fields.TS_SD_R_COMMAND;
packet[1] = 0;
packet[2] = TS_SD_PROTOCOL_FETCH_INFO;
packet[5] = 0x02;
packet[6] = 0x02;
stream.sendPacket(packet);
response = stream.getDataBuffer().getPacket("read command", true);
if (response == null)
throw new IOException("No packet");
log.info("read command " + IoStream.printHexBinary(response));
return response;
}
private void downloadFile(String fileName) {
String lastFour = ConnectPanel.getLastFour(fileName);
byte[] packet = new byte[17];
packet[0] = Fields.TS_SD_W_COMMAND;
packet[2] = TS_SD_PROTOCOL_FETCH_INFO;
packet[6] = Fields.TS_SD_PROTOCOL_FETCH_COMPRESSED;
applyLastFour(lastFour, packet);
IoStream stream = connectPanel.getControllerConnector().getConnector().getBinaryProtocol().getStream();
FileOutputStream fos = null;
try {
stream.sendPacket(packet);
byte[] response = stream.getDataBuffer().getPacket("Download file");
log.info("Download file " + IoStream.printHexBinary(response));
setStatus("Downloading " + fileName);
fos = new FileOutputStream(getDestinationFolder(controllerAccessSupplier) + File.separator + fileName, false);
int chunk = 0;
int totalSize = 0;
long start = System.currentTimeMillis();
while (true) {
packet = new byte[17];
packet[0] = Fields.TS_SD_R_COMMAND;
packet[2] = Fields.TS_SD_PROTOCOL_FETCH_DATA;
packet[3] = (byte) chunk;
packet[4] = (byte) (chunk >> 8);
stream.sendPacket(packet);
response = stream.getDataBuffer().getPacket("Get file", true);
if (response == null) {
log.info("No content response");
break;
}
int dataBytes = response.length - TRANSFER_HEADER_SIZE;
totalSize += dataBytes;
if (chunk % 10 == 0)
log.info("Got content package size " + response.length + "/total=" + totalSize);
fos.write(response, TRANSFER_HEADER_SIZE, dataBytes);
if (dataBytes != 2048) {
log.info(response.length + " must be the last packet");
long duration = System.currentTimeMillis() - start;
setStatus(fileName + " downloaded " + humanReadableByteCountBin(totalSize) + " in " + duration + " ms");
break;
}
if (chunk % 10 == 0)
setStatus(humanReadableByteCountBin(totalSize) + " so far");
chunk++;
}
} catch (IOException e) {
throw new IllegalStateException(e);
} finally {
close(fos);
}
}
private void deleteFile(String fileName) {
String lastFour = ConnectPanel.getLastFour(fileName);
byte[] packet = new byte[17];
packet[0] = Fields.TS_SD_W_COMMAND;
packet[2] = TS_SD_PROTOCOL_FETCH_INFO;
packet[6] = Fields.TS_SD_PROTOCOL_REMOVE_FILE;
applyLastFour(lastFour, packet);
IoStream stream = connectPanel.getControllerConnector().getConnector().getBinaryProtocol().getStream();
try {
stream.sendPacket(packet);
byte[] response = stream.getDataBuffer().getPacket("delete file");
log.info("Delete file " + IoStream.printHexBinary(response));
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
private void applyLastFour(String lastFour, byte[] packet) {
for (int i = 0; i < 4; i++)
packet[7 + i] = (byte) lastFour.charAt(i);
}
private void setStatus(String message) {
SwingUtilities.invokeLater(() -> status.setText(message)); SwingUtilities.invokeLater(() -> status.setText(message));
} }
private static String humanReadableByteCountBin(long bytes) { public void onConnectionEstablished() {
long absB = bytes == Long.MIN_VALUE ? Long.MAX_VALUE : Math.abs(bytes); ConnectPanel.IO_THREAD.execute(this::requestFileList);
if (absB < 1024) {
return bytes + " B";
}
long value = absB;
CharacterIterator ci = new StringCharacterIterator("KMGTPE");
for (int i = 40; i >= 0 && absB > 0xfffccccccccccccL >> i; i -= 10) {
value >>= 10;
ci.next();
}
value *= Long.signum(bytes);
return String.format("%.1f %ciB", value / 1024.0, ci.current());
} }
} }

View File

@ -7,6 +7,9 @@ import com.rusefi.ui.util.FrameHelper;
*/ */
public class RemoteTabSandbox { public class RemoteTabSandbox {
public static void main(String[] args) { public static void main(String[] args) {
new FrameHelper().showFrame(new RemoteTab().getContent()); RemoteTab.Listener listener = () -> {
};
new FrameHelper().showFrame(new RemoteTab(listener).getContent());
} }
} }