feat: new shifter types and better gear ratio editor
This commit is contained in:
@ -20,6 +20,8 @@ class ShifterService {
|
||||
Stream<CentralStatus> get statusStream => _statusController.stream;
|
||||
|
||||
static const int _gearRatioSlots = 32;
|
||||
static const int _defaultGearIndexOffset = _gearRatioSlots;
|
||||
static const int _gearRatioPayloadBytes = _gearRatioSlots + 1;
|
||||
static const double _maxGearRatio = 255 / 64;
|
||||
static const int _gearRatioWriteMtu = 64;
|
||||
|
||||
@ -56,7 +58,7 @@ class ShifterService {
|
||||
return writeCommand(UniversalShifterCommand.connectToDevice);
|
||||
}
|
||||
|
||||
Future<Result<List<double>>> readGearRatios() async {
|
||||
Future<Result<GearRatiosData>> readGearRatios() async {
|
||||
final readRes = await _bluetooth.readCharacteristic(
|
||||
buttonDeviceId,
|
||||
universalShifterControlServiceUuid,
|
||||
@ -67,25 +69,43 @@ class ShifterService {
|
||||
}
|
||||
|
||||
final raw = readRes.unwrap();
|
||||
if (raw.length > _gearRatioSlots) {
|
||||
if (raw.length > _gearRatioPayloadBytes) {
|
||||
return bail(
|
||||
'Invalid gear ratio payload length: expected at most $_gearRatioSlots, got ${raw.length}',
|
||||
'Invalid gear ratio payload length: expected at most $_gearRatioPayloadBytes, got ${raw.length}',
|
||||
);
|
||||
}
|
||||
|
||||
final normalizedRaw = List<int>.filled(_gearRatioSlots, 0, growable: false);
|
||||
final normalizedRaw = List<int>.filled(
|
||||
_gearRatioPayloadBytes,
|
||||
0,
|
||||
growable: false,
|
||||
);
|
||||
for (var i = 0; i < raw.length; i++) {
|
||||
normalizedRaw[i] = raw[i];
|
||||
}
|
||||
|
||||
final ratios = normalizedRaw
|
||||
.where((v) => v > 0)
|
||||
.map((v) => _decodeGearRatio(v))
|
||||
.toList(growable: false);
|
||||
return Ok(ratios);
|
||||
final ratios = <double>[];
|
||||
for (var i = 0; i < _gearRatioSlots; i++) {
|
||||
final value = normalizedRaw[i];
|
||||
if (value == 0) {
|
||||
break;
|
||||
}
|
||||
ratios.add(_decodeGearRatio(value));
|
||||
}
|
||||
|
||||
final defaultIndexRaw = normalizedRaw[_defaultGearIndexOffset];
|
||||
final defaultGearIndex = ratios.isEmpty
|
||||
? 0
|
||||
: defaultIndexRaw.clamp(0, ratios.length - 1).toInt();
|
||||
return Ok(
|
||||
GearRatiosData(
|
||||
ratios: ratios,
|
||||
defaultGearIndex: defaultGearIndex,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<Result<void>> writeGearRatios(List<double> ratios) async {
|
||||
Future<Result<void>> writeGearRatios(GearRatiosData data) async {
|
||||
final mtuResult =
|
||||
await _bluetooth.requestMtu(buttonDeviceId, mtu: _gearRatioWriteMtu);
|
||||
if (mtuResult.isErr()) {
|
||||
@ -93,12 +113,16 @@ class ShifterService {
|
||||
'Could not request MTU before writing gear ratios: ${mtuResult.unwrapErr()}');
|
||||
}
|
||||
|
||||
final payload = List<int>.filled(_gearRatioSlots, 0, growable: false);
|
||||
final payload =
|
||||
List<int>.filled(_gearRatioPayloadBytes, 0, growable: false);
|
||||
final ratios = data.ratios;
|
||||
final limit =
|
||||
ratios.length < _gearRatioSlots ? ratios.length : _gearRatioSlots;
|
||||
for (var i = 0; i < limit; i++) {
|
||||
payload[i] = _encodeGearRatio(ratios[i]);
|
||||
}
|
||||
payload[_defaultGearIndexOffset] =
|
||||
limit == 0 ? 0 : data.defaultGearIndex.clamp(0, limit - 1).toInt();
|
||||
|
||||
return _bluetooth.writeCharacteristic(
|
||||
buttonDeviceId,
|
||||
@ -177,3 +201,13 @@ class ShifterService {
|
||||
return raw / 64.0;
|
||||
}
|
||||
}
|
||||
|
||||
class GearRatiosData {
|
||||
const GearRatiosData({
|
||||
required this.ratios,
|
||||
required this.defaultGearIndex,
|
||||
});
|
||||
|
||||
final List<double> ratios;
|
||||
final int defaultGearIndex;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user