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>
54 lines
1.8 KiB
Dart
54 lines
1.8 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import '../../core/providers/bt_provider.dart';
|
|
|
|
/// Thin banner under the app bar that tells the driver when the numbers on
|
|
/// screen are NOT live, instead of silently showing stale or default values.
|
|
class DataStatusBanner extends ConsumerWidget {
|
|
const DataStatusBanner({super.key});
|
|
|
|
@override
|
|
Widget build(BuildContext context, WidgetRef ref) {
|
|
final status = ref.watch(btProvider.select((s) => s.status));
|
|
final data = ref.watch(btProvider.select((s) => s.dataStatus));
|
|
final error = ref.watch(btProvider.select((s) => s.error));
|
|
|
|
final String? msg;
|
|
Color color = const Color(0xFF8A5A00);
|
|
if (status == BtStatus.reconnecting) {
|
|
msg = 'Connection interrupted — reconnecting…';
|
|
} else if (status == BtStatus.error) {
|
|
msg = error ?? 'Connection lost';
|
|
color = const Color(0xFF7A0000);
|
|
} else if (status == BtStatus.disconnected) {
|
|
msg = 'Not connected';
|
|
color = const Color(0xFF444444);
|
|
} else if (status != BtStatus.connected) {
|
|
msg = null;
|
|
} else {
|
|
msg = switch (data) {
|
|
EcuDataStatus.live => null,
|
|
EcuDataStatus.none => 'Connected — waiting for data…',
|
|
EcuDataStatus.adapterDefaults =>
|
|
'Adapter connected, but no data from the ECU (ignition on?)',
|
|
EcuDataStatus.noResponse => 'Adapter is not responding',
|
|
};
|
|
}
|
|
|
|
if (msg == null) return const SizedBox.shrink();
|
|
return Material(
|
|
color: color,
|
|
child: SizedBox(
|
|
width: double.infinity,
|
|
child: Padding(
|
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
|
child: Text(
|
|
msg,
|
|
style: const TextStyle(color: Colors.white, fontSize: 12),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|