Checked against the USB tool on the bench (3210 rpm): - O2: raw x 5/256 V (raw 195 -> 3.81 V, as the USB tool shows). - New derived sensor: injector duty = INJ ms x RPM / 1200 (16.67 ms @ 3210 rpm -> 44 %, as the USB tool shows). - Default dashboard flags: MIL, Fuel Pump, FAN Out, VTEC Out (the requirement's "FuelCut" is the fuel pump flag on the S300). - Log one raw frame (bytes 0-63) every 5 s so USB-tool readings can be matched to bytes; INJ, IGN, TPS, MIL, AF and PA still need that check. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
108 lines
3.5 KiB
Dart
108 lines
3.5 KiB
Dart
import 'dart:typed_data';
|
||
|
||
import 'package:flutter_test/flutter_test.dart';
|
||
import 'package:hvbt_dash/core/models/sensor_defs.dart';
|
||
import 'package:hvbt_dash/core/models/units.dart';
|
||
import 'package:hvbt_dash/core/protocol/neg8.dart';
|
||
import 'package:hvbt_dash/core/protocol/s300_parser.dart';
|
||
|
||
/// S300 frame with header, the given bytes set, and a valid checksum.
|
||
Uint8List _s300(Map<int, int> bytes) {
|
||
final f = Uint8List(128);
|
||
f[0] = 0x1B;
|
||
bytes.forEach((i, v) => f[i] = v);
|
||
f[127] = calculateNeg8(f.sublist(0, 127));
|
||
return f;
|
||
}
|
||
|
||
void main() {
|
||
group('display units', () {
|
||
test('metric defaults are unchanged', () {
|
||
final v = sensorView('vss', const UnitPrefs());
|
||
expect(v.unit, 'km/h');
|
||
expect(v.convert(100), 100);
|
||
});
|
||
|
||
test('speed in mph', () {
|
||
final v = sensorView('vss', const UnitPrefs(speed: SpeedUnit.mph));
|
||
expect(v.unit, 'mph');
|
||
expect(v.convert(100), closeTo(62.14, 0.01));
|
||
});
|
||
|
||
test('temperatures in °F, range converted too', () {
|
||
final v =
|
||
sensorView('ect', const UnitPrefs(temp: TempUnit.fahrenheit));
|
||
expect(v.unit, '°F');
|
||
expect(v.convert(100), closeTo(212, 0.001));
|
||
expect(v.min, closeTo(-40, 0.001));
|
||
expect(v.max, closeTo(302, 0.001));
|
||
});
|
||
|
||
test('MAP and baro in bar and psi', () {
|
||
final bar = sensorView('map', const UnitPrefs(pressure: PressureUnit.bar));
|
||
expect(bar.unit, 'bar');
|
||
expect(bar.format(101.3), '1.01');
|
||
final psi = sensorView('pa', const UnitPrefs(pressure: PressureUnit.psi));
|
||
expect(psi.unit, 'psi');
|
||
expect(psi.convert(100), closeTo(14.50, 0.01));
|
||
});
|
||
|
||
test('unit choice does not touch unrelated sensors', () {
|
||
const u = UnitPrefs(
|
||
speed: SpeedUnit.mph,
|
||
temp: TempUnit.fahrenheit,
|
||
pressure: PressureUnit.psi,
|
||
);
|
||
expect(sensorView('rpm', u).convert(3000), 3000);
|
||
expect(sensorView('bat', u).unit, 'V');
|
||
});
|
||
});
|
||
|
||
group('values checked against the USB tool (bench, 3210 rpm)', () {
|
||
test('O2 volts = raw × 5/256 (raw 195 → 3.81 V)', () {
|
||
expect(parseS300(_s300({16: 195})).o2, closeTo(3.81, 0.005));
|
||
});
|
||
|
||
test('injector duty = INJ ms × RPM / 1200 (16.67 ms @ 3210 → 44 %)', () {
|
||
final s = parseS300(_s300({})).copyWith(inj: 16.67, rpm: 3210);
|
||
expect(sensorValue(s, 'duty'), closeTo(44.6, 0.1));
|
||
});
|
||
});
|
||
|
||
group('S300 flags', () {
|
||
test('SW3 cut/launch bits (byte 19)', () {
|
||
final s = parseS300(_s300({19: 0x20 | 0x08}));
|
||
expect(s.ignCut, isTrue);
|
||
expect(s.boostCut, isTrue);
|
||
expect(s.shiftCut, isFalse);
|
||
expect(s.launch, isFalse);
|
||
});
|
||
|
||
test('SW1 brake/AC/VTP (byte 17), SW5 closed loop (byte 49)', () {
|
||
final s = parseS300(_s300({17: 0x04 | 0x02 | 0x01, 49: 0x04}));
|
||
expect(s.brakeSwitch, isTrue);
|
||
expect(s.acSwitch, isTrue);
|
||
expect(s.vtp, isTrue);
|
||
expect(s.closedLoop, isTrue);
|
||
});
|
||
|
||
test('IAT / CLT / WBO error flags come from stored DTC bits', () {
|
||
// ERR01 bit1 = P0113 IAT, ERR00 bit5 = ECT, ERR00 bit0 = O2A
|
||
final s = parseS300(_s300({38: 0x02, 37: 0x20 | 0x01}));
|
||
expect(s.iatError, isTrue);
|
||
expect(s.ectError, isTrue);
|
||
expect(s.o2Error, isTrue);
|
||
final clean = parseS300(_s300({}));
|
||
expect(clean.iatError || clean.ectError || clean.o2Error, isFalse);
|
||
});
|
||
|
||
test('every flag id resolves and reads a value', () {
|
||
final s = parseS300(_s300({}));
|
||
for (final f in flagDefs) {
|
||
expect(flagDef(f.id).id, f.id);
|
||
expect(f.read(s), isA<bool>());
|
||
}
|
||
});
|
||
});
|
||
}
|