hondavert-dev/lib/core/providers/bt_provider.dart
HVBT Dev 763821171c feat: Add ECU Frame Debug Screen and DTC Screen
- Implemented RawFrameDebugScreen for displaying ECU frame data and diagnostics.
- Created DtcScreen to show active Diagnostic Trouble Codes (DTCs) with detailed descriptions.
- Added DtcRow widget for individual DTC representation.
- Introduced GraphScreen for visualizing sensor data over time with customizable time windows.
- Developed SensorListScreen to display available sensors and their current values.
- Enhanced SettingsScreen with options for Bluetooth connection, ECU protocol selection, polling interval, and theme settings.
- Added AppColors and AppTheme for consistent theming across the application.
- Implemented BtStatusChip to show Bluetooth connection status in the app bar.
- Created RecordFab for starting and stopping data recording sessions.
2026-07-26 23:14:58 +05:30

150 lines
4.7 KiB
Dart

import 'dart:async';
import 'dart:typed_data';
import 'package:flutter_bluetooth_serial/flutter_bluetooth_serial.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../bluetooth/bt_poller.dart';
import '../bluetooth/bt_service.dart';
import 'settings_provider.dart';
// ─── Connection status enum ──────────────────────────────────────────────────
enum BtStatus { disconnected, connecting, connected, error }
// ─── State ───────────────────────────────────────────────────────────────────
class BtState {
final BtStatus status;
final String? deviceName;
final String? error;
final int frameCount;
final int droppedFrames;
final Uint8List? lastFrame;
const BtState({
required this.status,
this.deviceName,
this.error,
this.frameCount = 0,
this.droppedFrames = 0,
this.lastFrame,
});
const BtState.disconnected() : this(status: BtStatus.disconnected);
BtState connecting(String name) =>
BtState(status: BtStatus.connecting, deviceName: name);
BtState connected(String name) =>
BtState(status: BtStatus.connected, deviceName: name);
BtState withError(String msg) => BtState(
status: BtStatus.error,
deviceName: deviceName,
error: msg,
frameCount: frameCount,
droppedFrames: droppedFrames,
lastFrame: lastFrame,
);
BtState withFrameStats(int frames, int dropped, Uint8List frame) => BtState(
status: status,
deviceName: deviceName,
frameCount: frames,
droppedFrames: dropped,
lastFrame: Uint8List.fromList(frame),
);
bool get isConnected => status == BtStatus.connected;
bool get isConnecting => status == BtStatus.connecting;
}
// ─── Notifier ────────────────────────────────────────────────────────────────
class BtNotifier extends StateNotifier<BtState> {
final Ref _ref;
BtPoller? _poller;
StreamSubscription<Uint8List>? _pollSub;
final StreamController<Uint8List> _frameController =
StreamController<Uint8List>.broadcast();
BtNotifier(this._ref) : super(const BtState.disconnected());
/// Raw validated 128-byte frame stream — consumed by sensorStateProvider.
Stream<Uint8List> get frameStream => _frameController.stream;
Future<void> connect(BluetoothDevice device) async {
state = state.connecting(device.name ?? device.address);
final service = _ref.read(btServiceProvider);
final settings = _ref.read(settingsProvider);
try {
await service.connect(device);
_poller = BtPoller(
service,
ecuType: settings.ecuType,
pollingInterval: settings.pollingInterval,
);
_pollSub = _poller!.frameStream.listen(
(frame) {
_frameController.add(frame);
state = state.withFrameStats(
_poller!.validFrames,
_poller!.droppedFrames,
frame,
);
},
onError: (Object e) {
_frameController.addError(e);
state = state.withError(e.toString());
},
onDone: () => state = state.withError('BT stream closed'),
);
_poller!.start();
state = state.connected(device.name ?? device.address);
} catch (e) {
await service.disconnect();
state = state.withError(e.toString());
}
}
Future<void> disconnect() async {
await _pollSub?.cancel();
_poller?.stop();
_poller?.dispose();
_poller = null;
await _ref.read(btServiceProvider).disconnect();
state = const BtState.disconnected();
}
@override
void dispose() {
_pollSub?.cancel();
_poller?.dispose();
_frameController.close();
super.dispose();
}
}
// ─── Providers ───────────────────────────────────────────────────────────────
/// Singleton BtService — lives as long as the app.
final btServiceProvider = Provider<BtService>((ref) {
final service = BtService();
ref.onDispose(service.dispose);
return service;
});
/// BT connection state notifier.
final btProvider =
StateNotifierProvider<BtNotifier, BtState>((ref) => BtNotifier(ref));
/// Paired BT devices list (re-fetchable via ref.refresh).
final pairedDevicesProvider = FutureProvider<List<BluetoothDevice>>((ref) {
return FlutterBluetoothSerial.instance.getBondedDevices();
});