main
1import 'dart:async';
2
3import 'package:blood_pressure_app/features/bluetooth/backend/bluetooth_state.dart';
4import 'package:blood_pressure_app/logging.dart';
5
6/// Generic stream data parser base class
7abstract class StreamDataParser<StreamData, ParsedData> {
8 /// Method to be implemented by backend that converts the raw bluetooth adapter state to our BluetoothState
9 ParsedData parse(StreamData rawState);
10}
11
12/// Generic stream data parser base class that caches the last known state returned by the stream
13abstract class StreamDataParserCached<StreamData, ParsedData> extends StreamDataParser<StreamData, ParsedData> {
14 /// Initial state when it's unknown, f.e. when stream didn't return any data yet
15 ParsedData get initialState;
16 ParsedData? _lastKnownState;
17
18 /// The last known adapter state
19 ParsedData get lastKnownState => _lastKnownState ?? initialState;
20
21 /// Internal method to cache the last adapter state value, backends should only implement parse not this method
22 ParsedData parseAndCache(StreamData rawState) {
23 _lastKnownState = parse(rawState);
24 return lastKnownState;
25 }
26}
27
28/// Transforms the backend's bluetooth adapter state stream to emit [BluetoothAdapterState]'s
29///
30/// Can normally be used directly, backends should only inject a customized BluetoothStateParser
31class StreamDataParserTransformer<StreamData, ParsedData, SD extends StreamDataParser<StreamData, ParsedData>>
32 extends StreamDataTransformer<StreamData, ParsedData> {
33 /// Create a BluetoothAdapterStateStreamTransformer
34 ///
35 /// [stateParser] The BluetoothStateParser that provides the backend logic to convert BackendState to BluetoothAdapterState
36 StreamDataParserTransformer({ required SD stateParser, super.sync, super.cancelOnError }) {
37 _stateParser = stateParser;
38 }
39
40 late SD _stateParser;
41
42 @override
43 void onData(StreamData streamData) {
44 late ParsedData data;
45 if (_stateParser is StreamDataParserCached) {
46 data = (_stateParser as StreamDataParserCached).parseAndCache(streamData);
47 } else {
48 data = _stateParser.parse(streamData);
49 }
50
51 sendData(data);
52 }
53}
54
55
56/// Generic stream transformer util that should support cancelling & pausing etc
57///
58/// Implementations should only need to worry about transforming the data by overriding
59/// the onData method and sending the transformed data using sendData
60///
61/// TODO: move outside bluetooth logic
62abstract class StreamDataTransformer<S,T> with TypeLogger implements StreamTransformer<S,T> {
63 /// Create a BluetoothStreamTransformer
64 ///
65 /// - [sync] Passed to [StreamController]
66 /// - [cancelOnError] Passed to [Stream]
67 StreamDataTransformer({ bool sync = false, bool cancelOnError = false }) {
68 _cancelOnError = cancelOnError;
69
70 _controller = StreamController<T>(
71 onListen: _onListen,
72 onCancel: _onCancel,
73 onPause: () => _subscription?.pause(),
74 onResume: () => _subscription?.resume(),
75 sync: sync,
76 );
77 }
78
79 late StreamController<T> _controller;
80 StreamSubscription? _subscription;
81 Stream<S>? _stream;
82 bool _cancelOnError = false;
83
84 void _onListen() {
85 logger.finest('_onListen');
86 _subscription = _stream?.listen(
87 onData,
88 onError: _controller.addError,
89 onDone: _controller.close,
90 cancelOnError: _cancelOnError);
91 }
92
93 void _onCancel() {
94 logger.finest('_onCancel');
95 _subscription?.cancel();
96 _subscription = null;
97 }
98
99 /// Method that actually transforms the data being passed through this stream
100 void onData(S streamData);
101
102 /// Send data to the listening stream, should f.e. be called from within the
103 /// onData call to forward the transformed data
104 void sendData(T data) {
105 _controller.add(data);
106 }
107
108 @override
109 Stream<T> bind(Stream<S> stream) {
110 logger.finest('bind');
111 _stream = stream;
112 return _controller.stream;
113 }
114
115 @override
116 StreamTransformer<RS, RT> cast<RS, RT>() => StreamTransformer.castFrom(this);
117}