Skip to content

Commit 6e85554

Browse files
committed
Implement debug logging utility and integrate into application flow
1 parent 63aa05a commit 6e85554

File tree

8 files changed

+403
-58
lines changed

8 files changed

+403
-58
lines changed

lib/main.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,18 @@ import 'screens/home_screen.dart';
88
import 'services/bluetooth/bluetooth_service.dart';
99
import 'services/bluetooth/mobile_bluetooth.dart';
1010
import 'services/bluetooth/web_bluetooth.dart';
11+
import 'utils/debug_logger_io.dart';
1112

1213
void main() async {
1314
WidgetsFlutterBinding.ensureInitialized();
1415

16+
// Initialize debug logger (checks for ?debug=1 URL param on web)
17+
DebugLogger.initialize();
18+
debugLog('[APP] MeshMapper starting...');
19+
1520
// Initialize Hive for local storage
1621
await Hive.initFlutter();
22+
debugLog('[APP] Hive initialized');
1723

1824
runApp(const MeshMapperApp());
1925
}

lib/providers/app_state_provider.dart

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,15 @@ import '../services/device_model_service.dart';
1414
import '../services/gps_service.dart';
1515
import '../services/meshcore/connection.dart';
1616
import '../services/ping_service.dart';
17+
import '../utils/debug_logger_io.dart';
18+
19+
/// Auto-ping mode (matches MeshMapper_WebClient behavior)
20+
enum AutoMode {
21+
/// TX/RX Auto: Sends pings on movement, listens for RX responses
22+
txRx,
23+
/// RX Auto: Passive listening only (no transmit)
24+
rxOnly,
25+
}
1726

