hondavert-dev/lib/ui/widgets/main_nav.dart
metatroncubeswdev 9a56baef61 Rework BT poller and S300 decoding after client feedback
Client reported crashes, wrong/frozen data and that the app floods the
adapter MCU. See docs/FIX_2026-10-12.md for the full analysis.

- Poller: strictly one request outstanding; next request only after a valid
  128-byte reply or a 500 ms timeout, then a quiet period + buffer flush.
  Default 100 ms (10 Hz), minimum 80 ms. DTC clear/probe share the same lock.
- BtNotifier: filter the adapter's default (no-ECU-data) frame instead of
  holding last values; EcuDataStatus + status banner; auto-reconnect on link
  loss, stalls or long default-only runs (rate-limited). A reconnect still in
  flight when the user disconnects is discarded.
- S300 offsets corrected by column counting (trims, PA, ERR00-03, Eth, SW5,
  AF); INJ/AIN little-endian; fan = SW5 FANC only. Default-frame detection
  requires bytes 0-16 and 32-36.
- KPro RPM/MAP2/AIN little-endian (RPM still needs a bench check).
- Crash hardening: zone/Flutter/platform error handlers to an in-app log
  (copyable from Debug); permissions requested before listing devices.
- Remove temporary debug prints; graph redraws live; polling options
  100-300 ms, persisted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 21:02:54 -04:00

101 lines
2.8 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../screens/dashboard/dashboard_screen.dart';
import '../screens/sensor_list/sensor_list_screen.dart';
import '../screens/graph/graph_screen.dart';
import '../screens/dtc/dtc_screen.dart';
import '../screens/datalog/datalog_list_screen.dart';
import '../screens/debug/raw_frame_debug_screen.dart';
import '../screens/settings/settings_screen.dart';
import 'bt_status_chip.dart';
import 'data_status_banner.dart';
import 'record_fab.dart';
final _navIndexProvider = StateProvider<int>((ref) => 0);
class MainNav extends ConsumerWidget {
const MainNav({super.key});
static const _screens = [
DashboardScreen(),
SensorListScreen(),
GraphScreen(),
DtcScreen(),
];
static const _items = [
BottomNavigationBarItem(
icon: Icon(Icons.speed),
label: 'Dashboard',
),
BottomNavigationBarItem(
icon: Icon(Icons.list_alt),
label: 'Sensors',
),
BottomNavigationBarItem(
icon: Icon(Icons.show_chart),
label: 'Graph',
),
BottomNavigationBarItem(
icon: Icon(Icons.warning_amber_outlined),
label: 'DTC',
),
];
@override
Widget build(BuildContext context, WidgetRef ref) {
final index = ref.watch(_navIndexProvider);
return Scaffold(
appBar: AppBar(
title: const Text('HV BT Dashboard'),
actions: [
const BtStatusChip(),
IconButton(
icon: const Icon(Icons.folder_open_outlined),
tooltip: 'Saved Sessions',
onPressed: () => Navigator.push(
context,
MaterialPageRoute(builder: (_) => const DatalogListScreen()),
),
),
IconButton(
icon: const Icon(Icons.bug_report_outlined),
tooltip: 'ECU Frame Debug',
onPressed: () => Navigator.push(
context,
MaterialPageRoute(builder: (_) => const RawFrameDebugScreen()),
),
),
IconButton(
icon: const Icon(Icons.settings_outlined),
tooltip: 'Settings',
onPressed: () => Navigator.push(
context,
MaterialPageRoute(builder: (_) => const SettingsScreen()),
),
),
],
),
body: Column(
children: [
const DataStatusBanner(),
Expanded(
child: IndexedStack(
index: index,
children: _screens,
),
),
],
),
bottomNavigationBar: BottomNavigationBar(
currentIndex: index,
items: _items,
onTap: (i) => ref.read(_navIndexProvider.notifier).state = i,
),
floatingActionButton: const RecordFab(),
);
}
}