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>
101 lines
2.8 KiB
Dart
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(),
|
|
);
|
|
}
|
|
}
|