174 lines
4.6 KiB
Dart
174 lines
4.6 KiB
Dart
import 'dart:async';
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import '../../database/database.dart';
|
|
import 'package:drift/drift.dart' as drift;
|
|
import 'package:geolocator/geolocator.dart';
|
|
|
|
enum RideState { idle, ready, riding, paused, finished }
|
|
|
|
class RideSession {
|
|
final int? dbId;
|
|
final RideState state;
|
|
final double tripDistanceMiles;
|
|
final double topSpeedMph;
|
|
final double maxLeanLeft;
|
|
final double maxLeanRight;
|
|
final double maxAccelG;
|
|
final double maxBrakingG;
|
|
final DateTime? startTime;
|
|
|
|
RideSession({
|
|
this.dbId,
|
|
this.state = RideState.idle,
|
|
this.tripDistanceMiles = 0.0,
|
|
this.topSpeedMph = 0.0,
|
|
this.maxLeanLeft = 0.0,
|
|
this.maxLeanRight = 0.0,
|
|
this.maxAccelG = 0.0,
|
|
this.maxBrakingG = 0.0,
|
|
this.startTime,
|
|
});
|
|
|
|
RideSession copyWith({
|
|
int? dbId,
|
|
RideState? state,
|
|
double? tripDistanceMiles,
|
|
double? topSpeedMph,
|
|
double? maxLeanLeft,
|
|
double? maxLeanRight,
|
|
double? maxAccelG,
|
|
double? maxBrakingG,
|
|
DateTime? startTime,
|
|
}) {
|
|
return RideSession(
|
|
dbId: dbId ?? this.dbId,
|
|
state: state ?? this.state,
|
|
tripDistanceMiles: tripDistanceMiles ?? this.tripDistanceMiles,
|
|
topSpeedMph: topSpeedMph ?? this.topSpeedMph,
|
|
maxLeanLeft: maxLeanLeft ?? this.maxLeanLeft,
|
|
maxLeanRight: maxLeanRight ?? this.maxLeanRight,
|
|
maxAccelG: maxAccelG ?? this.maxAccelG,
|
|
maxBrakingG: maxBrakingG ?? this.maxBrakingG,
|
|
startTime: startTime ?? this.startTime,
|
|
);
|
|
}
|
|
}
|
|
|
|
class RideStateMachine extends StateNotifier<RideSession> {
|
|
final AppDatabase db;
|
|
Position? _lastPosition;
|
|
|
|
RideStateMachine(this.db) : super(RideSession());
|
|
|
|
void setReady() {
|
|
state = state.copyWith(state: RideState.ready);
|
|
}
|
|
|
|
Future<void> startRide() async {
|
|
final id = await db.startRide();
|
|
state = state.copyWith(
|
|
dbId: id,
|
|
state: RideState.riding,
|
|
startTime: DateTime.now(),
|
|
tripDistanceMiles: 0,
|
|
topSpeedMph: 0,
|
|
);
|
|
_lastPosition = null;
|
|
}
|
|
|
|
void pauseRide() {
|
|
state = state.copyWith(state: RideState.paused);
|
|
}
|
|
|
|
void resumeRide() {
|
|
state = state.copyWith(state: RideState.riding);
|
|
_lastPosition = null; // reset so we don't jump distance
|
|
}
|
|
|
|
Future<void> finishRide() async {
|
|
if (state.dbId != null) {
|
|
await db.endRide(
|
|
state.dbId!,
|
|
RidesCompanion(
|
|
distanceMiles: drift.Value(state.tripDistanceMiles),
|
|
topSpeedMph: drift.Value(state.topSpeedMph),
|
|
maxLeanLeft: drift.Value(state.maxLeanLeft),
|
|
maxLeanRight: drift.Value(state.maxLeanRight),
|
|
maxAccelG: drift.Value(state.maxAccelG),
|
|
maxBrakingG: drift.Value(state.maxBrakingG),
|
|
),
|
|
);
|
|
}
|
|
state = state.copyWith(state: RideState.finished);
|
|
}
|
|
|
|
void reset() {
|
|
state = RideSession();
|
|
}
|
|
|
|
void updateTelemetry({
|
|
Position? position,
|
|
double? speedMph,
|
|
double? leanAngle,
|
|
double? accelG,
|
|
double? lateralG,
|
|
}) {
|
|
if (state.state != RideState.riding) return;
|
|
|
|
double newTopSpeed = state.topSpeedMph;
|
|
double newMaxLeanLeft = state.maxLeanLeft;
|
|
double newMaxLeanRight = state.maxLeanRight;
|
|
double newMaxAccelG = state.maxAccelG;
|
|
double newMaxBrakingG = state.maxBrakingG;
|
|
double newDistance = state.tripDistanceMiles;
|
|
|
|
if (speedMph != null && speedMph > state.topSpeedMph) {
|
|
newTopSpeed = speedMph;
|
|
}
|
|
|
|
if (leanAngle != null) {
|
|
if (leanAngle < 0 && leanAngle.abs() > state.maxLeanLeft) {
|
|
newMaxLeanLeft = leanAngle.abs();
|
|
} else if (leanAngle > 0 && leanAngle > state.maxLeanRight) {
|
|
newMaxLeanRight = leanAngle;
|
|
}
|
|
}
|
|
|
|
if (accelG != null) {
|
|
if (accelG > 0 && accelG > state.maxAccelG) {
|
|
newMaxAccelG = accelG;
|
|
} else if (accelG < 0 && accelG.abs() > state.maxBrakingG) {
|
|
newMaxBrakingG = accelG.abs();
|
|
}
|
|
}
|
|
|
|
if (position != null) {
|
|
if (_lastPosition != null) {
|
|
// Calculate distance in meters
|
|
final distMeters = Geolocator.distanceBetween(
|
|
_lastPosition!.latitude,
|
|
_lastPosition!.longitude,
|
|
position.latitude,
|
|
position.longitude,
|
|
);
|
|
newDistance += distMeters / 1609.34; // convert to miles
|
|
}
|
|
_lastPosition = position;
|
|
}
|
|
|
|
state = state.copyWith(
|
|
topSpeedMph: newTopSpeed,
|
|
maxLeanLeft: newMaxLeanLeft,
|
|
maxLeanRight: newMaxLeanRight,
|
|
maxAccelG: newMaxAccelG,
|
|
maxBrakingG: newMaxBrakingG,
|
|
tripDistanceMiles: newDistance,
|
|
);
|
|
}
|
|
}
|
|
|
|
final rideStateMachineProvider = StateNotifierProvider<RideStateMachine, RideSession>((ref) {
|
|
final db = ref.watch(databaseProvider);
|
|
return RideStateMachine(db);
|
|
});
|