Dashboard - Selectable main sensor (gauge) + sub sensor, 10 selectable values (landscape 5 left + 5 right, portrait 2x5 on one screen) and 4 selectable flags; long-press to change; persisted. - Flags: the requirement examples (MIL, FAN Out, VTEC Out, Knock, WBO / IAT / CLT Error - errors derived from the stored DTC bits) plus the other S300 status bits (cuts, launch, closed loop, VTP, AC, brake). - Record button moved to the app bar; debug screen moved to Settings. Sensor list: 1 column on phone portrait, 2 on tablet/landscape; name, unit, min/max, segmented bar and value. Graph: auto-scaled Y, window max/min, current value, seconds axis and a touch cursor; RPM/MAP/TPS/ECT by default. Units: km/h|mph, degC|degF, kPa|bar|psi (display only), persisted. Light/dark: palette is a ThemeExtension used by every screen; theme persisted. SafeArea keeps content off the side navigation bar. Datalog playback now drives the dashboard, list, graph and DTC screen, with the playback bar on the main screen and seek updating the displays. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
132 lines
5.4 KiB
Dart
132 lines
5.4 KiB
Dart
import '../protocol/sensor_state.dart';
|
|
import 'sensor_def.dart';
|
|
import 'flag_def.dart';
|
|
|
|
/// Master list of all analog sensor definitions. Units, ranges and decimals
|
|
/// are the base (metric) ones; see units.dart for the displayed unit.
|
|
const List<SensorDef> sensorDefs = [
|
|
SensorDef(
|
|
id: 'rpm', displayName: 'RPM', unit: 'rpm', min: 0, max: 9000, decimals: 0),
|
|
SensorDef(
|
|
id: 'vss', displayName: 'Speed', unit: 'km/h', min: 0, max: 280, decimals: 0),
|
|
SensorDef(
|
|
id: 'map', displayName: 'Manifold pressure', unit: 'kPa', min: 0, max: 300),
|
|
SensorDef(
|
|
id: 'tps', displayName: 'Throttle position', unit: '%', min: 0, max: 100),
|
|
SensorDef(
|
|
id: 'inj',
|
|
displayName: 'Injector duration',
|
|
unit: 'ms',
|
|
min: 0,
|
|
max: 20,
|
|
decimals: 2),
|
|
SensorDef(
|
|
id: 'ign', displayName: 'Timing advance', unit: '°', min: -20, max: 60),
|
|
SensorDef(
|
|
id: 'ect', displayName: 'Coolant temp', unit: '°C', min: -40, max: 150),
|
|
SensorDef(
|
|
id: 'iat',
|
|
displayName: 'Intake air temp',
|
|
unit: '°C',
|
|
min: -40,
|
|
max: 150),
|
|
SensorDef(
|
|
id: 'bat', displayName: 'Battery voltage', unit: 'V', min: 0, max: 20),
|
|
// O2/AFR/AIN scales not yet bench-verified: shown as raw values.
|
|
SensorDef(
|
|
id: 'o2', displayName: 'O2 sensor', unit: 'raw', min: 0, max: 255, decimals: 0),
|
|
SensorDef(
|
|
id: 'gear', displayName: 'Gear', unit: '', min: 0, max: 6, decimals: 0),
|
|
SensorDef(
|
|
id: 'eth', displayName: 'Ethanol', unit: '%', min: 0, max: 100, decimals: 0),
|
|
SensorDef(
|
|
id: 'pa', displayName: 'Barometric pressure', unit: 'kPa', min: 0, max: 120),
|
|
SensorDef(
|
|
id: 'afr', displayName: 'AFR', unit: 'raw', min: 0, max: 255, decimals: 0),
|
|
SensorDef(
|
|
id: 'strim', displayName: 'Short trim', unit: '%', min: -30, max: 30),
|
|
SensorDef(
|
|
id: 'ltrim', displayName: 'Long trim', unit: '%', min: -30, max: 30),
|
|
SensorDef(
|
|
id: 'ain0', displayName: 'Analog input 0', unit: 'raw', min: 0, max: 1023, decimals: 0),
|
|
SensorDef(
|
|
id: 'ain1', displayName: 'Analog input 1', unit: 'raw', min: 0, max: 1023, decimals: 0),
|
|
SensorDef(
|
|
id: 'ain2', displayName: 'Analog input 2', unit: 'raw', min: 0, max: 1023, decimals: 0),
|
|
SensorDef(
|
|
id: 'ain3', displayName: 'Analog input 3', unit: 'raw', min: 0, max: 1023, decimals: 0),
|
|
SensorDef(
|
|
id: 'ain4', displayName: 'Analog input 4', unit: 'raw', min: 0, max: 1023, decimals: 0),
|
|
SensorDef(
|
|
id: 'ain5', displayName: 'Analog input 5', unit: 'raw', min: 0, max: 1023, decimals: 0),
|
|
SensorDef(
|
|
id: 'ain6', displayName: 'Analog input 6', unit: 'raw', min: 0, max: 1023, decimals: 0),
|
|
SensorDef(
|
|
id: 'ain7', displayName: 'Analog input 7', unit: 'raw', min: 0, max: 1023, decimals: 0),
|
|
];
|
|
|
|
SensorDef sensorDef(String id) =>
|
|
sensorDefs.firstWhere((d) => d.id == id, orElse: () => sensorDefs.first);
|
|
|
|
/// Base-unit value of sensor [id] in [s].
|
|
double sensorValue(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;
|
|
}
|
|
}
|
|
|
|
/// Master list of boolean flags (selectable on the dashboard). The first
|
|
/// eight follow the requirement document's examples; the rest are the other
|
|
/// status bits the ECU frame carries.
|
|
final List<FlagDef> flagDefs = [
|
|
FlagDef(id: 'mil', displayName: 'MIL', read: (s) => s.mil),
|
|
FlagDef(id: 'fanout', displayName: 'FAN Out', read: (s) => s.fanOut),
|
|
FlagDef(id: 'vtec', displayName: 'VTEC Out', read: (s) => s.vtec),
|
|
FlagDef(id: 'knock', displayName: 'Knock', read: (s) => s.knock),
|
|
FlagDef(id: 'o2error', displayName: 'WBO Error', read: (s) => s.o2Error),
|
|
FlagDef(id: 'iaterror', displayName: 'IAT Error', read: (s) => s.iatError),
|
|
FlagDef(id: 'ecterror', displayName: 'CLT Error', read: (s) => s.ectError),
|
|
FlagDef(id: 'fuelpump', displayName: 'Fuel Pump', read: (s) => s.fuelPump),
|
|
FlagDef(id: 'revlimit', displayName: 'Rev Limit', read: (s) => s.revLimit),
|
|
FlagDef(id: 'igncut', displayName: 'Ign Cut', read: (s) => s.ignCut),
|
|
FlagDef(id: 'boostcut', displayName: 'Boost Cut', read: (s) => s.boostCut),
|
|
FlagDef(id: 'launch', displayName: 'Launch Cut', read: (s) => s.launch),
|
|
FlagDef(
|
|
id: 'launchretard',
|
|
displayName: 'Launch Retard',
|
|
read: (s) => s.launchRetard),
|
|
FlagDef(id: 'shiftcut', displayName: 'Shift Cut', read: (s) => s.shiftCut),
|
|
FlagDef(
|
|
id: 'closedloop', displayName: 'Closed Loop', read: (s) => s.closedLoop),
|
|
FlagDef(id: 'vtp', displayName: 'VTEC Press', read: (s) => s.vtp),
|
|
FlagDef(id: 'acswitch', displayName: 'AC Switch', read: (s) => s.acSwitch),
|
|
FlagDef(id: 'brake', displayName: 'Brake', read: (s) => s.brakeSwitch),
|
|
];
|
|
|
|
FlagDef flagDef(String id) =>
|
|
flagDefs.firstWhere((f) => f.id == id, orElse: () => flagDefs.first);
|