mirror of
https://github.com/zjs81/meshcore-open.git
synced 2026-07-29 14:58:44 +10:00
refactor(tcp): promote MeshCoreTcpConnector, fix translations, harden UI
- Replace thin MeshCoreTcpManager facade with a proper MeshCoreTcpConnector
that owns TcpTransportService and the frame subscription, mirroring
MeshCoreUsbManager. The connector no longer holds a raw TcpTransportService
or a _tcpFrameSubscription field.
- Remove hardcoded default host IP from TcpScreen (keep port 5000 hint).
- Disable connect button during scanning state, not just connecting state.
- Fix tcpPortLabel mistranslated as nautical "port/harbor" in de, it, pt,
nl, sv, sk, sl, zh; fix corrupted Slovak tcpPortHint ("5 000" → "5000").
- Remove unused tcpStatus_connecting string from all 15 locale arb files
and all generated app_localizations_*.dart files.
- Add extendedPadding to TCP screen FABs to match USB screen.
- Add Key to connect button; update tests to use byKey and assert
onPressed == null when button is disabled during scanning.
This commit is contained in:
@@ -116,8 +116,7 @@ class MeshCoreConnector extends ChangeNotifier {
|
||||
bool _manualDisconnect = false;
|
||||
final MeshCoreUsbManager _usbManager = MeshCoreUsbManager();
|
||||
StreamSubscription<Uint8List>? _usbFrameSubscription;
|
||||
final MeshCoreTcpManager _tcpManager = MeshCoreTcpManager();
|
||||
StreamSubscription<Uint8List>? _tcpFrameSubscription;
|
||||
final MeshCoreTcpConnector _tcpConnector = MeshCoreTcpConnector();
|
||||
MeshCoreTransportType _activeTransport = MeshCoreTransportType.bluetooth;
|
||||
|
||||
final List<ScanResult> _scanResults = [];
|
||||
@@ -257,7 +256,7 @@ class MeshCoreConnector extends ChangeNotifier {
|
||||
bool get isUsbTransportConnected =>
|
||||
_state == MeshCoreConnectionState.connected &&
|
||||
_activeTransport == MeshCoreTransportType.usb;
|
||||
String? get activeTcpEndpoint => _tcpManager.activeEndpoint;
|
||||
String? get activeTcpEndpoint => _tcpConnector.activeEndpoint;
|
||||
bool get isTcpTransportConnected =>
|
||||
_state == MeshCoreConnectionState.connected &&
|
||||
_activeTransport == MeshCoreTransportType.tcp;
|
||||
@@ -666,7 +665,7 @@ class MeshCoreConnector extends ChangeNotifier {
|
||||
_appDebugLogService = appDebugLogService;
|
||||
_backgroundService = backgroundService;
|
||||
_usbManager.setDebugLogService(_appDebugLogService);
|
||||
_tcpManager.setDebugLogService(_appDebugLogService);
|
||||
_tcpConnector.setDebugLogService(_appDebugLogService);
|
||||
|
||||
// Initialize notification service
|
||||
_notificationService.initialize();
|
||||
@@ -1002,22 +1001,21 @@ class MeshCoreConnector extends ChangeNotifier {
|
||||
await disconnect(manual: false);
|
||||
return;
|
||||
}
|
||||
if (_tcpManager.isConnected) {
|
||||
await _tcpManager.disconnect();
|
||||
if (_tcpConnector.isConnected) {
|
||||
await _tcpConnector.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
await _tcpFrameSubscription?.cancel();
|
||||
_tcpFrameSubscription = null;
|
||||
await _tcpManager.connect(host: host, port: port);
|
||||
await _tcpConnector.cancelFrameSubscription();
|
||||
await _tcpConnector.connect(host: host, port: port);
|
||||
final isTcpConnectCancelled =
|
||||
_activeTransport != MeshCoreTransportType.tcp ||
|
||||
_state != MeshCoreConnectionState.connecting ||
|
||||
!_tcpManager.isConnected;
|
||||
!_tcpConnector.isConnected;
|
||||
if (isTcpConnectCancelled) {
|
||||
await handleTcpConnectAbort(
|
||||
message:
|
||||
'connectTcp aborted before handshake: state=$_state transport=$_activeTransport connected=${_tcpManager.isConnected}',
|
||||
'connectTcp aborted before handshake: state=$_state transport=$_activeTransport connected=${_tcpConnector.isConnected}',
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -1027,16 +1025,16 @@ class MeshCoreConnector extends ChangeNotifier {
|
||||
final isTcpConnectCancelledAfterDelay =
|
||||
_activeTransport != MeshCoreTransportType.tcp ||
|
||||
_state != MeshCoreConnectionState.connecting ||
|
||||
!_tcpManager.isConnected;
|
||||
!_tcpConnector.isConnected;
|
||||
if (isTcpConnectCancelledAfterDelay) {
|
||||
await handleTcpConnectAbort(
|
||||
message:
|
||||
'connectTcp aborted after connect delay: state=$_state transport=$_activeTransport connected=${_tcpManager.isConnected}',
|
||||
'connectTcp aborted after connect delay: state=$_state transport=$_activeTransport connected=${_tcpConnector.isConnected}',
|
||||
);
|
||||
return;
|
||||
}
|
||||
_tcpFrameSubscription = _tcpManager.frameStream.listen(
|
||||
_handleFrame,
|
||||
_tcpConnector.listenFrames(
|
||||
onFrame: _handleFrame,
|
||||
onError: (error, stackTrace) {
|
||||
_appDebugLogService?.error('TCP transport error: $error', tag: 'TCP');
|
||||
unawaited(disconnect(manual: false));
|
||||
@@ -1073,7 +1071,7 @@ class MeshCoreConnector extends ChangeNotifier {
|
||||
manualDisconnect: _manualDisconnect,
|
||||
state: _state,
|
||||
activeTransport: _activeTransport,
|
||||
tcpManagerConnected: _tcpManager.isConnected,
|
||||
tcpManagerConnected: _tcpConnector.isConnected,
|
||||
);
|
||||
if (tcpConnectCancelledBeforeHandshake) {
|
||||
_appDebugLogService?.info(
|
||||
@@ -1445,9 +1443,7 @@ class MeshCoreConnector extends ChangeNotifier {
|
||||
await _usbFrameSubscription?.cancel();
|
||||
_usbFrameSubscription = null;
|
||||
await _usbManager.disconnect();
|
||||
await _tcpFrameSubscription?.cancel();
|
||||
_tcpFrameSubscription = null;
|
||||
await _tcpManager.disconnect();
|
||||
await _tcpConnector.disconnect();
|
||||
|
||||
await _notifySubscription?.cancel();
|
||||
_notifySubscription = null;
|
||||
@@ -1530,7 +1526,7 @@ class MeshCoreConnector extends ChangeNotifier {
|
||||
if (_activeTransport == MeshCoreTransportType.usb) {
|
||||
await _usbManager.write(data);
|
||||
} else if (_activeTransport == MeshCoreTransportType.tcp) {
|
||||
await _tcpManager.write(data);
|
||||
await _tcpConnector.write(data);
|
||||
} else {
|
||||
if (_rxCharacteristic == null) {
|
||||
throw Exception("MeshCore RX characteristic not available");
|
||||
@@ -4484,14 +4480,13 @@ class MeshCoreConnector extends ChangeNotifier {
|
||||
_scanSubscription?.cancel();
|
||||
_connectionSubscription?.cancel();
|
||||
_usbFrameSubscription?.cancel();
|
||||
_tcpFrameSubscription?.cancel();
|
||||
_notifySubscription?.cancel();
|
||||
_notifyListenersTimer?.cancel();
|
||||
_reconnectTimer?.cancel();
|
||||
_batteryPollTimer?.cancel();
|
||||
_receivedFramesController.close();
|
||||
_usbManager.dispose();
|
||||
_tcpManager.dispose();
|
||||
_tcpConnector.dispose();
|
||||
|
||||
// Flush pending unread writes before disposal
|
||||
_unreadStore.flush();
|
||||
|
||||
@@ -1,34 +1,70 @@
|
||||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import '../services/app_debug_log_service.dart';
|
||||
import '../services/tcp_transport_service.dart';
|
||||
|
||||
class MeshCoreTcpManager {
|
||||
/// Manages TCP transport for MeshCore devices.
|
||||
///
|
||||
/// Owns the [TcpTransportService] and TCP-specific connection state.
|
||||
/// The main [MeshCoreConnector] delegates all TCP operations here.
|
||||
class MeshCoreTcpConnector {
|
||||
final TcpTransportService _service = TcpTransportService();
|
||||
AppDebugLogService? _debugLog;
|
||||
StreamSubscription<Uint8List>? _frameSubscription;
|
||||
|
||||
// --- Getters ---
|
||||
String? get activeEndpoint => _service.activeEndpoint;
|
||||
bool get isConnected => _service.isConnected;
|
||||
Stream<Uint8List> get frameStream => _service.frameStream;
|
||||
|
||||
// --- Configuration ---
|
||||
void setDebugLogService(AppDebugLogService? service) {
|
||||
_debugLog = service;
|
||||
_service.setDebugLogService(service);
|
||||
}
|
||||
|
||||
// --- Connection lifecycle ---
|
||||
Future<void> connect({required String host, required int port}) async {
|
||||
_debugLog?.info('TcpManager.connect endpoint=$host:$port', tag: 'TCP');
|
||||
_debugLog?.info('TcpConnector.connect endpoint=$host:$port', tag: 'TCP');
|
||||
await _frameSubscription?.cancel();
|
||||
_frameSubscription = null;
|
||||
await _service.connect(host: host, port: port);
|
||||
_debugLog?.info(
|
||||
'TcpConnector.connect done, endpoint=${_service.activeEndpoint}',
|
||||
tag: 'TCP',
|
||||
);
|
||||
}
|
||||
|
||||
StreamSubscription<Uint8List> listenFrames({
|
||||
required void Function(Uint8List) onFrame,
|
||||
required void Function(Object, StackTrace?) onError,
|
||||
required void Function() onDone,
|
||||
}) {
|
||||
_frameSubscription = _service.frameStream.listen(
|
||||
onFrame,
|
||||
onError: onError,
|
||||
onDone: onDone,
|
||||
);
|
||||
return _frameSubscription!;
|
||||
}
|
||||
|
||||
Future<void> cancelFrameSubscription() async {
|
||||
await _frameSubscription?.cancel();
|
||||
_frameSubscription = null;
|
||||
}
|
||||
|
||||
Future<void> disconnect() async {
|
||||
_debugLog?.info('TcpManager.disconnect', tag: 'TCP');
|
||||
if (!_service.isConnected && _frameSubscription == null) return;
|
||||
_debugLog?.info('TcpConnector.disconnect', tag: 'TCP');
|
||||
await _frameSubscription?.cancel();
|
||||
_frameSubscription = null;
|
||||
await _service.disconnect();
|
||||
}
|
||||
|
||||
Future<void> write(Uint8List data) => _service.write(data);
|
||||
|
||||
void dispose() {
|
||||
_frameSubscription?.cancel();
|
||||
_service.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user