diff --git a/lib/screens/monitoring/wind_speed/blocs/wind_speed_bloc.dart b/lib/screens/monitoring/wind_speed/blocs/wind_speed_bloc.dart index 1ce90e6..76d5b63 100644 --- a/lib/screens/monitoring/wind_speed/blocs/wind_speed_bloc.dart +++ b/lib/screens/monitoring/wind_speed/blocs/wind_speed_bloc.dart @@ -17,21 +17,27 @@ class WindSpeedBloc extends Bloc { (event, emit) async { emit(state.copyWith(isLoading: true)); + // 1. Ambil semua history + final history = await _repository.getSensorHistory( + 'anemometer/history', (json) => MyWindSpeed.fromJson(json)); + + // 2. Petakan ke 24 jam (Harian) + final List dailyGraph = _mapHistoryToDaily(history); + + emit(state.copyWith( + dailySpeeds: dailyGraph, + isLoading: false, + )); + + // 2. Monitoring Real-time await emit.forEach( _repository.getSensorStream( 'anemometer/realtime', (json) => MyWindSpeed.fromJson(json)), onData: (data) { // Mengambil list lama dan menambah data baru untuk grafik - final updatedSpeeds = List.from(state.dailySpeeds) - ..add(data.speed); - // Batasi jumlah data di grafik (misal cuma simpan 20 data terakhir agar tidak berat) - if (updatedSpeeds.length > 20) { - updatedSpeeds.removeAt(0); - } return state.copyWith( currentSpeed: data.speed, - dailySpeeds: updatedSpeeds, // Update list grafik isLoading: false, ); }, @@ -41,13 +47,78 @@ class WindSpeedBloc extends Bloc { transformer: restartable(), ); - on((event, emit) { + on((event, emit) async { emit(state.copyWith(selectedPeriod: event.period)); + // Ambil ulang history dari database + final history = await _repository.getSensorHistory( + 'anemometer/history', (json) => MyWindSpeed.fromJson(json)); + List updatedGraph; + + // Pilih mapper berdasarkan pilihan user + if (event.period == "Minggu Ini") { + updatedGraph = _mapHistoryToWeekly(history); + } else if (event.period == "Bulan Ini") { + updatedGraph = _mapHistoryToMonthly(history); + } else { + updatedGraph = _mapHistoryToDaily(history); + } + + emit(state.copyWith( + dailySpeeds: updatedGraph, + isLoading: false, + )); }); } @override - Future close() { - return super.close(); - } + Future close() => super.close(); +} + +List _mapHistoryToDaily(List history) { + // Buat list 24 angka nol (representasi jam 00:00 - 23:00) + List slots = List.generate(24, (_) => 0.0); + + for (var item in history) { + // Ubah timestamp UTC ke jam lokal + DateTime date = item.timestamp; + + // Jika data ini adalah data hari ini, masukkan ke slot jamnya + if (date.day == DateTime.now().day) { + slots[date.hour] = item.speed; + } + } + return slots; +} + +// Mapper Mingguan (7 Slot: Senin - Minggu) +List _mapHistoryToWeekly(List history) { + List slots = List.generate(7, (_) => 0.0); + final now = DateTime.now(); + + // Mencari awal minggu ini (Senin) + DateTime startOfWeek = now.subtract(Duration(days: now.weekday - 1)); + startOfWeek = DateTime(startOfWeek.year, startOfWeek.month, startOfWeek.day); + + for (var item in history) { + if (item.timestamp.isAfter(startOfWeek)) { + // index 0 = Senin, dst. + slots[item.timestamp.weekday - 1] = item.speed; + } + } + return slots; +} + +// Mapper Bulanan (Sesuai jumlah hari di bulan ini) +List _mapHistoryToMonthly(List history) { + final now = DateTime.now(); + final daysInMonth = DateTime(now.year, now.month + 1, 0).day; + List slots = List.generate(daysInMonth, (_) => 0.0); + + for (var item in history) { + if (item.timestamp.month == now.month && item.timestamp.year == now.year) { + // index 0 = Tanggal 1, dst. + slots[item.timestamp.day - 1] = item.speed; + } + } + return slots; } diff --git a/lib/screens/monitoring/wind_speed/views/widgets/period_selector.dart b/lib/screens/monitoring/wind_speed/views/widgets/period_selector.dart index 067b14a..95c6f6c 100644 --- a/lib/screens/monitoring/wind_speed/views/widgets/period_selector.dart +++ b/lib/screens/monitoring/wind_speed/views/widgets/period_selector.dart @@ -1 +1,57 @@ /// === Tombol Filter Jam/Hari/Minggu === /// +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import '../../blocs/wind_speed_bloc.dart'; + +class PeriodSelector extends StatelessWidget { + const PeriodSelector({super.key}); + + @override + Widget build(BuildContext context) { + // Kita ambil state saat ini untuk tahu periode mana yang aktif + final selectedPeriod = + context.select((WindSpeedBloc bloc) => bloc.state.selectedPeriod); + + return Container( + padding: const EdgeInsets.all(4), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(15), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + _buildTab(context, "Hari Ini", selectedPeriod), + _buildTab(context, "Minggu Ini", selectedPeriod), + _buildTab(context, "Bulan Ini", selectedPeriod), + ], + ), + ); + } + + Widget _buildTab(BuildContext context, String label, String current) { + bool isActive = label == current; + + return GestureDetector( + onTap: () { + // Kirim event ke Bloc saat tombol diklik + context.read().add(WindSpeedPeriodChanged(label)); + }, + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + decoration: BoxDecoration( + color: isActive ? Colors.blue : Colors.transparent, + borderRadius: BorderRadius.circular(12), + ), + child: Text( + label, + style: TextStyle( + color: isActive ? Colors.white : Colors.grey, + fontWeight: FontWeight.bold, + fontSize: 12, + ), + ), + ), + ); + } +} diff --git a/lib/screens/monitoring/wind_speed/views/wind_speed_screen.dart b/lib/screens/monitoring/wind_speed/views/wind_speed_screen.dart index c8d2567..84d4a4b 100644 --- a/lib/screens/monitoring/wind_speed/views/wind_speed_screen.dart +++ b/lib/screens/monitoring/wind_speed/views/wind_speed_screen.dart @@ -1,8 +1,9 @@ import 'package:flutter/material.dart'; -import 'package:fl_chart/fl_chart.dart'; +// import 'package:fl_chart/fl_chart.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import '../blocs/wind_speed_bloc.dart'; import './widgets/wind_speed_chart_widget.dart'; +import 'widgets/period_selector.dart'; class WindSpeedScreen extends StatelessWidget { const WindSpeedScreen({super.key}); @@ -37,10 +38,13 @@ class WindSpeedScreen extends StatelessWidget { _buildMainSpeedDisplay(state.currentSpeed), const SizedBox(height: 30), - const Text("Tren Real-time", + const Text("Tren Kecepatan", style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)), const SizedBox(height: 15), + const PeriodSelector(), + const SizedBox(height: 15), + WindSpeedChartWidget(dailySpeeds: state.dailySpeeds), const SizedBox(height: 30), diff --git a/packages/monitoring_repository/lib/src/firebase_monitoring_repo.dart b/packages/monitoring_repository/lib/src/firebase_monitoring_repo.dart index 5d19c33..2dd935b 100644 --- a/packages/monitoring_repository/lib/src/firebase_monitoring_repo.dart +++ b/packages/monitoring_repository/lib/src/firebase_monitoring_repo.dart @@ -38,4 +38,28 @@ class FirebaseMonitoringRepo implements MonitoringRepository { final data = snapshot.value as Map? ?? {}; return mapper(data); } + + @override + Future> getSensorHistory( + String path, + T Function(Map json) mapper, + ) async { + try { + // Ambil data dari path history + final snapshot = await _db.ref(path).get(); + + if (snapshot.exists && snapshot.value is Map) { + final Map data = snapshot.value as Map; + + // Karena RTDB push() menghasilkan Map dengan ID unik, + // kita ambil values-nya saja dan ubah jadi List objek + return data.values.map((item) { + return mapper(item as Map); + }).toList(); + } + return []; + } catch (e) { + return []; + } + } } diff --git a/packages/monitoring_repository/lib/src/models/wind_speed.dart b/packages/monitoring_repository/lib/src/models/wind_speed.dart index 8aa88fb..afc1c9d 100644 --- a/packages/monitoring_repository/lib/src/models/wind_speed.dart +++ b/packages/monitoring_repository/lib/src/models/wind_speed.dart @@ -9,6 +9,12 @@ class MyWindSpeed { required this.timestamp, }); + static final empty = MyWindSpeed( + speed: 0.0, + pulse: 0, + timestamp: DateTime.fromMillisecondsSinceEpoch(0), + ); + factory MyWindSpeed.fromJson(Map json) { return MyWindSpeed( speed: (json['speed'] ?? 0).toDouble(), diff --git a/packages/monitoring_repository/lib/src/monitoring_repo.dart b/packages/monitoring_repository/lib/src/monitoring_repo.dart index d97747b..ac08d1f 100644 --- a/packages/monitoring_repository/lib/src/monitoring_repo.dart +++ b/packages/monitoring_repository/lib/src/monitoring_repo.dart @@ -1,13 +1,21 @@ import 'models/models.dart'; abstract class MonitoringRepository { + // fungsi untuk ambil data berkali/streaming Stream getSensorStream( String path, T Function(Map json) mapper, ); + // fungsi untuk ambil sensor sekali Future getSensorSnapshot( String path, T Function(Map json) mapper, ); + + // Fungsi untuk ambil riwayat (List) + Future> getSensorHistory( + String path, + T Function(Map json) mapper, + ); }