rusefi/java_console/io/src/main/java/com/rusefi/binaryprotocol/BinaryProtocol.java

614 lines
22 KiB
Java
Raw Normal View History

2015-07-10 06:01:56 -07:00
package com.rusefi.binaryprotocol;
2020-07-24 09:44:29 -07:00
import com.devexperts.logging.Logging;
2017-03-01 12:21:03 -08:00
import com.opensr5.ConfigurationImage;
import com.opensr5.io.ConfigurationImageFile;
2017-03-01 14:57:03 -08:00
import com.opensr5.io.DataListener;
2017-11-19 11:26:21 -08:00
import com.rusefi.ConfigurationImageDiff;
import com.rusefi.NamedThreadFactory;
import com.rusefi.core.SignatureHelper;
2017-11-19 11:26:21 -08:00
import com.rusefi.Timeouts;
import com.rusefi.binaryprotocol.test.Bug3923;
import com.rusefi.config.generated.Fields;
2020-07-25 07:32:23 -07:00
import com.rusefi.core.Pair;
import com.rusefi.core.SensorCentral;
2016-02-29 19:02:59 -08:00
import com.rusefi.io.*;
2020-07-11 13:04:27 -07:00
import com.rusefi.io.commands.GetOutputsCommand;
2020-10-03 10:51:51 -07:00
import com.rusefi.io.commands.HelloCommand;
import com.rusefi.core.FileUtil;
2020-05-21 19:30:47 -07:00
import com.rusefi.tune.xml.Msq;
import com.rusefi.ui.livedocs.LiveDocsRegistry;
import org.jetbrains.annotations.Nullable;
2015-07-10 06:01:56 -07:00
import java.io.IOException;
2015-08-23 18:03:42 -07:00
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
2015-07-10 06:01:56 -07:00
import java.util.Arrays;
import java.util.concurrent.*;
2015-07-10 06:01:56 -07:00
2020-07-24 09:44:29 -07:00
import static com.devexperts.logging.Logging.getLogging;
2015-07-10 06:01:56 -07:00
import static com.rusefi.binaryprotocol.IoHelper.*;
2020-09-30 21:24:49 -07:00
import static com.rusefi.config.generated.Fields.*;
2015-07-10 06:01:56 -07:00
/**
2020-05-15 21:20:54 -07:00
* This object represents logical state of physical connection.
* <p>
2020-05-15 21:20:54 -07:00
* Instance is connected until we experience issues. Once we decide to close the connection there is no restart -
* new instance of this class would need to be created once we establish a new physical connection.
* <p>
2020-06-09 17:08:16 -07:00
* Andrey Belomutskiy, (c) 2013-2020
2015-07-10 06:01:56 -07:00
* 3/6/2015
*/
2020-10-04 15:55:23 -07:00
public class BinaryProtocol {
2020-07-24 09:44:29 -07:00
private static final Logging log = getLogging(BinaryProtocol.class);
private static final ThreadFactory THREAD_FACTORY = new NamedThreadFactory("text pull");
2017-03-03 18:23:40 -08:00
2017-06-20 21:16:19 -07:00
private static final String USE_PLAIN_PROTOCOL_PROPERTY = "protocol.plain";
private static final String CONFIGURATION_RUSEFI_BINARY = "current_configuration.rusefi_binary";
2020-05-21 19:30:47 -07:00
private static final String CONFIGURATION_RUSEFI_XML = "current_configuration.msq";
2017-03-03 18:23:40 -08:00
/**
* This properly allows to switch to non-CRC32 mode
* todo: finish this feature, assuming we even need it.
*/
2019-08-14 20:12:00 -07:00
public static boolean PLAIN_PROTOCOL = Boolean.getBoolean(USE_PLAIN_PROTOCOL_PROPERTY);
2017-03-03 18:23:40 -08:00
2020-06-25 17:51:09 -07:00
private final LinkManager linkManager;
2015-07-10 06:01:56 -07:00
private final IoStream stream;
private final IncomingDataBuffer incomingData;
private boolean isBurnPending;
public String signature;
2022-04-17 15:53:50 -07:00
public boolean isGoodOutputChannels;
2015-07-10 06:01:56 -07:00
2021-12-04 17:01:41 -08:00
private final BinaryProtocolState state = new BinaryProtocolState();
2020-06-25 20:44:49 -07:00
2016-02-07 09:02:02 -08:00
// todo: this ioLock needs better documentation!
2015-08-30 15:01:44 -07:00
private final Object ioLock = new Object();
2015-07-10 06:01:56 -07:00
2022-02-11 05:50:29 -08:00
BinaryProtocolLogger binaryProtocolLogger;
public static boolean DISABLE_LOCAL_CACHE;
2020-07-16 21:27:33 -07:00
public static String findCommand(byte command) {
switch (command) {
2020-07-31 17:24:01 -07:00
case Fields.TS_PAGE_COMMAND:
return "PAGE";
2020-07-31 07:26:14 -07:00
case Fields.TS_COMMAND_F:
return "PROTOCOL";
2020-07-16 21:27:33 -07:00
case Fields.TS_CRC_CHECK_COMMAND:
return "CRC_CHECK";
case Fields.TS_BURN_COMMAND:
return "BURN";
case Fields.TS_HELLO_COMMAND:
return "HELLO";
case Fields.TS_READ_COMMAND:
return "READ";
2020-07-26 15:10:51 -07:00
case Fields.TS_GET_TEXT:
return "TS_GET_TEXT";
2020-07-16 21:27:33 -07:00
case Fields.TS_GET_FIRMWARE_VERSION:
return "GET_FW_VERSION";
case Fields.TS_CHUNK_WRITE_COMMAND:
return "WRITE_CHUNK";
2020-07-18 21:58:53 -07:00
case Fields.TS_OUTPUT_COMMAND:
return "TS_OUTPUT_COMMAND";
case Fields.TS_RESPONSE_OK:
return "TS_RESPONSE_OK";
2020-07-16 21:27:33 -07:00
default:
return "command " + (char) command + "/" + command;
2020-07-16 21:27:33 -07:00
}
}
2020-07-18 08:57:36 -07:00
public IoStream getStream() {
return stream;
}
2015-07-10 06:01:56 -07:00
public boolean isClosed;
2020-06-25 20:44:49 -07:00
public CommunicationLoggingListener communicationLoggingListener;
2020-07-02 19:22:50 -07:00
2021-12-06 09:43:53 -08:00
public BinaryProtocol(LinkManager linkManager, IoStream stream) {
2020-06-25 17:51:09 -07:00
this.linkManager = linkManager;
2015-07-10 06:01:56 -07:00
this.stream = stream;
communicationLoggingListener = linkManager.messageListener::postMessage;
2020-07-02 19:22:50 -07:00
2021-12-06 09:43:53 -08:00
incomingData = stream.getDataBuffer();
2022-02-11 05:50:29 -08:00
binaryProtocolLogger = new BinaryProtocolLogger(linkManager);
binaryProtocolLogger.needCompositeLogger = linkManager.getCompositeLogicEnabled();
2020-05-31 09:51:53 -07:00
}
public static void sleep(long millis) {
2015-07-10 06:01:56 -07:00
try {
2019-09-01 16:37:58 -07:00
Thread.sleep(millis);
2015-07-10 06:01:56 -07:00
} catch (InterruptedException e) {
throw new IllegalStateException(e);
}
}
public void doSend(final String command, boolean fireEvent) throws InterruptedException {
2020-07-24 09:44:29 -07:00
log.info("Sending [" + command + "]");
2016-04-02 21:02:46 -07:00
if (fireEvent && LinkManager.LOG_LEVEL.isDebugEnabled()) {
2020-07-02 19:22:50 -07:00
communicationLoggingListener.onPortHolderMessage(BinaryProtocol.class, "Sending [" + command + "]");
2015-07-10 06:01:56 -07:00
}
2020-06-25 17:51:09 -07:00
Future f = linkManager.submit(new Runnable() {
2015-07-10 06:01:56 -07:00
@Override
public void run() {
sendTextCommand(command);
}
@Override
public String toString() {
return "Runnable for " + command;
}
});
try {
f.get(Timeouts.COMMAND_TIMEOUT_SEC, TimeUnit.SECONDS);
} catch (ExecutionException e) {
throw new IllegalStateException(e);
} catch (TimeoutException e) {
2020-07-24 09:44:29 -07:00
log.error("timeout sending [" + command + "] giving up: " + e);
2015-07-10 06:01:56 -07:00
return;
}
/**
* this here to make CommandQueue happy
*/
2020-06-25 19:27:39 -07:00
linkManager.getCommandQueue().handleConfirmationMessage(CommandQueue.CONFIRMATION_PREFIX + command);
2015-07-10 06:01:56 -07:00
}
2021-12-04 18:06:47 -08:00
public static String getSignature(IoStream stream) throws IOException {
HelloCommand.send(stream);
2021-12-04 18:06:47 -08:00
return HelloCommand.getHelloResponse(stream.getDataBuffer());
}
2015-07-10 06:01:56 -07:00
/**
* this method reads configuration snapshot from controller
2015-08-23 18:03:42 -07:00
*
2015-07-10 06:01:56 -07:00
* @return true if everything fine
*/
public String connectAndReadConfiguration(Arguments arguments, DataListener listener) {
2020-10-03 10:51:51 -07:00
try {
2021-12-04 18:06:47 -08:00
signature = getSignature(stream);
2021-12-06 09:24:48 -08:00
log.info("Got " + signature + " signature");
SignatureHelper.downloadIfNotAvailable(SignatureHelper.getUrl(signature));
2020-10-03 10:51:51 -07:00
} catch (IOException e) {
return "Failed to read signature " + e;
2020-10-03 10:51:51 -07:00
}
String errorMessage = validateConfigVersion();
if (errorMessage != null)
return errorMessage;
2021-12-06 15:55:33 -08:00
readImage(arguments, Fields.TOTAL_CONFIG_SIZE);
2015-07-10 06:01:56 -07:00
if (isClosed)
return "Failed to read calibration";
2015-07-10 06:01:56 -07:00
2022-02-11 05:50:29 -08:00
startPullThread(listener);
binaryProtocolLogger.start();
return null;
}
/**
* @return null if everything is good, error message otherwise
*/
private String validateConfigVersion() {
int requestSize = 4;
byte[] packet = GetOutputsCommand.createRequest(TS_FILE_VERSION_OFFSET, requestSize);
String msg = "load TS_CONFIG_VERSION";
byte[] response = executeCommand(Fields.TS_OUTPUT_COMMAND, packet, msg);
if (!checkResponseCode(response, (byte) Fields.TS_RESPONSE_OK) || response.length != requestSize + 1) {
close();
return "Failed to " + msg;
}
int actualVersion = FileUtil.littleEndianWrap(response, 1, requestSize).getInt();
if (actualVersion != TS_FILE_VERSION) {
log.error("Got TS_CONFIG_VERSION " + actualVersion);
return "Incompatible firmware format=" + actualVersion + " while format " + TS_FILE_VERSION + " expected";
}
return null;
2015-07-10 06:01:56 -07:00
}
2022-02-11 05:50:29 -08:00
private void startPullThread(final DataListener textListener) {
2020-06-25 17:51:09 -07:00
if (!linkManager.COMMUNICATION_QUEUE.isEmpty()) {
log.info("Current queue size: " + linkManager.COMMUNICATION_QUEUE.size());
2015-07-10 06:01:56 -07:00
}
Runnable textPull = new Runnable() {
@Override
public void run() {
while (!isClosed) {
// FileLog.rlog("queue: " + LinkManager.COMMUNICATION_QUEUE.toString());
if (linkManager.COMMUNICATION_QUEUE.isEmpty() && linkManager.getNeedPullData()) {
2020-06-25 17:51:09 -07:00
linkManager.submit(new Runnable() {
2015-07-10 06:01:56 -07:00
@Override
public void run() {
2022-04-17 15:53:50 -07:00
isGoodOutputChannels = requestOutputChannels();
if (isGoodOutputChannels)
2020-06-07 10:22:34 -07:00
HeartBeatListeners.onDataArrived();
2022-02-11 05:50:29 -08:00
binaryProtocolLogger.compositeLogic(BinaryProtocol.this);
if (linkManager.isNeedPullText()) {
String text = requestPendingMessages();
if (text != null)
textListener.onDataArrived((text + "\r\n").getBytes());
}
if (linkManager.isNeedPullLiveData()) {
LiveDocsRegistry.LiveDataProvider liveDataProvider = LiveDocsRegistry.getLiveDataProvider();
LiveDocsRegistry.INSTANCE.refresh(liveDataProvider);
}
2015-07-10 06:01:56 -07:00
}
});
}
2020-05-25 16:30:02 -07:00
sleep(Timeouts.TEXT_PULL_PERIOD);
2015-07-10 06:01:56 -07:00
}
2022-06-29 10:15:15 -07:00
log.info("Port shutdown: Stopping text pull");
2015-07-10 06:01:56 -07:00
}
};
Thread tr = THREAD_FACTORY.newThread(textPull);
2015-07-10 06:01:56 -07:00
tr.start();
}
private void dropPending() {
2015-08-30 15:01:44 -07:00
synchronized (ioLock) {
2015-07-10 06:01:56 -07:00
if (isClosed)
return;
incomingData.dropPending();
}
}
2020-07-29 16:42:48 -07:00
public void uploadChanges(ConfigurationImage newVersion) {
2020-05-16 10:23:16 -07:00
ConfigurationImage current = getControllerConfiguration();
2015-07-10 06:01:56 -07:00
// let's have our own copy which no one would be able to change
newVersion = newVersion.clone();
int offset = 0;
while (offset < current.getSize()) {
Pair<Integer, Integer> range = ConfigurationImageDiff.findDifferences(current, newVersion, offset);
if (range == null)
break;
int size = range.second - range.first;
2020-07-29 16:42:48 -07:00
log.info("Need to patch: " + range + ", size=" + size);
2015-07-10 06:01:56 -07:00
byte[] oldBytes = current.getRange(range.first, size);
2020-07-29 16:42:48 -07:00
log.info("old " + Arrays.toString(oldBytes));
2015-07-10 06:01:56 -07:00
byte[] newBytes = newVersion.getRange(range.first, size);
2020-07-29 16:42:48 -07:00
log.info("new " + Arrays.toString(newBytes));
2015-07-10 06:01:56 -07:00
writeData(newVersion.getContent(), 0, range.first, size);
2015-07-10 06:01:56 -07:00
offset = range.second;
}
2020-07-29 16:42:48 -07:00
burn();
2015-07-10 06:01:56 -07:00
setController(newVersion);
}
private byte[] receivePacket(String msg) throws IOException {
2015-07-10 06:01:56 -07:00
long start = System.currentTimeMillis();
2015-08-30 15:01:44 -07:00
synchronized (ioLock) {
return incomingData.getPacket(msg, start);
2015-07-10 06:01:56 -07:00
}
}
2020-05-16 09:20:30 -07:00
/**
* read complete tune from physical data stream
*/
2021-12-06 15:55:33 -08:00
public void readImage(Arguments arguments, int size) {
ConfigurationImage image = getAndValidateLocallyCached();
if (image == null) {
2021-12-06 15:55:33 -08:00
image = readFullImageFromController(arguments, size);
if (image == null)
return;
}
setController(image);
2020-07-24 09:44:29 -07:00
log.info("Got configuration from controller.");
ConnectionStatusLogic.INSTANCE.setValue(ConnectionStatusValue.CONNECTED);
}
2021-12-06 14:27:38 -08:00
public static class Arguments {
final boolean saveFile;
public Arguments(boolean saveFile) {
this.saveFile = saveFile;
}
}
@Nullable
2021-12-06 15:55:33 -08:00
private ConfigurationImage readFullImageFromController(Arguments arguments, int size) {
ConfigurationImage image;
image = new ConfigurationImage(size);
2015-07-10 06:01:56 -07:00
int offset = 0;
long start = System.currentTimeMillis();
2020-07-24 09:44:29 -07:00
log.info("Reading from controller...");
2015-07-10 06:01:56 -07:00
while (offset < image.getSize() && (System.currentTimeMillis() - start < Timeouts.READ_IMAGE_TIMEOUT)) {
if (isClosed)
return null;
2015-07-10 06:01:56 -07:00
int remainingSize = image.getSize() - offset;
2020-07-31 07:26:14 -07:00
int requestSize = Math.min(remainingSize, Fields.BLOCKING_FACTOR);
2015-07-10 06:01:56 -07:00
byte[] packet = new byte[4];
putShort(packet, 0, swap16(offset));
putShort(packet, 2, swap16(requestSize));
2015-07-10 06:01:56 -07:00
byte[] response = executeCommand(Fields.TS_READ_COMMAND, packet, "load image offset=" + offset);
2015-07-10 06:01:56 -07:00
2020-10-04 15:55:23 -07:00
if (!checkResponseCode(response, (byte) Fields.TS_RESPONSE_OK) || response.length != requestSize + 1) {
2021-12-06 09:24:48 -08:00
String code = (response == null || response.length == 0) ? "empty" : "ERROR_CODE=" + getCode(response);
String info = response == null ? "NO RESPONSE" : (code + " length=" + response.length);
2020-07-24 09:44:29 -07:00
log.info("readImage: ERROR UNEXPECTED Something is wrong, retrying... " + info);
// todo: looks like forever retry? that's weird
2015-07-10 06:01:56 -07:00
continue;
}
2020-06-07 10:22:34 -07:00
HeartBeatListeners.onDataArrived();
ConnectionStatusLogic.INSTANCE.markConnected();
2015-07-10 06:01:56 -07:00
System.arraycopy(response, 1, image.getContent(), offset, requestSize);
offset += requestSize;
}
2021-12-23 20:36:19 -08:00
if (arguments != null && arguments.saveFile) {
2021-12-06 15:55:33 -08:00
try {
ConfigurationImageFile.saveToFile(image, CONFIGURATION_RUSEFI_BINARY);
Msq tune = MsqFactory.valueOf(image);
tune.writeXmlFile(CONFIGURATION_RUSEFI_XML);
} catch (Exception e) {
System.err.println("Ignoring " + e);
}
}
return image;
}
2020-09-30 21:24:49 -07:00
private static String getCode(byte[] response) {
2020-09-30 21:51:12 -07:00
int b = response[0] & 0xff;
2020-09-30 21:24:49 -07:00
switch (b) {
case TS_RESPONSE_CRC_FAILURE:
return "CRC_FAILURE";
case TS_RESPONSE_UNRECOGNIZED_COMMAND:
return "UNRECOGNIZED_COMMAND";
case TS_RESPONSE_OUT_OF_RANGE:
return "OUT_OF_RANGE";
case TS_RESPONSE_FRAMING_ERROR:
return "FRAMING_ERROR";
2021-12-06 09:24:48 -08:00
case TS_RESPONSE_UNDERRUN:
return "TS_RESPONSE_UNDERRUN";
2020-09-30 21:24:49 -07:00
}
return Integer.toString(b);
}
private ConfigurationImage getAndValidateLocallyCached() {
if (DISABLE_LOCAL_CACHE)
return null;
ConfigurationImage localCached;
try {
localCached = ConfigurationImageFile.readFromFile(CONFIGURATION_RUSEFI_BINARY);
} catch (IOException e) {
System.err.println("Error reading " + CONFIGURATION_RUSEFI_BINARY + ": no worries " + e);
return null;
}
if (localCached != null) {
int crcOfLocallyCachedConfiguration = IoHelper.getCrc32(localCached.getContent());
log.info(String.format(CONFIGURATION_RUSEFI_BINARY + " Local cache CRC %x\n", crcOfLocallyCachedConfiguration));
2021-12-06 09:43:53 -08:00
int crcFromController = getCrcFromController(localCached.getSize());
if (crcOfLocallyCachedConfiguration == crcFromController) {
return localCached;
}
2021-12-06 09:43:53 -08:00
}
return null;
2015-07-10 06:01:56 -07:00
}
2021-12-06 09:43:53 -08:00
public int getCrcFromController(int configSize) {
byte[] packet = createCrcCommand(configSize);
byte[] response = executeCommand(Fields.TS_CRC_CHECK_COMMAND, packet, "get CRC32");
2021-12-06 09:43:53 -08:00
if (checkResponseCode(response, (byte) Fields.TS_RESPONSE_OK) && response.length == 5) {
ByteBuffer bb = ByteBuffer.wrap(response, 1, 4);
// that's unusual - most of the protocol is LITTLE_ENDIAN
bb.order(ByteOrder.BIG_ENDIAN);
int crcFromController = bb.getInt();
log.info(String.format("rusEFI says tune CRC32 0x%x %d\n", crcFromController, crcFromController));
short crc16FromController = (short) crcFromController;
log.info(String.format("rusEFI says tune CRC16 0x%x %d\n", crc16FromController, crc16FromController));
return crcFromController;
} else {
return -1;
}
}
2021-12-04 21:31:50 -08:00
public static byte[] createCrcCommand(int size) {
byte[] packet = new byte[4];
putShort(packet, 0, swap16(/*offset = */ 0));
putShort(packet, 2, swap16(size));
2021-12-04 21:31:50 -08:00
return packet;
}
public byte[] executeCommand(char opcode, String msg) {
return executeCommand(opcode, null, msg);
}
2015-07-10 06:01:56 -07:00
/**
* Blocking sending binary packet and waiting for a response
*
* @return null in case of IO issues
*/
public byte[] executeCommand(char opcode, byte[] packet, String msg) {
2015-07-10 06:01:56 -07:00
if (isClosed)
return null;
byte[] fullRequest;
if (packet != null) {
fullRequest = new byte[packet.length + 1];
System.arraycopy(packet, 0, fullRequest, 1, packet.length);
} else {
fullRequest = new byte[1];
}
fullRequest[0] = (byte)opcode;
2015-07-10 06:01:56 -07:00
try {
linkManager.assertCommunicationThread();
2015-07-10 06:01:56 -07:00
dropPending();
if (Bug3923.obscene)
log.info("Sending opcode " + opcode + " payload " + packet.length);
sendPacket(fullRequest);
return receivePacket(msg);
2020-07-11 19:30:56 -07:00
} catch (IOException e) {
2020-07-24 09:44:29 -07:00
log.error(msg + ": executeCommand failed: " + e);
2015-07-10 06:01:56 -07:00
close();
return null;
}
}
public void close() {
if (isClosed)
return;
isClosed = true;
2022-02-11 05:50:29 -08:00
binaryProtocolLogger.close();
2015-07-10 06:01:56 -07:00
stream.close();
}
public void writeData(byte[] content, int contentOffset, int ecuOffset, int size) {
2015-07-10 06:01:56 -07:00
isBurnPending = true;
byte[] packet = new byte[4 + size];
putShort(packet, 0, swap16(ecuOffset));
putShort(packet, 2, swap16(size));
2015-07-10 06:01:56 -07:00
System.arraycopy(content, contentOffset, packet, 4, size);
2015-07-10 06:01:56 -07:00
long start = System.currentTimeMillis();
while (!isClosed && (System.currentTimeMillis() - start < Timeouts.BINARY_IO_TIMEOUT)) {
byte[] response = executeCommand(Fields.TS_CHUNK_WRITE_COMMAND, packet, "writeImage");
2020-10-04 15:55:23 -07:00
if (!checkResponseCode(response, (byte) Fields.TS_RESPONSE_OK) || response.length != 1) {
2020-07-29 16:42:48 -07:00
log.error("writeData: Something is wrong, retrying...");
2015-07-10 06:01:56 -07:00
continue;
}
break;
}
}
2020-07-29 16:42:48 -07:00
public void burn() {
2015-07-10 06:01:56 -07:00
if (!isBurnPending)
return;
2020-07-29 16:42:48 -07:00
log.info("Need to burn");
2015-07-10 06:01:56 -07:00
while (true) {
if (isClosed)
return;
byte[] response = executeCommand(Fields.TS_BURN_COMMAND, "burn");
2020-10-04 15:55:23 -07:00
if (!checkResponseCode(response, (byte) Fields.TS_RESPONSE_BURN_OK) || response.length != 1) {
2015-07-10 06:01:56 -07:00
continue;
}
break;
}
2020-07-29 16:42:48 -07:00
log.info("DONE");
2015-07-10 06:01:56 -07:00
isBurnPending = false;
}
public void setController(ConfigurationImage controller) {
2020-06-25 20:44:49 -07:00
state.setController(controller);
2015-07-10 06:01:56 -07:00
}
2020-05-16 10:23:16 -07:00
/**
* Configuration as it is in the controller to the best of our knowledge
*/
public ConfigurationImage getControllerConfiguration() {
2020-06-25 20:44:49 -07:00
return state.getControllerConfiguration();
2015-07-10 06:01:56 -07:00
}
2017-03-03 18:23:40 -08:00
private void sendPacket(byte[] command) throws IOException {
2020-07-24 09:44:29 -07:00
stream.sendPacket(command);
2016-12-22 05:02:29 -08:00
}
2015-07-10 06:01:56 -07:00
/**
2017-09-18 05:00:10 -07:00
* This method blocks until a confirmation is received or {@link Timeouts#BINARY_IO_TIMEOUT} is reached
2015-07-10 06:01:56 -07:00
*
* @return true in case of timeout, false if got proper confirmation
*/
private boolean sendTextCommand(String text) {
2022-02-12 08:54:34 -08:00
byte[] command = getTextCommandBytesOnlyText(text);
2015-07-10 06:01:56 -07:00
long start = System.currentTimeMillis();
while (!isClosed && (System.currentTimeMillis() - start < Timeouts.BINARY_IO_TIMEOUT)) {
byte[] response = executeCommand(Fields.TS_EXECUTE, command, "execute");
2020-10-04 15:55:23 -07:00
if (!checkResponseCode(response, (byte) Fields.TS_RESPONSE_COMMAND_OK) || response.length != 1) {
2015-07-10 06:01:56 -07:00
continue;
}
return false;
}
return true;
}
2019-08-14 20:58:47 -07:00
public static byte[] getTextCommandBytes(String text) {
byte[] asBytes = text.getBytes();
byte[] command = new byte[asBytes.length + 1];
command[0] = Fields.TS_EXECUTE;
System.arraycopy(asBytes, 0, command, 1, asBytes.length);
return command;
}
2022-02-12 08:54:34 -08:00
public static byte[] getTextCommandBytesOnlyText(String text) {
return text.getBytes();
2019-08-14 20:58:47 -07:00
}
2018-01-24 17:41:35 -08:00
private String requestPendingMessages() {
2015-07-10 06:01:56 -07:00
if (isClosed)
return null;
try {
byte[] response = executeCommand(Fields.TS_GET_TEXT, "text");
2015-07-10 06:01:56 -07:00
if (response != null && response.length == 1)
Thread.sleep(100);
return new String(response, 1, response.length - 1);
} catch (InterruptedException e) {
2020-07-24 09:44:29 -07:00
log.error(e.toString());
2018-01-24 17:41:35 -08:00
return null;
2015-07-10 06:01:56 -07:00
}
}
2015-08-23 18:03:42 -07:00
public boolean requestOutputChannels() {
2015-08-23 18:03:42 -07:00
if (isClosed)
return false;
2017-05-27 06:00:18 -07:00
// TODO: Get rid of the +1. This adds a byte at the front to tack a fake TS response code on the front
// of the reassembled packet.
2022-04-17 15:48:22 -07:00
byte[] reassemblyBuffer = new byte[TS_TOTAL_OUTPUT_SIZE + 1];
reassemblyBuffer[0] = Fields.TS_RESPONSE_OK;
2017-05-27 06:00:18 -07:00
int reassemblyIdx = 0;
2022-04-17 15:48:22 -07:00
int remaining = TS_TOTAL_OUTPUT_SIZE;
2015-08-23 18:03:42 -07:00
while (remaining > 0) {
// If less than one full chunk left, do a smaller read
int chunkSize = Math.min(remaining, Fields.BLOCKING_FACTOR);
2015-12-02 17:10:06 -08:00
byte[] response = executeCommand(
Fields.TS_OUTPUT_COMMAND,
GetOutputsCommand.createRequest(reassemblyIdx, chunkSize),
"output channels"
);
if (response == null || response.length != (chunkSize + 1) || response[0] != Fields.TS_RESPONSE_OK) {
return false;
}
// Copy this chunk in to the reassembly buffer
System.arraycopy(response, 1, reassemblyBuffer, reassemblyIdx + 1, chunkSize);
remaining -= chunkSize;
}
state.setCurrentOutputs(reassemblyBuffer);
SensorCentral.getInstance().grabSensorValues(reassemblyBuffer);
return true;
2020-06-20 21:31:01 -07:00
}
2020-06-25 22:33:44 -07:00
public BinaryProtocolState getBinaryProtocolState() {
return state;
}
2015-07-10 06:01:56 -07:00
}