feat(train): the Loop's live heart rate in a recording (ADR 0030, D41)
A recording with a Loop set up reads its heart rate through Polar's SDK while no strap sends: the strap first, the Loop where there is none or while it is lost. The SDK is one for the process, held by the open app's bridge and by the recording service, so that the band stays connected with the app gone; the app reopening finds it connected. The recording file gains `loop` state lines; the rate is kept alone, as `h` lines, and the Record sheet names the source. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
388b3b1ae6
commit
fa7680821d
3 changed files with 577 additions and 72 deletions
|
|
@ -140,6 +140,24 @@ void main() {
|
|||
expect(liveOf(lost, at(10)).bpm, isNull);
|
||||
});
|
||||
|
||||
test('lost, the Loop sending gives the heart rate in its place (D41)', () {
|
||||
final recording = Recording(
|
||||
activity: Activity.running,
|
||||
startedAt: start,
|
||||
straps: [
|
||||
LinkMark(time: at(0), state: LinkState.connected),
|
||||
LinkMark(time: at(10), state: LinkState.lost),
|
||||
],
|
||||
loops: [LinkMark(time: at(1), state: LinkState.connected)],
|
||||
heartRates: [HeartRate(time: at(12), bpm: 138)],
|
||||
);
|
||||
|
||||
final live = liveOf(recording, at(13));
|
||||
expect(live.strap, LinkState.lost);
|
||||
expect(live.loop, LinkState.connected);
|
||||
expect(live.bpm, 138);
|
||||
});
|
||||
|
||||
test('without one, it has no strap', () {
|
||||
final live = liveOf(withStrap(const [], const []), at(5));
|
||||
|
||||
|
|
|
|||
|
|
@ -54,6 +54,9 @@ void main() {
|
|||
channelName: 'Recording',
|
||||
);
|
||||
|
||||
/// The person's Loop, where they have one (D41).
|
||||
String? loop;
|
||||
|
||||
/// The bloc as the screen opens it.
|
||||
Future<RecordingBloc> open() async {
|
||||
final bloc = RecordingBloc(
|
||||
|
|
@ -64,6 +67,7 @@ void main() {
|
|||
DeviceStore(File('${folder.path}/rower.json')),
|
||||
notice: notice,
|
||||
ticks: ticks.stream,
|
||||
loop: () => loop,
|
||||
)..add(const RecordingEventOpened());
|
||||
opened.add(bloc);
|
||||
await bloc.stream.firstWhere((s) => s.phase != RecordingPhase.loading);
|
||||
|
|
@ -385,6 +389,29 @@ void main() {
|
|||
});
|
||||
});
|
||||
|
||||
group('the Loop (D41)', () {
|
||||
tearDown(() => loop = null);
|
||||
|
||||
test("a recording starts with the person's Loop, strap or not, and "
|
||||
'without one where they have none', () async {
|
||||
final bloc = await open();
|
||||
bloc.add(const RecordingEventStartRequested(Activity.running));
|
||||
await bloc.stream.firstWhere((s) => s.phase == RecordingPhase.recording);
|
||||
bloc.add(const RecordingEventStopRequested());
|
||||
await bloc.stream.firstWhere((s) => s.phase != RecordingPhase.recording);
|
||||
bloc.add(const RecordingEventDiscardRequested());
|
||||
await bloc.stream.firstWhere((s) => s.phase == RecordingPhase.idle);
|
||||
|
||||
loop = 'A1';
|
||||
bloc.add(
|
||||
const RecordingEventStartRequested(Activity.running, useStrap: false),
|
||||
);
|
||||
await bloc.stream.firstWhere((s) => s.phase == RecordingPhase.recording);
|
||||
|
||||
expect(platform.started.map((s) => s.loop), [null, 'A1']);
|
||||
});
|
||||
});
|
||||
|
||||
group('the rower (D28)', () {
|
||||
const rower = KnownDevice(address: 'C4:00:00:00:00:01', name: 'MRK-950R');
|
||||
|
||||
|
|
|
|||
|
|
@ -140,6 +140,25 @@ void main() {
|
|||
expect(recording.heartRates.single.bpm, 128);
|
||||
});
|
||||
|
||||
test("the Loop's state is read beside the heart rates (D41)", () {
|
||||
final recording = readRecording(
|
||||
'$header'
|
||||
'loop ${at(0)} connecting\n'
|
||||
'loop ${at(2)} connected\n'
|
||||
'h ${at(3)} 120\n'
|
||||
'loop ${at(8)} lost\n',
|
||||
)!;
|
||||
|
||||
DateTime time(int seconds) => start.add(Duration(seconds: seconds));
|
||||
expect(recording.loops, [
|
||||
LinkMark(time: time(0), state: LinkState.connecting),
|
||||
LinkMark(time: time(2), state: LinkState.connected),
|
||||
LinkMark(time: time(8), state: LinkState.lost),
|
||||
]);
|
||||
expect(recording.straps, isEmpty);
|
||||
expect(recording.heartRates.single.bpm, 120);
|
||||
});
|
||||
|
||||
test("the rower's frames and state are read (D28)", () {
|
||||
final recording = readRecording(
|
||||
'$header'
|
||||
|
|
|
|||
Loading…
Reference in a new issue