package yaokong;
|
|
import java.io.ByteArrayOutputStream;
|
import java.io.IOException;
|
import java.io.InputStream;
|
import java.io.OutputStream;
|
import java.nio.ByteBuffer;
|
import java.nio.ByteOrder;
|
import java.util.Objects;
|
|
public abstract class WaitForAck {
|
protected final InputStream inputStream;
|
protected final OutputStream outputStream;
|
|
protected WaitForAck(InputStream inputStream, OutputStream outputStream) {
|
this.inputStream = Objects.requireNonNull(inputStream, "inputStream");
|
this.outputStream = Objects.requireNonNull(outputStream, "outputStream");
|
}
|
|
// 在超时内等待下位机返回指定指令的确认帧
|
protected boolean waitForAck(byte expectedCommand) throws IOException {
|
long deadline = System.currentTimeMillis() + 1000L;
|
while (System.currentTimeMillis() < deadline) {
|
if (inputStream.available() > 0) {
|
byte[] response = readResponse();
|
if (response != null && parseResponse(response, expectedCommand)) {
|
return true;
|
}
|
}
|
try {
|
Thread.sleep(10L);
|
} catch (InterruptedException interruptedException) {
|
Thread.currentThread().interrupt();
|
break;
|
}
|
}
|
return false;
|
}
|
|
// 从输入流读取完整响应帧并校验帧头帧尾
|
private byte[] readResponse() throws IOException {
|
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
while (true) {
|
int value = inputStream.read();
|
if (value == -1) {
|
return buffer.size() == 0 ? null : buffer.toByteArray();
|
}
|
|
buffer.write(value);
|
|
if (buffer.size() == 1 && buffer.toByteArray()[0] != BluetoothProtocol.FRAME_HEADER[0]) {
|
buffer.reset();
|
continue;
|
}
|
|
if (buffer.size() == 2) {
|
byte[] header = buffer.toByteArray();
|
if (header[0] != BluetoothProtocol.FRAME_HEADER[0] || header[1] != BluetoothProtocol.FRAME_HEADER[1]) {
|
buffer.reset();
|
continue;
|
}
|
}
|
|
if ((byte) value == BluetoothProtocol.FRAME_FOOTER) {
|
return buffer.toByteArray();
|
}
|
}
|
}
|
|
// 解析确认帧并校验CRC与指令号
|
private boolean parseResponse(byte[] response, byte expectedCommand) {
|
if (response.length < 7) {
|
return false;
|
}
|
|
if (response[0] != BluetoothProtocol.FRAME_HEADER[0] || response[1] != BluetoothProtocol.FRAME_HEADER[1]) {
|
return false;
|
}
|
|
if (response[2] != BluetoothProtocol.CMD_RESPONSE) {
|
return false;
|
}
|
|
ByteBuffer wrapper = ByteBuffer.wrap(response).order(ByteOrder.LITTLE_ENDIAN);
|
wrapper.position(3);
|
int payloadLength = wrapper.getShort() & 0xFFFF;
|
int frameLength = 2 + 1 + 2 + payloadLength + 2 + 1;
|
|
if (response.length < frameLength) {
|
return false;
|
}
|
|
if (payloadLength < 2) {
|
return false;
|
}
|
|
int crcOffset = 2 + 1 + 2 + payloadLength;
|
int calculatedCrc = CRC16.calculateCRC16(response, 2, 1 + 2 + payloadLength);
|
int receivedCrc = ByteBuffer.wrap(response, crcOffset, 2).order(ByteOrder.LITTLE_ENDIAN).getShort() & 0xFFFF;
|
|
if (calculatedCrc != receivedCrc) {
|
return false;
|
}
|
|
byte ackCommand = response[2 + 1 + 2];
|
byte status = response[2 + 1 + 2 + 1];
|
|
if (ackCommand != expectedCommand) {
|
return false;
|
}
|
|
return status == BluetoothProtocol.ERROR_SUCCESS;
|
}
|
}
|