This commit is contained in:
kleponijo 2026-06-29 13:42:06 +07:00
parent a6e74d4da0
commit 898c784359
4 changed files with 69 additions and 23 deletions

View File

@ -19,6 +19,7 @@ class WindSpeedBloc extends Bloc<WindSpeedEvent, WindSpeedState> {
final MonitoringRepository _repository;
final NotificationBloc _notificationBloc;
StreamSubscription<MyWindSpeed>? _subscription;
StreamSubscription<Map<String, MyWindSpeed>>? _historySubscription;
WindSpeedBloc({
required MonitoringRepository repository,
@ -35,6 +36,7 @@ class WindSpeedBloc extends Bloc<WindSpeedEvent, WindSpeedState> {
on<WindSpeedDeleteByDateRequested>(_onDeleteByDate);
on<WindSpeedDeleteByDateRangeRequested>(_onDeleteByDateRange);
on<WindSpeedDeleteByHourRangeRequested>(_onDeleteByHourRange);
on<_WindSpeedHistoryUpdated>(_onHistoryUpdated);
}
Future<String> _getDeviceId() async {
@ -49,42 +51,55 @@ class WindSpeedBloc extends Bloc<WindSpeedEvent, WindSpeedState> {
WatchWindSpeedStarted event,
Emitter<WindSpeedState> emit,
) async {
// Cancel stream lama dulu sebelum apapun
await _subscription?.cancel();
await _historySubscription?.cancel();
_subscription = null;
_historySubscription = null;
emit(state.copyWith(isLoading: true, currentSpeed: 0));
final deviceId = await _getDeviceId();
final historyMap = await _repository.getSensorHistoryWithKeys(
// History stream (realtime, otomatis update)
_historySubscription = _repository
.getSensorHistoryStream(
'anemometer/$deviceId/history',
(json) => MyWindSpeed.fromJson(json),
);
)
.listen((newMap) => add(_WindSpeedHistoryUpdated(newMap)));
final history = _sortedList(historyMap);
final graphs = _buildGraphs(history);
if (history.isNotEmpty) _emitWindAlert(history.last.speed);
emit(state.copyWith(
historyMap: historyMap,
history: history,
filteredHistory: history,
dailySpeeds: graphs['daily']!,
weeklySpeeds: graphs['weekly']!,
monthlySpeeds: graphs['monthly']!,
isLoading: false,
alertLevel:
history.isNotEmpty ? _getAlertLevel(history.last.speed) : 'Normal',
));
// Subscribe stream baru
// Realtime stream
_subscription = _repository
.getSensorStream(
'anemometer/$deviceId/realtime',
(json) => MyWindSpeed.fromJson(json),
)
.listen((data) => add(_WindSpeedRealtimeUpdated(data)));
// Loading selesai, state awal kosong dulu
// _onHistoryUpdated akan otomatis mengisi data begitu stream pertama emit
emit(state.copyWith(isLoading: false));
}
void _onHistoryUpdated(
_WindSpeedHistoryUpdated event,
Emitter<WindSpeedState> emit,
) {
final history = _sortedList(event.historyMap);
final graphs = _buildGraphs(history);
final filtered = state.selectedDate != null
? history
.where((e) => _isSameDate(e.timestamp, state.selectedDate!))
.toList()
: history;
emit(state.copyWith(
historyMap: event.historyMap,
history: history,
filteredHistory: filtered,
dailySpeeds: graphs['daily'],
weeklySpeeds: graphs['weekly'],
monthlySpeeds: graphs['monthly'],
));
}
//
@ -429,6 +444,7 @@ class WindSpeedBloc extends Bloc<WindSpeedEvent, WindSpeedState> {
@override
Future<void> close() async {
await _subscription?.cancel();
await _historySubscription?.cancel();
return super.close();
}
}

View File

@ -76,3 +76,10 @@ class WindSpeedDeleteByHourRangeRequested extends WindSpeedEvent {
@override
List<Object?> get props => [date, startHour, endHour];
}
class _WindSpeedHistoryUpdated extends WindSpeedEvent {
final Map<String, MyWindSpeed> historyMap;
const _WindSpeedHistoryUpdated(this.historyMap);
@override
List<Object?> get props => [historyMap];
}

View File

@ -200,4 +200,21 @@ class FirebaseMonitoringRepo implements MonitoringRepository {
Future<void> sendRemoteRestart(String deviceId) async {
await _db.ref('anemometer/$deviceId/command/restart').set(true);
}
@override
Stream<Map<String, T>> getSensorHistoryStream<T>(
String path,
T Function(Map<dynamic, dynamic> json) mapper,
) {
return _db.ref(path).onValue.map((event) {
if (!event.snapshot.exists || event.snapshot.value is! Map) return {};
final data = event.snapshot.value as Map<dynamic, dynamic>;
return {
for (final entry in data.entries)
entry.key.toString(): mapper(
entry.value is Map ? entry.value as Map<dynamic, dynamic> : {},
),
};
});
}
}

View File

@ -41,4 +41,10 @@ abstract class MonitoringRepository {
);
Future<void> deleteHistoryByKeys(String path, List<String> keys);
Future<void> deleteAllHistory(String path);
// Tambah di abstract class MonitoringRepository:
Stream<Map<String, T>> getSensorHistoryStream<T>(
String path,
T Function(Map<dynamic, dynamic> json) mapper,
);
}