websocket comms console widget
This commit is contained in:
@@ -9,6 +9,7 @@ import '../widgets/navigator_widget.dart';
|
||||
import '../widgets/stat_box.dart';
|
||||
import '../widgets/stat_box_main.dart';
|
||||
import '../widgets/system_bar.dart';
|
||||
import '../widgets/debug_console.dart';
|
||||
|
||||
// test service for triggers
|
||||
import '../services/test_flipflop_service.dart';
|
||||
@@ -202,11 +203,26 @@ class _DashboardScreenState extends State<DashboardScreen> {
|
||||
|
||||
const SizedBox(width: 32),
|
||||
|
||||
// Right side: Image display (flex: 1)
|
||||
// Right side: Navigator with debug console overlay
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Center(
|
||||
child: NavigatorWidget(key: _navigatorKey),
|
||||
child: Stack(
|
||||
children: [
|
||||
// Bottom layer: Navigator
|
||||
Center(
|
||||
child: NavigatorWidget(key: _navigatorKey),
|
||||
),
|
||||
// Top layer: Debug console on lower half only
|
||||
Column(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
const Spacer(), // Top half - empty
|
||||
const Expanded(
|
||||
child: DebugConsole(maxLines: 8),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -73,6 +73,11 @@ class WebSocketService {
|
||||
late StreamController<CommandAck> _ackController;
|
||||
late StreamController<BackendAlert> _alertController;
|
||||
late StreamController<WsConnectionState> _connectionController;
|
||||
late StreamController<String> _debugController;
|
||||
|
||||
// Debug message buffer
|
||||
static const int _maxDebugMessages = 50;
|
||||
final List<String> _debugMessages = [];
|
||||
|
||||
void _setupStreams() {
|
||||
_arduinoController = StreamController<ArduinoData>.broadcast();
|
||||
@@ -81,6 +86,16 @@ class WebSocketService {
|
||||
_ackController = StreamController<CommandAck>.broadcast();
|
||||
_alertController = StreamController<BackendAlert>.broadcast();
|
||||
_connectionController = StreamController<WsConnectionState>.broadcast();
|
||||
_debugController = StreamController<String>.broadcast();
|
||||
}
|
||||
|
||||
/// Log a debug message (adds to buffer and stream)
|
||||
void _log(String message) {
|
||||
_debugMessages.add(message);
|
||||
if (_debugMessages.length > _maxDebugMessages) {
|
||||
_debugMessages.removeAt(0);
|
||||
}
|
||||
_debugController.add(message);
|
||||
}
|
||||
|
||||
// --- Public API: Streams ---
|
||||
@@ -103,6 +118,12 @@ class WebSocketService {
|
||||
/// Stream of connection state changes
|
||||
Stream<WsConnectionState> get connectionStream => _connectionController.stream;
|
||||
|
||||
/// Stream of debug log messages
|
||||
Stream<String> get debugStream => _debugController.stream;
|
||||
|
||||
/// Current debug message buffer (for initial display)
|
||||
List<String> get debugMessages => List.unmodifiable(_debugMessages);
|
||||
|
||||
// --- Public API: Sync getters (backward compat) ---
|
||||
|
||||
/// Current connection state
|
||||
@@ -135,25 +156,25 @@ class WebSocketService {
|
||||
});
|
||||
|
||||
_socket!.onConnect((_) {
|
||||
print('[WS] Connected to $_serverUrl');
|
||||
_log('connected');
|
||||
_setConnectionState(WsConnectionState.connected);
|
||||
_cancelReconnect();
|
||||
});
|
||||
|
||||
_socket!.onDisconnect((_) {
|
||||
print('[WS] Disconnected');
|
||||
_log('disconnected');
|
||||
_setConnectionState(WsConnectionState.disconnected);
|
||||
_scheduleReconnect();
|
||||
});
|
||||
|
||||
_socket!.onConnectError((error) {
|
||||
print('[WS] Connection error: $error');
|
||||
_log('error: $error');
|
||||
_setConnectionState(WsConnectionState.disconnected);
|
||||
_scheduleReconnect();
|
||||
});
|
||||
|
||||
_socket!.onError((error) {
|
||||
print('[WS] Error: $error');
|
||||
_log('error: $error');
|
||||
});
|
||||
|
||||
// --- Telemetry Events ---
|
||||
@@ -163,6 +184,7 @@ class WebSocketService {
|
||||
final arduino = ArduinoData.fromJson(data);
|
||||
_latestArduino = arduino;
|
||||
_arduinoController.add(arduino);
|
||||
_log('ard: ${arduino.rpm ?? "-"}rpm ${arduino.voltage ?? "-"}V g${arduino.gear ?? "-"}');
|
||||
}
|
||||
});
|
||||
|
||||
@@ -171,6 +193,7 @@ class WebSocketService {
|
||||
final gps = GpsData.fromJson(data);
|
||||
_latestGps = gps;
|
||||
_gpsController.add(gps);
|
||||
_log('gps: ${gps.speed?.toStringAsFixed(1) ?? "-"}m/s mode${gps.mode ?? "-"}');
|
||||
}
|
||||
});
|
||||
|
||||
@@ -182,6 +205,7 @@ class WebSocketService {
|
||||
);
|
||||
_latestStatus = status;
|
||||
_statusController.add(status);
|
||||
_log('status: gps=${status.gpsConnected} ard=${status.arduinoConnected}');
|
||||
}
|
||||
});
|
||||
|
||||
@@ -196,6 +220,7 @@ class WebSocketService {
|
||||
extra: data['extra'],
|
||||
);
|
||||
_ackController.add(ack);
|
||||
_log('ack: ${ack.id}=${ack.status}${ack.error != null ? " err:${ack.error}" : ""}');
|
||||
}
|
||||
});
|
||||
|
||||
@@ -206,6 +231,7 @@ class WebSocketService {
|
||||
message: data['message'] ?? '',
|
||||
);
|
||||
_alertController.add(alert);
|
||||
_log('alert: [${alert.type}] ${alert.message}');
|
||||
}
|
||||
});
|
||||
|
||||
@@ -283,5 +309,6 @@ class WebSocketService {
|
||||
_ackController.close();
|
||||
_alertController.close();
|
||||
_connectionController.close();
|
||||
_debugController.close();
|
||||
}
|
||||
}
|
||||
|
||||
72
pi/ui/lib/widgets/debug_console.dart
Normal file
72
pi/ui/lib/widgets/debug_console.dart
Normal file
@@ -0,0 +1,72 @@
|
||||
import 'dart:async';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../services/websocket_service.dart';
|
||||
import '../theme/app_theme.dart';
|
||||
|
||||
/// Self-contained debug console that displays WebSocket log messages.
|
||||
/// Subscribes to WebSocketService.debugStream internally.
|
||||
class DebugConsole extends StatefulWidget {
|
||||
/// Maximum lines to display
|
||||
final int maxLines;
|
||||
|
||||
const DebugConsole({
|
||||
super.key,
|
||||
this.maxLines = 8,
|
||||
});
|
||||
|
||||
@override
|
||||
State<DebugConsole> createState() => _DebugConsoleState();
|
||||
}
|
||||
|
||||
class _DebugConsoleState extends State<DebugConsole> {
|
||||
final List<String> _messages = [];
|
||||
StreamSubscription<String>? _debugSub;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
// Initialize with existing buffer
|
||||
_messages.addAll(WebSocketService.instance.debugMessages);
|
||||
_trimMessages();
|
||||
|
||||
// Subscribe to new messages
|
||||
_debugSub = WebSocketService.instance.debugStream.listen((msg) {
|
||||
setState(() {
|
||||
_messages.add(msg);
|
||||
_trimMessages();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void _trimMessages() {
|
||||
while (_messages.length > widget.maxLines) {
|
||||
_messages.removeAt(0);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_debugSub?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = AppTheme.of(context);
|
||||
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: Text(
|
||||
_messages.isEmpty ? '(no messages)' : _messages.join('\n'),
|
||||
style: TextStyle(
|
||||
fontFamily: 'monospace',
|
||||
fontSize: 34,
|
||||
color: theme.foreground,
|
||||
height: 1.2,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user