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.dart';
|
||||||
import '../widgets/stat_box_main.dart';
|
import '../widgets/stat_box_main.dart';
|
||||||
import '../widgets/system_bar.dart';
|
import '../widgets/system_bar.dart';
|
||||||
|
import '../widgets/debug_console.dart';
|
||||||
|
|
||||||
// test service for triggers
|
// test service for triggers
|
||||||
import '../services/test_flipflop_service.dart';
|
import '../services/test_flipflop_service.dart';
|
||||||
@@ -202,12 +203,27 @@ class _DashboardScreenState extends State<DashboardScreen> {
|
|||||||
|
|
||||||
const SizedBox(width: 32),
|
const SizedBox(width: 32),
|
||||||
|
|
||||||
// Right side: Image display (flex: 1)
|
// Right side: Navigator with debug console overlay
|
||||||
Expanded(
|
Expanded(
|
||||||
flex: 1,
|
flex: 1,
|
||||||
child: Center(
|
child: Stack(
|
||||||
|
children: [
|
||||||
|
// Bottom layer: Navigator
|
||||||
|
Center(
|
||||||
child: NavigatorWidget(key: _navigatorKey),
|
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<CommandAck> _ackController;
|
||||||
late StreamController<BackendAlert> _alertController;
|
late StreamController<BackendAlert> _alertController;
|
||||||
late StreamController<WsConnectionState> _connectionController;
|
late StreamController<WsConnectionState> _connectionController;
|
||||||
|
late StreamController<String> _debugController;
|
||||||
|
|
||||||
|
// Debug message buffer
|
||||||
|
static const int _maxDebugMessages = 50;
|
||||||
|
final List<String> _debugMessages = [];
|
||||||
|
|
||||||
void _setupStreams() {
|
void _setupStreams() {
|
||||||
_arduinoController = StreamController<ArduinoData>.broadcast();
|
_arduinoController = StreamController<ArduinoData>.broadcast();
|
||||||
@@ -81,6 +86,16 @@ class WebSocketService {
|
|||||||
_ackController = StreamController<CommandAck>.broadcast();
|
_ackController = StreamController<CommandAck>.broadcast();
|
||||||
_alertController = StreamController<BackendAlert>.broadcast();
|
_alertController = StreamController<BackendAlert>.broadcast();
|
||||||
_connectionController = StreamController<WsConnectionState>.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 ---
|
// --- Public API: Streams ---
|
||||||
@@ -103,6 +118,12 @@ class WebSocketService {
|
|||||||
/// Stream of connection state changes
|
/// Stream of connection state changes
|
||||||
Stream<WsConnectionState> get connectionStream => _connectionController.stream;
|
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) ---
|
// --- Public API: Sync getters (backward compat) ---
|
||||||
|
|
||||||
/// Current connection state
|
/// Current connection state
|
||||||
@@ -135,25 +156,25 @@ class WebSocketService {
|
|||||||
});
|
});
|
||||||
|
|
||||||
_socket!.onConnect((_) {
|
_socket!.onConnect((_) {
|
||||||
print('[WS] Connected to $_serverUrl');
|
_log('connected');
|
||||||
_setConnectionState(WsConnectionState.connected);
|
_setConnectionState(WsConnectionState.connected);
|
||||||
_cancelReconnect();
|
_cancelReconnect();
|
||||||
});
|
});
|
||||||
|
|
||||||
_socket!.onDisconnect((_) {
|
_socket!.onDisconnect((_) {
|
||||||
print('[WS] Disconnected');
|
_log('disconnected');
|
||||||
_setConnectionState(WsConnectionState.disconnected);
|
_setConnectionState(WsConnectionState.disconnected);
|
||||||
_scheduleReconnect();
|
_scheduleReconnect();
|
||||||
});
|
});
|
||||||
|
|
||||||
_socket!.onConnectError((error) {
|
_socket!.onConnectError((error) {
|
||||||
print('[WS] Connection error: $error');
|
_log('error: $error');
|
||||||
_setConnectionState(WsConnectionState.disconnected);
|
_setConnectionState(WsConnectionState.disconnected);
|
||||||
_scheduleReconnect();
|
_scheduleReconnect();
|
||||||
});
|
});
|
||||||
|
|
||||||
_socket!.onError((error) {
|
_socket!.onError((error) {
|
||||||
print('[WS] Error: $error');
|
_log('error: $error');
|
||||||
});
|
});
|
||||||
|
|
||||||
// --- Telemetry Events ---
|
// --- Telemetry Events ---
|
||||||
@@ -163,6 +184,7 @@ class WebSocketService {
|
|||||||
final arduino = ArduinoData.fromJson(data);
|
final arduino = ArduinoData.fromJson(data);
|
||||||
_latestArduino = arduino;
|
_latestArduino = arduino;
|
||||||
_arduinoController.add(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);
|
final gps = GpsData.fromJson(data);
|
||||||
_latestGps = gps;
|
_latestGps = gps;
|
||||||
_gpsController.add(gps);
|
_gpsController.add(gps);
|
||||||
|
_log('gps: ${gps.speed?.toStringAsFixed(1) ?? "-"}m/s mode${gps.mode ?? "-"}');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -182,6 +205,7 @@ class WebSocketService {
|
|||||||
);
|
);
|
||||||
_latestStatus = status;
|
_latestStatus = status;
|
||||||
_statusController.add(status);
|
_statusController.add(status);
|
||||||
|
_log('status: gps=${status.gpsConnected} ard=${status.arduinoConnected}');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -196,6 +220,7 @@ class WebSocketService {
|
|||||||
extra: data['extra'],
|
extra: data['extra'],
|
||||||
);
|
);
|
||||||
_ackController.add(ack);
|
_ackController.add(ack);
|
||||||
|
_log('ack: ${ack.id}=${ack.status}${ack.error != null ? " err:${ack.error}" : ""}');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -206,6 +231,7 @@ class WebSocketService {
|
|||||||
message: data['message'] ?? '',
|
message: data['message'] ?? '',
|
||||||
);
|
);
|
||||||
_alertController.add(alert);
|
_alertController.add(alert);
|
||||||
|
_log('alert: [${alert.type}] ${alert.message}');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -283,5 +309,6 @@ class WebSocketService {
|
|||||||
_ackController.close();
|
_ackController.close();
|
||||||
_alertController.close();
|
_alertController.close();
|
||||||
_connectionController.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