mirror of
https://github.com/zjs81/meshcore-open.git
synced 2026-07-21 08:11:06 +10:00
Refactor USB handling to improve connection management and error cleanup
This commit is contained in:
committed by
just-stuff-tm
parent
f462815775
commit
98f7c3b088
@@ -21,6 +21,8 @@ import io.flutter.plugin.common.EventChannel
|
|||||||
import io.flutter.plugin.common.MethodCall
|
import io.flutter.plugin.common.MethodCall
|
||||||
import io.flutter.plugin.common.MethodChannel
|
import io.flutter.plugin.common.MethodChannel
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
import java.util.concurrent.ExecutorService
|
||||||
|
import java.util.concurrent.Executors
|
||||||
|
|
||||||
class MainActivity : FlutterActivity() {
|
class MainActivity : FlutterActivity() {
|
||||||
private val usbMethodChannelName = "meshcore_open/android_usb_serial"
|
private val usbMethodChannelName = "meshcore_open/android_usb_serial"
|
||||||
@@ -29,6 +31,7 @@ class MainActivity : FlutterActivity() {
|
|||||||
|
|
||||||
private lateinit var usbManager: UsbManager
|
private lateinit var usbManager: UsbManager
|
||||||
private val mainHandler = Handler(Looper.getMainLooper())
|
private val mainHandler = Handler(Looper.getMainLooper())
|
||||||
|
private val usbIoExecutor: ExecutorService = Executors.newSingleThreadExecutor()
|
||||||
|
|
||||||
private var eventSink: EventChannel.EventSink? = null
|
private var eventSink: EventChannel.EventSink? = null
|
||||||
private var usbConnection: UsbDeviceConnection? = null
|
private var usbConnection: UsbDeviceConnection? = null
|
||||||
@@ -112,6 +115,7 @@ class MainActivity : FlutterActivity() {
|
|||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
closeUsbConnection()
|
closeUsbConnection()
|
||||||
|
usbIoExecutor.shutdownNow()
|
||||||
unregisterReceiver(permissionReceiver)
|
unregisterReceiver(permissionReceiver)
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
}
|
}
|
||||||
@@ -191,11 +195,17 @@ class MainActivity : FlutterActivity() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
usbIoExecutor.execute {
|
||||||
port.write(data, 1000)
|
try {
|
||||||
result.success(null)
|
port.write(data, 1000)
|
||||||
} catch (error: Exception) {
|
mainHandler.post {
|
||||||
result.error("usb_write_failed", error.message, null)
|
result.success(null)
|
||||||
|
}
|
||||||
|
} catch (error: Exception) {
|
||||||
|
mainHandler.post {
|
||||||
|
result.error("usb_write_failed", error.message, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -981,7 +981,6 @@ class MeshCoreConnector extends ChangeNotifier {
|
|||||||
_selfInfoRetryTimer = null;
|
_selfInfoRetryTimer = null;
|
||||||
_hasReceivedDeviceInfo = false;
|
_hasReceivedDeviceInfo = false;
|
||||||
_pendingInitialChannelSync = false;
|
_pendingInitialChannelSync = false;
|
||||||
_hasReceivedDeviceInfo = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool get _shouldAutoReconnect =>
|
bool get _shouldAutoReconnect =>
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ class UsbSerialDecodedPacket {
|
|||||||
|
|
||||||
class UsbSerialFrameDecoder {
|
class UsbSerialFrameDecoder {
|
||||||
final List<int> _rxBuffer = <int>[];
|
final List<int> _rxBuffer = <int>[];
|
||||||
|
int _startIndex = 0;
|
||||||
|
|
||||||
List<UsbSerialDecodedPacket> ingest(Uint8List bytes) {
|
List<UsbSerialDecodedPacket> ingest(Uint8List bytes) {
|
||||||
if (bytes.isEmpty) {
|
if (bytes.isEmpty) {
|
||||||
@@ -37,34 +38,56 @@ class UsbSerialFrameDecoder {
|
|||||||
final packets = <UsbSerialDecodedPacket>[];
|
final packets = <UsbSerialDecodedPacket>[];
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
if (_rxBuffer.isEmpty) {
|
if (_startIndex >= _rxBuffer.length) {
|
||||||
|
_rxBuffer.clear();
|
||||||
|
_startIndex = 0;
|
||||||
return packets;
|
return packets;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_rxBuffer.first != usbSerialRxFrameStart &&
|
if (_rxBuffer[_startIndex] != usbSerialRxFrameStart &&
|
||||||
_rxBuffer.first != usbSerialTxFrameStart) {
|
_rxBuffer[_startIndex] != usbSerialTxFrameStart) {
|
||||||
_rxBuffer.removeAt(0);
|
_startIndex++;
|
||||||
|
_compactBufferIfNeeded();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_rxBuffer.length < usbSerialHeaderLength) {
|
final availableLength = _rxBuffer.length - _startIndex;
|
||||||
|
if (availableLength < usbSerialHeaderLength) {
|
||||||
|
_compactBufferIfNeeded(force: true);
|
||||||
return packets;
|
return packets;
|
||||||
}
|
}
|
||||||
|
|
||||||
final payloadLength = _rxBuffer[1] | (_rxBuffer[2] << 8);
|
final payloadLength =
|
||||||
|
_rxBuffer[_startIndex + 1] | (_rxBuffer[_startIndex + 2] << 8);
|
||||||
final packetLength = usbSerialHeaderLength + payloadLength;
|
final packetLength = usbSerialHeaderLength + payloadLength;
|
||||||
if (_rxBuffer.length < packetLength) {
|
if (availableLength < packetLength) {
|
||||||
|
_compactBufferIfNeeded(force: true);
|
||||||
return packets;
|
return packets;
|
||||||
}
|
}
|
||||||
|
|
||||||
final frameStart = _rxBuffer.first;
|
final frameStart = _rxBuffer[_startIndex];
|
||||||
final payload = Uint8List.fromList(
|
final payload = Uint8List.fromList(
|
||||||
_rxBuffer.sublist(usbSerialHeaderLength, packetLength),
|
_rxBuffer.sublist(
|
||||||
|
_startIndex + usbSerialHeaderLength,
|
||||||
|
_startIndex + packetLength,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
_rxBuffer.removeRange(0, packetLength);
|
_startIndex += packetLength;
|
||||||
|
_compactBufferIfNeeded();
|
||||||
packets.add(
|
packets.add(
|
||||||
UsbSerialDecodedPacket(frameStart: frameStart, payload: payload),
|
UsbSerialDecodedPacket(frameStart: frameStart, payload: payload),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _compactBufferIfNeeded({bool force = false}) {
|
||||||
|
if (_startIndex == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!force && _startIndex < 1024 && _startIndex < (_rxBuffer.length ~/ 2)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_rxBuffer.removeRange(0, _startIndex);
|
||||||
|
_startIndex = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,8 +88,10 @@ class UsbSerialService {
|
|||||||
|
|
||||||
debugPrint('USB serial opened port=$_connectedPortName via Web Serial');
|
debugPrint('USB serial opened port=$_connectedPortName via Web Serial');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
await _cleanupFailedConnect();
|
||||||
_status = UsbSerialStatus.disconnected;
|
_status = UsbSerialStatus.disconnected;
|
||||||
_connectedPortName = null;
|
_connectedPortName = null;
|
||||||
|
_connectedPortKey = null;
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -205,6 +207,37 @@ class UsbSerialService {
|
|||||||
return port.callMethod<JSPromise<JSAny?>>('open'.toJS, options).toDart;
|
return port.callMethod<JSPromise<JSAny?>>('open'.toJS, options).toDart;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _cleanupFailedConnect() async {
|
||||||
|
final reader = _reader;
|
||||||
|
final writer = _writer;
|
||||||
|
final port = _port;
|
||||||
|
|
||||||
|
_reader = null;
|
||||||
|
_writer = null;
|
||||||
|
_port = null;
|
||||||
|
|
||||||
|
if (reader != null) {
|
||||||
|
try {
|
||||||
|
await reader.callMethod<JSPromise<JSAny?>>('cancel'.toJS).toDart;
|
||||||
|
} catch (_) {
|
||||||
|
// Ignore cleanup errors after a failed connect.
|
||||||
|
}
|
||||||
|
_releaseLock(reader);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (writer != null) {
|
||||||
|
_releaseLock(writer);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (port != null) {
|
||||||
|
try {
|
||||||
|
await port.callMethod<JSPromise<JSAny?>>('close'.toJS).toDart;
|
||||||
|
} catch (_) {
|
||||||
|
// Ignore cleanup errors after a failed connect.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
JSObject? _getReader(JSObject port) {
|
JSObject? _getReader(JSObject port) {
|
||||||
final readable = port.getProperty<JSAny?>('readable'.toJS);
|
final readable = port.getProperty<JSAny?>('readable'.toJS);
|
||||||
if (readable == null) {
|
if (readable == null) {
|
||||||
|
|||||||
Reference in New Issue
Block a user