1827
/// Main application state provider
1928
class AppStateProvider extends ChangeNotifier {
@@ -44,6 +53,7 @@ class AppStateProvider extends ChangeNotifier {
4453
// Ping state
4554
PingStats _pingStats = const PingStats();
4655
bool _autoPingEnabled = false;
56+
AutoMode _autoMode = AutoMode.txRx;
4757
int _queueSize = 0;
4858

4959
// Discovered devices
@@ -73,6 +83,7 @@ class AppStateProvider extends ChangeNotifier {
7383
String? get manufacturerString => _manufacturerString;
7484
PingStats get pingStats => _pingStats;
7585
bool get autoPingEnabled => _autoPingEnabled;
86+
AutoMode get autoMode => _autoMode;
7687
int get queueSize => _queueSize;
7788
List<DiscoveredDevice> get discoveredDevices => _discoveredDevices;
7889
bool get isScanning => _isScanning;
@@ -287,18 +298,29 @@ class AppStateProvider extends ChangeNotifier {
287298
/// Send a manual TX ping
288299
Future<bool> sendPing() async {
289300
if (_pingService == null) return false;
301+
debugLog('[PING] Sending manual TX ping');
290302
return await _pingService!.sendTxPing();
291303
}
292304

293-
/// Toggle auto-ping mode
294-
void toggleAutoPing() {
305+
/// Toggle auto-ping mode (TX/RX or RX-only)
306+
void toggleAutoPing(AutoMode mode) {
295307
if (_pingService == null) return;
296308

297-
if (_autoPingEnabled) {
309+
// If currently running the same mode, stop it
310+
if (_autoPingEnabled && _autoMode == mode) {
311+
debugLog('[PING] Stopping auto mode: ${mode.name}');
298312
_pingService!.disableAutoPing();
299313
_autoPingEnabled = false;
300314
} else {
301-
_pingService!.enableAutoPing();
315+
// Stop any existing mode first
316+
if (_autoPingEnabled) {
317+
_pingService!.disableAutoPing();
318+
}
319+
320+
// Start new mode
321+
debugLog('[PING] Starting auto mode: ${mode.name}');
322+
_autoMode = mode;
323+
_pingService!.enableAutoPing(rxOnly: mode == AutoMode.rxOnly);
302324
_autoPingEnabled = true;
303325
}
304326

lib/services/bluetooth/web_bluetooth.dart

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import 'dart:typed_data';
44
import 'package:flutter_web_bluetooth/flutter_web_bluetooth.dart' as fwb;
55

66
import '../../models/connection_state.dart';
7+
import '../../utils/debug_logger_io.dart';
78
import '../meshcore/protocol_constants.dart';
89
import 'bluetooth_service.dart';
910

@@ -63,13 +64,14 @@ class WebBluetoothService implements BluetoothService {
6364
// Web Bluetooth doesn't support scanning - uses requestDevice dialog
6465
// This is a stub that will yield devices from the request dialog
6566
_updateStatus(ConnectionStatus.scanning);
67+
debugLog('[BLE] Opening device picker with service filter: ${BleUuids.serviceUuid}');
6668

6769
try {
68-
// Request device with MeshCore service filter
70+
// Request device filtered by MeshCore service UUID (matches JS implementation)
6971
final device = await _webBluetooth.requestDevice(
70-
fwb.RequestOptionsBuilder.acceptAllDevices(
71-
optionalServices: [BleUuids.serviceUuid.toLowerCase()],
72-
),
72+
fwb.RequestOptionsBuilder([
73+
fwb.RequestFilterBuilder(services: [BleUuids.serviceUuid.toLowerCase()]),
74+
]),
7375
);
7476

7577
if (device != null) {
@@ -97,12 +99,13 @@ class WebBluetoothService implements BluetoothService {
9799
Future<void> connect(String deviceId) async {
98100
try {
99101
_updateStatus(ConnectionStatus.connecting);
102+
debugLog('[BLE] Connecting to device: $deviceId');
100103

101-
// Request device again (Web Bluetooth requires this)
104+
// Request device filtered by MeshCore service UUID (matches JS implementation)
102105
_device = await _webBluetooth.requestDevice(
103-
fwb.RequestOptionsBuilder.acceptAllDevices(
104-
optionalServices: [BleUuids.serviceUuid.toLowerCase()],
105-
),
106+
fwb.RequestOptionsBuilder([
107+
fwb.RequestFilterBuilder(services: [BleUuids.serviceUuid.toLowerCase()]),
108+
]),
106109
);
107110

108111
if (_device == null) {

lib/services/ping_service.dart

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import 'package:geolocator/geolocator.dart';
44

55
import '../models/connection_state.dart';
66
import '../models/ping_data.dart';
7+
import '../utils/debug_logger_io.dart';
78
import 'api_queue_service.dart';
89
import 'gps_service.dart';
910
import 'meshcore/connection.dart';
@@ -41,6 +42,7 @@ class PingService {
4142

4243
// Auto-ping mode
4344
bool _autoPingEnabled = false;
45+
bool _rxOnlyMode = false;
4446
StreamSubscription? _positionSubscription;
4547

4648
/// Callback for ping events
@@ -228,15 +230,24 @@ class PingService {
228230
}
229231

230232
/// Enable auto-ping mode
231-
void enableAutoPing() {
233+
/// @param rxOnly - If true, only listens for RX (no TX pings)
234+
void enableAutoPing({bool rxOnly = false}) {
232235
if (_autoPingEnabled) return;
233236

234237
_autoPingEnabled = true;
238+
_rxOnlyMode = rxOnly;
239+
debugLog('[PING] Auto-ping enabled, rxOnly: $rxOnly');
235240

236241
// Listen for position updates and auto-ping when conditions are met
237242
_positionSubscription = _gpsService.positionStream.listen((position) async {
238243
if (!_autoPingEnabled) return;
239244

245+
// RX-only mode doesn't send pings
246+
if (_rxOnlyMode) {
247+
debugLog('[RX] RX-only mode - listening for signals');
248+
return;
249+
}
250+
240251
final validation = canPing();
241252
if (validation == PingValidation.valid) {
242253
await sendTxPing();
@@ -246,7 +257,9 @@ class PingService {
246257

247258
/// Disable auto-ping mode
248259
void disableAutoPing() {
260+
debugLog('[PING] Auto-ping disabled');
249261
_autoPingEnabled = false;
262+
_rxOnlyMode = false;
250263
_positionSubscription?.cancel();
251264
_positionSubscription = null;
252265
}

lib/utils/debug_logger.dart

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
// This file is for web platform only - uses dart:html
2+
// For non-web, see debug_logger_stub.dart
3+
// Export via debug_logger_io.dart for conditional import
4+
5+
// ignore: avoid_web_libraries_in_flutter
6+
import 'dart:html' as html show window;
7+
// ignore: avoid_web_libraries_in_flutter
8+
import 'dart:js_util' as js_util;
9+
10+
import 'package:flutter/foundation.dart';
11+
12+
/// Debug logging utility that mirrors MeshMapper_WebClient debug system.
13+
///
14+
/// Logs are only output when DEBUG_ENABLED is true (set via `?debug=1` URL param).
15+
/// All log messages should use tagged format: `[TAG] message`
16+
///
17+
/// Common tags: [BLE], [GPS], [PING], [API], [RX], [UI], [CONN]
18+
class DebugLogger {
19+
static bool _debugEnabled = false;
20+
static bool _initialized = false;
21+
22+
/// Initialize the debug logger by checking URL parameters.
23+
/// Call this early in app startup (e.g., in main.dart).
24+
static void initialize() {
25+
if (_initialized) return;
26+
_initialized = true;
27+
28+
if (kIsWeb) {
29+
try {
30+
// Parse URL parameters from browser
31+
final uri = Uri.base;
32+
final debugParam = uri.queryParameters['debug'];
33+
_debugEnabled = debugParam == '1' || debugParam == 'true';
34+
35+
if (_debugEnabled) {
36+
_consoleLog('[DEBUG] Debug logging ENABLED via URL param');
37+
}
38+
} catch (e) {
39+
// Fallback - URL parsing failed
40+
_debugEnabled = false;
41+
}
42+
} else {
43+
// On mobile, check for debug mode compile flag
44+
_debugEnabled = kDebugMode;
45+
}
46+
}
47+
48+
/// Check if debug logging is enabled
49+
static bool get isEnabled => _debugEnabled;
50+
51+
/// Manually enable/disable debug logging (for testing)
52+
static void setEnabled(bool enabled) {
53+
_debugEnabled = enabled;
54+
}
55+
56+
/// Log a general info message to the console.
57+
/// Use tagged format: debugLog('[BLE] Connected to device');
58+
static void log(String message, [Object? arg1, Object? arg2, Object? arg3]) {
59+
if (!_debugEnabled) return;
60+
61+
final args = [message, if (arg1 != null) arg1, if (arg2 != null) arg2, if (arg3 != null) arg3];
62+
63+
if (kIsWeb) {
64+
_consoleLog(args.join(' '));
65+
} else {
66+
debugPrint(args.join(' '));
67+
}
68+
}
69+
70+
/// Log a warning message to the console.
71+
/// Use tagged format: debugWarn('[GPS] Position stale, re-acquiring');
72+
static void warn(String message, [Object? arg1, Object? arg2, Object? arg3]) {
73+
if (!_debugEnabled) return;
74+
75+
final args = ['⚠️', message, if (arg1 != null) arg1, if (arg2 != null) arg2, if (arg3 != null) arg3];
76+
77+
if (kIsWeb) {
78+
_consoleWarn(args.join(' '));
79+
} else {
80+
debugPrint('WARN: ${args.join(' ')}');
81+
}
82+
}
83+
84+
/// Log an error message to the console.
85+
/// Use tagged format: debugError('[API] Failed to post queue', error);
86+
static void error(String message, [Object? arg1, Object? arg2, Object? arg3]) {
87+
if (!_debugEnabled) return;
88+
89+
final args = ['❌', message, if (arg1 != null) arg1, if (arg2 != null) arg2, if (arg3 != null) arg3];
90+
91+
if (kIsWeb) {
92+
_consoleError(args.join(' '));
93+
} else {
94+
debugPrint('ERROR: ${args.join(' ')}');
95+
}
96+
}
97+
98+
// Web console methods using dart:js_util for proper browser console output
99+
static void _consoleLog(String message) {
100+
try {
101+
js_util.callMethod(html.window.console, 'log', [message]);
102+
} catch (e) {
103+
// Fallback
104+
// ignore: avoid_print
105+
print(message);
106+
}
107+
}
108+
109+
static void _consoleWarn(String message) {
110+
try {
111+
js_util.callMethod(html.window.console, 'warn', [message]);
112+
} catch (e) {
113+
// ignore: avoid_print
114+
print('WARN: $message');
115+
}
116+
}
117+
118+
static void _consoleError(String message) {
119+
try {
120+
js_util.callMethod(html.window.console, 'error', [message]);
121+
} catch (e) {
122+
// ignore: avoid_print
123+
print('ERROR: $message');
124+
}
125+
}
126+
}
127+
128+
/// Convenience global functions matching MeshMapper_WebClient API
129+
void debugLog(String message, [Object? arg1, Object? arg2, Object? arg3]) {
130+
DebugLogger.log(message, arg1, arg2, arg3);
131+
}
132+
133+
void debugWarn(String message, [Object? arg1, Object? arg2, Object? arg3]) {
134+
DebugLogger.warn(message, arg1, arg2, arg3);
135+
}
136+
137+
void debugError(String message, [Object? arg1, Object? arg2, Object? arg3]) {
138+
DebugLogger.error(message, arg1, arg2, arg3);
139+
}

lib/utils/debug_logger_io.dart

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/// Debug logger with conditional imports for web vs mobile platforms.
2+
///
3+
/// Usage:
4+
/// ```dart
5+
/// import 'package:meshmapper/utils/debug_logger_io.dart';
6+
///
7+
/// DebugLogger.initialize();
8+
/// debugLog('[TAG] message');
9+
/// debugWarn('[TAG] warning');
10+
/// debugError('[TAG] error');
11+
/// ```
12+
export 'debug_logger_stub.dart'
13+
if (dart.library.html) 'debug_logger.dart';

0 commit comments

Comments
 (0)