From 86711dbb273337475683725aca71e8baf92f0ad2 Mon Sep 17 00:00:00 2001 From: Janez T Date: Wed, 15 Oct 2025 10:34:50 +0200 Subject: [PATCH] refactor: Improve connection and room login state management by utilizing helper methods --- CLAUDE.md | 88 ++++++++++++++++--- lib/providers/connection_provider.dart | 48 +++------- lib/providers/helpers/room_login_manager.dart | 3 +- 3 files changed, 87 insertions(+), 52 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 3fdf5a5..0301896 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -29,27 +29,58 @@ lib/ │ ├── message.dart # Messages and SAR markers │ ├── sar_marker.dart # SAR tactical markers │ ├── device_info.dart # BLE device connection state +│ ├── room_login_state.dart # Room login status tracking │ └── map_layer.dart # Map tile layer definitions ├── services/ # Business logic services -│ ├── meshcore_ble_service.dart # BLE communication +│ ├── meshcore_ble_service.dart # BLE service coordinator (399 lines) │ ├── meshcore_constants.dart # Protocol constants │ ├── buffer_reader.dart # Binary protocol reader │ ├── buffer_writer.dart # Binary protocol writer │ ├── cayenne_lpp_parser.dart # Telemetry decoder -│ └── tile_cache_service.dart # Offline map tiles +│ ├── tile_cache_service.dart # Offline map tiles +│ ├── protocol/ # Protocol layer (628 lines) +│ │ ├── frame_parser.dart # Parse incoming BLE frames +│ │ └── frame_builder.dart # Build outgoing BLE frames +│ └── ble/ # BLE layer (963 lines) +│ ├── ble_connection_manager.dart # Connection lifecycle +│ ├── ble_command_sender.dart # Command transmission +│ └── ble_response_handler.dart # Response processing ├── providers/ # State management -│ ├── connection_provider.dart # BLE connection state +│ ├── connection_provider.dart # BLE connection state (957 lines) │ ├── contacts_provider.dart # Contact list management │ ├── messages_provider.dart # Message history + SAR markers │ ├── map_provider.dart # Map navigation state -│ └── app_provider.dart # Coordinator provider +│ ├── app_provider.dart # Coordinator provider +│ └── helpers/ # Provider helpers (155 lines) +│ ├── room_login_manager.dart # Room login state management +│ └── message_delivery_tracker.dart # Message delivery tracking ├── screens/ # UI screens │ ├── home_screen.dart # Main screen with tabs -│ ├── messages_tab.dart # Message list view -│ ├── contacts_tab.dart # Contact list view -│ └── map_tab.dart # Interactive map view +│ ├── messages_tab.dart # Message list view (767 lines) +│ ├── contacts_tab.dart # Contact list view (208 lines) +│ ├── map_tab.dart # Interactive map view (1,217 lines) +│ ├── settings_screen.dart # App settings (875 lines) +│ ├── device_config_screen.dart # Device configuration (718 lines) +│ ├── map_management_screen.dart # Map tile management (728 lines) +│ └── packet_log_screen.dart # BLE packet diagnostics (565 lines) ├── widgets/ # Reusable UI components -│ └── map_markers.dart # Custom map marker widgets +│ ├── map_markers.dart # Custom map marker widgets +│ ├── messages/ # Message components (698 lines) +│ │ └── sar_update_sheet.dart # SAR marker creation modal +│ ├── contacts/ # Contact components (1,578 lines) +│ │ ├── contact_tile.dart # Contact list tile + details dialog +│ │ ├── direct_message_sheet.dart # Direct messaging modal +│ │ ├── room_login_sheet.dart # Room login modal +│ │ └── section_header.dart # Section header component +│ └── map/ # Map components (2,867 lines) +│ ├── detailed_compass_dialog.dart # Main compass dialog (570 lines) +│ ├── map_legend.dart # Map legend with counts +│ ├── compass_widget.dart # Small compass widget +│ └── compass/ # Compass subcomponents (1,150 lines) +│ ├── compass_header.dart # Compass rose + location display +│ ├── compass_filters.dart # Filter controls +│ ├── compass_sar_list.dart # SAR marker list +│ └── compass_contact_list.dart # Contact list ├── utils/ # Utilities │ └── sar_message_parser.dart # Parse S::lat,lon format └── main.dart # App entry point @@ -1011,28 +1042,57 @@ flutter build apk --split-per-abi ### Adding a New BLE Command +The BLE service is now split into modular components. Follow these steps: + 1. **Add command code** to `lib/services/meshcore_constants.dart`: ```dart static const int cmdYourCommand = 42; ``` -2. **Create command method** in `lib/services/meshcore_ble_service.dart`: +2. **Add frame builder** in `lib/services/protocol/frame_builder.dart`: ```dart - Future yourCommand() async { + /// Build YOUR_COMMAND frame + static Uint8List buildYourCommand({required String param}) { final writer = BufferWriter(); writer.writeByte(MeshCoreConstants.cmdYourCommand); - await _sendCommand(writer.toBytes()); + writer.writeString(param); + return writer.toBytes(); } ``` -3. **Handle response** in `_onDataReceived()`: +3. **Add public API method** in `lib/services/meshcore_ble_service.dart`: + ```dart + Future yourCommand({required String param}) async { + final frame = FrameBuilder.buildYourCommand(param: param); + await _commandSender.sendCommand(frame); + } + ``` + +4. **Add response parser** in `lib/services/protocol/frame_parser.dart`: + ```dart + static Map parseYourResponse(Uint8List data) { + final reader = BufferReader(data); + reader.readByte(); // Skip response code + return { + 'yourField': reader.readString(), + // ... parse other fields + }; + } + ``` + +5. **Handle response** in `lib/services/ble/ble_response_handler.dart`: ```dart case MeshCoreConstants.respYourResponse: - // Parse response data - onYourCallback?.call(data); + final parsed = FrameParser.parseYourResponse(data); + _bleService.onYourCallback?.call(parsed); break; ``` +6. **Add callback** in `lib/services/meshcore_ble_service.dart`: + ```dart + Function(Map)? onYourCallback; + ``` + ### Adding a New SAR Marker Type 1. **Update enum** in `lib/models/sar_marker.dart`: diff --git a/lib/providers/connection_provider.dart b/lib/providers/connection_provider.dart index e3ab7cd..5779351 100644 --- a/lib/providers/connection_provider.dart +++ b/lib/providers/connection_provider.dart @@ -1,5 +1,4 @@ import 'dart:async'; -import 'dart:typed_data'; import 'package:flutter/foundation.dart'; import 'package:flutter_blue_plus/flutter_blue_plus.dart'; import '../models/device_info.dart'; @@ -7,7 +6,6 @@ import '../models/contact.dart'; import '../models/message.dart'; import '../models/room_login_state.dart'; import '../services/meshcore_ble_service.dart'; -import '../services/cayenne_lpp_parser.dart'; import '../utils/sar_message_parser.dart'; import 'helpers/room_login_manager.dart'; import 'helpers/message_delivery_tracker.dart'; @@ -193,14 +191,14 @@ class ConnectionProvider with ChangeNotifier { _bleService.onMessageSent = (expectedAckTag, suggestedTimeoutMs, isFloodMode) { print('📥 [Provider] Message sent - ACK tag: $expectedAckTag, timeout: ${suggestedTimeoutMs}ms'); - // Pop the first pending message ID from the queue (FIFO) - // This assumes messages are sent sequentially and SENT responses arrive in order - if (_pendingSentMessageIds.isNotEmpty) { - final messageId = _pendingSentMessageIds.removeAt(0); + // Pop the first pending message ID from the queue (FIFO) via helper + final messageId = _messageDeliveryTracker.popPendingMessageId(); + + if (messageId != null) { print(' Matched with message ID: $messageId'); // Store the ACK tag to message ID mapping for delivery confirmation - _ackTagToMessageId[expectedAckTag] = messageId; + _messageDeliveryTracker.mapAckTagToMessageId(expectedAckTag, messageId); // Notify callback with message ID onMessageSent?.call(messageId, expectedAckTag, suggestedTimeoutMs); @@ -398,7 +396,7 @@ class ConnectionProvider with ChangeNotifier { _deviceInfo = DeviceInfo( connectionState: ConnectionState.disconnected, ); - clearRoomLoginStates(); // Clear login states on disconnect + _roomLoginManager.clearRoomLoginStates(); // Clear login states on disconnect notifyListeners(); } @@ -462,11 +460,11 @@ class ConnectionProvider with ChangeNotifier { text: text, ); - // If message ID provided, add it to the pending queue + // If message ID provided, add it to the pending queue via helper // When the SENT response arrives, it will be matched with this message ID // Note: Messages must be sent sequentially for this to work correctly if (messageId != null) { - _pendingSentMessageIds.add(messageId); + _messageDeliveryTracker.trackPendingMessage(messageId); print(' Added message ID to pending queue: $messageId'); } @@ -498,9 +496,9 @@ class ConnectionProvider with ChangeNotifier { text: text, ); - // If message ID provided, add it to the pending queue + // If message ID provided, add it to the pending queue via helper if (messageId != null) { - _pendingSentMessageIds.add(messageId); + _messageDeliveryTracker.trackPendingMessage(messageId); print(' Added message ID to pending queue: $messageId'); } } catch (e) { @@ -939,36 +937,14 @@ class ConnectionProvider with ChangeNotifier { notifyListeners(); } - /// Check if a password exists for a room (by public key prefix) - Future _hasPasswordForRoom(Uint8List publicKeyPrefix) async { - try { - final prefs = await SharedPreferences.getInstance(); - // Convert prefix to hex string for storage key - final prefixHex = publicKeyPrefix.map((b) => b.toRadixString(16).padLeft(2, '0')).join(':'); - final roomKey = 'room_password_$prefixHex'; - return prefs.getString(roomKey) != null; - } catch (e) { - debugPrint('Error checking password for room: $e'); - return false; - } - } - /// Get login state for a room by public key prefix RoomLoginState? getRoomLoginState(Uint8List publicKeyPrefix) { - final prefixHex = publicKeyPrefix.map((b) => b.toRadixString(16).padLeft(2, '0')).join(':'); - return _roomLoginStates[prefixHex]; + return _roomLoginManager.getRoomLoginState(publicKeyPrefix); } /// Check if logged into a specific room bool isLoggedIntoRoom(Uint8List publicKeyPrefix) { - final state = getRoomLoginState(publicKeyPrefix); - return state?.isLoggedIn ?? false; - } - - /// Clear all room login states (call on disconnect) - void clearRoomLoginStates() { - _roomLoginStates.clear(); - notifyListeners(); + return _roomLoginManager.isLoggedIntoRoom(publicKeyPrefix); } @override diff --git a/lib/providers/helpers/room_login_manager.dart b/lib/providers/helpers/room_login_manager.dart index c9d2097..8dc9f4d 100644 --- a/lib/providers/helpers/room_login_manager.dart +++ b/lib/providers/helpers/room_login_manager.dart @@ -1,4 +1,3 @@ -import 'dart:typed_data'; import 'package:flutter/foundation.dart'; import 'package:shared_preferences/shared_preferences.dart'; import '../../models/room_login_state.dart'; @@ -6,7 +5,7 @@ import '../../models/room_login_state.dart'; /// Room login state management helper /// /// Manages login state tracking for room contacts, including: -/// - Room login state per contact (Map) +/// - Room login state per contact (Map of String to RoomLoginState) /// - Password checking logic /// - Login success/fail state updates class RoomLoginManager {