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

129 lines
3.8 KiB
Dart

import 'package:flutter/material.dart';
import '../../../../core/models/sensor_defs.dart';
import '../../../../core/protocol/sensor_state.dart';
class SensorTile extends StatelessWidget {
final int slotIndex;
final String sensorId;
final SensorState state;
final Color accentColor;
final VoidCallback onTap;
const SensorTile({
super.key,
required this.slotIndex,
required this.sensorId,
required this.state,
required this.accentColor,
required this.onTap,
});
@override
Widget build(BuildContext context) {
final def = sensorDefs.firstWhere(
(d) => d.id == sensorId,
orElse: () => sensorDefs.first,
);
final value = _getValue(state, sensorId);
final normalized =
((value - def.min) / (def.max - def.min)).clamp(0.0, 1.0);
return GestureDetector(
onLongPress: onTap,
child: Container(
decoration: BoxDecoration(
color: const Color(0xFF1A1A1A),
borderRadius: BorderRadius.circular(10),
border: Border.all(color: Colors.white10),
),
padding: const EdgeInsets.fromLTRB(10, 8, 10, 6),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
def.displayName.toUpperCase(),
style: const TextStyle(
color: Colors.grey,
fontSize: 10,
letterSpacing: 1,
),
),
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
_format(value, sensorId),
style: const TextStyle(
color: Colors.white,
fontSize: 22,
fontWeight: FontWeight.bold,
letterSpacing: -0.5,
),
),
const SizedBox(width: 3),
Padding(
padding: const EdgeInsets.only(bottom: 2),
child: Text(
def.unit,
style: TextStyle(color: accentColor, fontSize: 11),
),
),
],
),
// Bar gauge
ClipRRect(
borderRadius: BorderRadius.circular(2),
child: LinearProgressIndicator(
value: normalized,
minHeight: 4,
backgroundColor: Colors.white10,
valueColor: AlwaysStoppedAnimation<Color>(
normalized > 0.8 ? Colors.red : accentColor,
),
),
),
],
),
),
);
}
String _format(double v, String id) {
if (id == 'gear') return v.toStringAsFixed(0);
if (id == 'rpm') return v.toStringAsFixed(0);
if (v.abs() >= 100) return v.toStringAsFixed(1);
return v.toStringAsFixed(2);
}
}
double _getValue(SensorState s, String id) {
switch (id) {
case 'rpm': return s.rpm;
case 'vss': return s.vss;
case 'map': return s.map;
case 'tps': return s.tps;
case 'inj': return s.inj;
case 'ign': return s.ign;
case 'ect': return s.ect;
case 'iat': return s.iat;
case 'bat': return s.bat;
case 'o2': return s.o2;
case 'gear': return s.gear.toDouble();
case 'eth': return s.eth;
case 'pa': return s.pa;
case 'afr': return s.afr;
case 'strim': return s.strim;
case 'ltrim': return s.ltrim;
case 'ain0': return s.ain0;
case 'ain1': return s.ain1;
case 'ain2': return s.ain2;
case 'ain3': return s.ain3;
case 'ain4': return s.ain4;
case 'ain5': return s.ain5;
case 'ain6': return s.ain6;
case 'ain7': return s.ain7;
default: return 0;
}
}