hondavert-dev/lib/core/datalog/datalog_recorder.dart
HVBT Dev 332dd6209e v1.3.0 — Phase 3: SQLite datalog (DB + recorder + player + providers)
- datalog_session.dart: DatalogSession model with toMap/fromMap, duration helpers
- datalog_db.dart: SQLite schema (sessions + frames tables), batch insertFrames,
  getSessions, getFrames, deleteSession, closeSession
- datalog_recorder.dart: listens to raw frame stream, buffers PendingFrames,
  flushes to DB every 500ms via Timer, finalises session on stop()
- datalog_player.dart: loads StoredFrames from DB, replays at original timing
  adjusted by speed multiplier (0.25x–8x), play/pause/stop/seek, onProgress cb
- datalog_provider.dart: datalogDbProvider, datalogRecorderProvider,
  datalogPlayerProvider singletons; RecordingNotifier (idle/recording state);
  sessionListProvider FutureProvider; PlaybackNotifier with full playback state
- pubspec.yaml: added path ^1.9.0 dependency
2026-04-13 20:42:26 +05:30

95 lines
2.2 KiB
Dart

import 'dart:async';
import 'dart:typed_data';
import 'datalog_db.dart';
import '../models/datalog_session.dart';
/// Listens to a raw frame stream and batches writes to SQLite every 500ms.
class DatalogRecorder {
final DatalogDb _db;
int? _sessionId;
String? _ecuType;
DateTime? _startTime;
int _frameCount = 0;
final List<PendingFrame> _batch = [];
Timer? _flushTimer;
StreamSubscription<Uint8List>? _frameSub;
bool get isRecording => _sessionId != null;
int get frameCount => _frameCount;
int? get sessionId => _sessionId;
DatalogRecorder(this._db);
/// Start a new recording session, listening to [frameStream].
Future<void> start(Stream<Uint8List> frameStream, String ecuType) async {
if (isRecording) await stop();
_ecuType = ecuType;
_startTime = DateTime.now();
_frameCount = 0;
_batch.clear();
final session = DatalogSession(
ecuType: ecuType,
startTime: _startTime!,
);
_sessionId = await _db.insertSession(session);
// Collect frames into batch buffer
_frameSub = frameStream.listen((Uint8List frame) {
_batch.add(PendingFrame(DateTime.now(), frame));
_frameCount++;
});
// Flush to DB every 500ms
_flushTimer = Timer.periodic(const Duration(milliseconds: 500), (_) {
_flush();
});
}
/// Stop recording and finalise the session row.
Future<DatalogSession?> stop() async {
if (!isRecording) return null;
_flushTimer?.cancel();
_flushTimer = null;
await _frameSub?.cancel();
_frameSub = null;
await _flush(); // write any remaining buffered frames
final endTime = DateTime.now();
await _db.closeSession(_sessionId!, endTime, _frameCount);
final finished = DatalogSession(
id: _sessionId,
ecuType: _ecuType!,
startTime: _startTime!,
endTime: endTime,
frameCount: _frameCount,
);
_sessionId = null;
_ecuType = null;
_startTime = null;
_frameCount = 0;
return finished;
}
Future<void> _flush() async {
if (_batch.isEmpty || _sessionId == null) return;
final toWrite = List<PendingFrame>.from(_batch);
_batch.clear();
await _db.insertFrames(_sessionId!, toWrite);
}
void dispose() {
_flushTimer?.cancel();
_frameSub?.cancel();
}
}