From 74a1609c21ea5b1887b08089189372e35d7f0dec Mon Sep 17 00:00:00 2001 From: kleponijo Date: Mon, 29 Jun 2026 11:51:24 +0700 Subject: [PATCH] oke --- .../blocs/device_setup_event.dart | 16 +-- .../blocs/device_setup_state.dart | 12 +- .../blocs/device_setup_wind_speed_bloc.dart | 17 +-- .../views/device_setup_wind_speed_screen.dart | 132 ++++-------------- .../lib/src/firebase_monitoring_repo.dart | 19 +-- .../lib/src/models/wind_speed.dart | 33 +++-- .../lib/src/monitoring_repo.dart | 13 +- 7 files changed, 70 insertions(+), 172 deletions(-) diff --git a/lib/screens/monitoring/device_setup/blocs/device_setup_event.dart b/lib/screens/monitoring/device_setup/blocs/device_setup_event.dart index 6563fa5..3affa26 100644 --- a/lib/screens/monitoring/device_setup/blocs/device_setup_event.dart +++ b/lib/screens/monitoring/device_setup/blocs/device_setup_event.dart @@ -19,27 +19,21 @@ class DeviceIdChanged extends DeviceSetupEvent { DeviceIdChanged(this.deviceId); } -class KFaktorChanged extends DeviceSetupEvent { - final double value; - KFaktorChanged(this.value); -} - -class RadiusChanged extends DeviceSetupEvent { - final double value; - RadiusChanged(this.value); -} - class IntervalRealtimeChanged extends DeviceSetupEvent { final int ms; IntervalRealtimeChanged(this.ms); } +class IntervalAverageChanged extends DeviceSetupEvent { + final int ms; + IntervalAverageChanged(this.ms); +} + class IntervalHistoryChanged extends DeviceSetupEvent { final int ms; IntervalHistoryChanged(this.ms); } -/// User ubah jumlah magnet (1 atau 3) class MagnetCountChanged extends DeviceSetupEvent { final int count; MagnetCountChanged(this.count); diff --git a/lib/screens/monitoring/device_setup/blocs/device_setup_state.dart b/lib/screens/monitoring/device_setup/blocs/device_setup_state.dart index 49ad026..d471f3f 100644 --- a/lib/screens/monitoring/device_setup/blocs/device_setup_state.dart +++ b/lib/screens/monitoring/device_setup/blocs/device_setup_state.dart @@ -22,9 +22,8 @@ class DeviceSetupState { final String espIp; final String deviceId; - final double kFaktor; - final double radiusM; final int intervalRealtimeMs; + final int intervalAverageMs; final int intervalHistoryMs; final int magnetCount; final List> logs; @@ -38,9 +37,8 @@ class DeviceSetupState { this.successMessage, this.espIp = '192.168.4.1', this.deviceId = 'esp_lapangan', - this.kFaktor = 50.0, - this.radiusM = 0.08, this.intervalRealtimeMs = 1000, + this.intervalAverageMs = 60000, this.intervalHistoryMs = 3600000, this.magnetCount = 1, // ← default 1 magnet this.logs = const [], @@ -58,9 +56,8 @@ class DeviceSetupState { String? successMessage, String? espIp, String? deviceId, - double? kFaktor, - double? radiusM, int? intervalRealtimeMs, + int? intervalAverageMs, int? intervalHistoryMs, int? magnetCount, List>? logs, @@ -74,9 +71,8 @@ class DeviceSetupState { successMessage: successMessage, espIp: espIp ?? this.espIp, deviceId: deviceId ?? this.deviceId, - kFaktor: kFaktor ?? this.kFaktor, - radiusM: radiusM ?? this.radiusM, intervalRealtimeMs: intervalRealtimeMs ?? this.intervalRealtimeMs, + intervalAverageMs: intervalAverageMs ?? this.intervalAverageMs, intervalHistoryMs: intervalHistoryMs ?? this.intervalHistoryMs, magnetCount: magnetCount ?? this.magnetCount, logs: logs ?? this.logs, diff --git a/lib/screens/monitoring/device_setup/blocs/device_setup_wind_speed_bloc.dart b/lib/screens/monitoring/device_setup/blocs/device_setup_wind_speed_bloc.dart index d22291b..17cd83c 100644 --- a/lib/screens/monitoring/device_setup/blocs/device_setup_wind_speed_bloc.dart +++ b/lib/screens/monitoring/device_setup/blocs/device_setup_wind_speed_bloc.dart @@ -26,10 +26,10 @@ class DeviceSetupBloc extends Bloc { // Settings on(_onSettingsStarted); on(_onDeviceIdChanged); - on((e, emit) => emit(state.copyWith(kFaktor: e.value))); - on((e, emit) => emit(state.copyWith(radiusM: e.value))); on( (e, emit) => emit(state.copyWith(intervalRealtimeMs: e.ms))); + on( + (e, emit) => emit(state.copyWith(intervalAverageMs: e.ms))); on( (e, emit) => emit(state.copyWith(intervalHistoryMs: e.ms))); on( @@ -56,19 +56,14 @@ class DeviceSetupBloc extends Bloc { // Baca device ID tersimpan lokal final prefs = await SharedPreferences.getInstance(); final savedId = prefs.getString(_kDeviceIdKey) ?? state.deviceId; - - // Baca settings dari Firebase final s = await _repository.getAnemometerSettings(); - - // Baca logs final logs = await _repository.getDeviceLogs(savedId); emit(state.copyWith( status: DeviceSetupStatus.settingsLoaded, deviceId: savedId, - kFaktor: s['k_faktor'] as double, - radiusM: s['radius_m'] as double, intervalRealtimeMs: s['interval_realtime_ms'] as int, + intervalAverageMs: s['interval_average_ms'] as int, intervalHistoryMs: s['interval_history_ms'] as int, magnetCount: (s['magnet_count'] as int?) ?? 1, logs: logs, @@ -104,9 +99,8 @@ class DeviceSetupBloc extends Bloc { emit(state.copyWith(status: DeviceSetupStatus.settingsSaving)); try { await _repository.updateAnemometerSettings( - kFaktor: state.kFaktor, - radiusM: state.radiusM, intervalRealtimeMs: state.intervalRealtimeMs, + intervalAverageMs: state.intervalAverageMs, intervalHistoryMs: state.intervalHistoryMs, magnetCount: state.magnetCount, ); @@ -335,9 +329,8 @@ class DeviceSetupBloc extends Bloc { ) { emit(DeviceSetupState( deviceId: state.deviceId, - kFaktor: state.kFaktor, - radiusM: state.radiusM, intervalRealtimeMs: state.intervalRealtimeMs, + intervalAverageMs: state.intervalAverageMs, intervalHistoryMs: state.intervalHistoryMs, )); } diff --git a/lib/screens/monitoring/device_setup/views/device_setup_wind_speed_screen.dart b/lib/screens/monitoring/device_setup/views/device_setup_wind_speed_screen.dart index e0eaa23..31440f4 100644 --- a/lib/screens/monitoring/device_setup/views/device_setup_wind_speed_screen.dart +++ b/lib/screens/monitoring/device_setup/views/device_setup_wind_speed_screen.dart @@ -18,6 +18,14 @@ const _kRealtimeOptions = { '1 jam': 3600000, }; +const _kAverageOptions = { + '30 detik': 30000, + '1 menit': 60000, + '5 menit': 300000, + '10 menit': 600000, + '30 menit': 1800000, +}; + // ── Opsi interval history ───────────────────────────────────── const _kHistoryOptions = { '10 menit': 600000, @@ -45,8 +53,6 @@ class _DeviceSetupScreenState extends State bool _obscurePassword = true; // Settings form controllers - final _kFaktorController = TextEditingController(); - final _radiusController = TextEditingController(); String? _customDeviceId; @override @@ -60,20 +66,11 @@ class _DeviceSetupScreenState extends State _tabController.dispose(); _ssidController.dispose(); _passwordController.dispose(); - _kFaktorController.dispose(); - _radiusController.dispose(); super.dispose(); } // ── Sync text controllers dengan state saat settings loaded ── - void _syncControllers(DeviceSetupState state) { - if (_kFaktorController.text != state.kFaktor.toString()) { - _kFaktorController.text = state.kFaktor.toStringAsFixed(2); - } - if (_radiusController.text != state.radiusM.toString()) { - _radiusController.text = state.radiusM.toStringAsFixed(3); - } - } + void _syncControllers(DeviceSetupState state) {} @override Widget build(BuildContext context) { @@ -144,8 +141,6 @@ class _DeviceSetupScreenState extends State children: [ _SensorSettingsTab( state: state, - kFaktorController: _kFaktorController, - radiusController: _radiusController, customDeviceId: _customDeviceId, onCustomDeviceIdChanged: (v) => setState(() => _customDeviceId = v), @@ -215,15 +210,11 @@ class _DeviceSetupScreenState extends State // ═══════════════════════════════════════════════════════════════ class _SensorSettingsTab extends StatelessWidget { final DeviceSetupState state; - final TextEditingController kFaktorController; - final TextEditingController radiusController; final String? customDeviceId; final ValueChanged onCustomDeviceIdChanged; const _SensorSettingsTab({ required this.state, - required this.kFaktorController, - required this.radiusController, required this.customDeviceId, required this.onCustomDeviceIdChanged, }); @@ -259,36 +250,6 @@ class _SensorSettingsTab extends StatelessWidget { ), const SizedBox(height: 24), - // ── Kalibrasi ──────────────────────────────────────── - _sectionTitle('Kalibrasi'), - const SizedBox(height: 10), - _SettingsCard(children: [ - _NumericField( - controller: kFaktorController, - label: 'K-Faktor', - hint: 'Contoh: 50.0', - suffix: '×', - helper: 'Konstanta kalibrasi vs AWS. Awal: 50.0', - onChanged: (v) { - final d = double.tryParse(v); - if (d != null && d > 0) bloc.add(KFaktorChanged(d)); - }, - ), - const Divider(height: 24), - _NumericField( - controller: radiusController, - label: 'Jari-jari Lengan', - hint: 'Contoh: 0.08', - suffix: 'm', - helper: 'Jarak pusat ke ujung cup anemometer. Default: 0.08 m', - onChanged: (v) { - final d = double.tryParse(v); - if (d != null && d > 0) bloc.add(RadiusChanged(d)); - }, - ), - ]), - const SizedBox(height: 24), - // ── Jumlah Magnet ───────────────────────────────────── _sectionTitle('Jumlah Magnet'), const SizedBox(height: 4), @@ -320,6 +281,21 @@ class _SensorSettingsTab extends StatelessWidget { ), const SizedBox(height: 24), + _sectionTitle('Interval Rata-rata (Average)'), + const SizedBox(height: 4), + Text( + 'Seberapa sering data rata-rata dikirim ke Firebase. ' + 'Harus lebih pendek dari interval history.', + style: TextStyle(fontSize: 12, color: Colors.grey.shade500), + ), + const SizedBox(height: 10), + _ChipSelector( + options: _kAverageOptions, + selectedMs: state.intervalAverageMs, + onSelected: (ms) => bloc.add(IntervalAverageChanged(ms)), + ), + const SizedBox(height: 24), + // ── Interval History ────────────────────────────────── _sectionTitle('Interval History (Rata-rata Jangka Panjang)'), const SizedBox(height: 4), @@ -969,66 +945,6 @@ class _ChipSelector extends StatelessWidget { } } -class _NumericField extends StatelessWidget { - final TextEditingController controller; - final String label; - final String hint; - final String suffix; - final String helper; - final ValueChanged onChanged; - - const _NumericField({ - required this.controller, - required this.label, - required this.hint, - required this.suffix, - required this.helper, - required this.onChanged, - }); - - @override - Widget build(BuildContext context) { - return TextField( - controller: controller, - keyboardType: const TextInputType.numberWithOptions(decimal: true), - inputFormatters: [FilteringTextInputFormatter.allow(RegExp(r'[0-9.]'))], - decoration: InputDecoration( - labelText: label, - hintText: hint, - suffixText: suffix, - helperText: helper, - helperMaxLines: 2, - border: OutlineInputBorder(borderRadius: BorderRadius.circular(10)), - contentPadding: - const EdgeInsets.symmetric(horizontal: 14, vertical: 12), - isDense: true, - ), - onChanged: onChanged, - ); - } -} - -class _SettingsCard extends StatelessWidget { - final List children; - const _SettingsCard({required this.children}); - - @override - Widget build(BuildContext context) { - return Container( - padding: const EdgeInsets.all(16), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(14), - boxShadow: [ - BoxShadow(color: Colors.black.withValues(alpha: 0.05), blurRadius: 6), - ], - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, children: children), - ); - } -} - class _MagnetCountSelector extends StatelessWidget { final int selected; final ValueChanged onSelected; diff --git a/packages/monitoring_repository/lib/src/firebase_monitoring_repo.dart b/packages/monitoring_repository/lib/src/firebase_monitoring_repo.dart index 7cb137c..0ae24e7 100644 --- a/packages/monitoring_repository/lib/src/firebase_monitoring_repo.dart +++ b/packages/monitoring_repository/lib/src/firebase_monitoring_repo.dart @@ -103,46 +103,49 @@ class FirebaseMonitoringRepo implements MonitoringRepository { Future> getAnemometerSettings() async { // Default sama dengan cfg_config.h di ESP const defaults = { - 'k_faktor': 50.0, - 'radius_m': 0.08, 'interval_realtime_ms': 1000, + 'interval_average_ms': 60000, 'interval_history_ms': 3600000, + 'magnet_count': 3, }; try { final snapshot = await _db.ref('anemometer/settings').get(); if (snapshot.exists && snapshot.value is Map) { final raw = snapshot.value as Map; return { - 'k_faktor': (raw['k_faktor'] ?? defaults['k_faktor']).toDouble(), - 'radius_m': (raw['radius_m'] ?? defaults['radius_m']).toDouble(), 'interval_realtime_ms': (raw['interval_realtime_ms'] ?? defaults['interval_realtime_ms']) as int, + 'interval_average_ms': + (raw['interval_average_ms'] ?? defaults['interval_average_ms']) + as int, 'interval_history_ms': (raw['interval_history_ms'] ?? defaults['interval_history_ms']) as int, + 'magnet_count': + (raw['magnet_count'] as int? ?? defaults['magnet_count']) as int, }; } } catch (_) {} - return defaults; } // ── Tulis settings ke Firebase (dari app) ──────────────────── @override Future updateAnemometerSettings({ - double? kFaktor, - double? radiusM, int? intervalRealtimeMs, + int? intervalAverageMs, int? intervalHistoryMs, int? magnetCount, }) async { final updates = {}; - if (kFaktor != null) updates['k_faktor'] = kFaktor; if (intervalRealtimeMs != null) updates['interval_realtime_ms'] = intervalRealtimeMs; + if (intervalAverageMs != null) + updates['interval_average_ms'] = intervalAverageMs; if (intervalHistoryMs != null) updates['interval_history_ms'] = intervalHistoryMs; + if (magnetCount != null) updates['magnet_count'] = magnetCount; if (updates.isNotEmpty) { await _db.ref('anemometer/settings').update(updates); } diff --git a/packages/monitoring_repository/lib/src/models/wind_speed.dart b/packages/monitoring_repository/lib/src/models/wind_speed.dart index e0b75d3..4bfe291 100644 --- a/packages/monitoring_repository/lib/src/models/wind_speed.dart +++ b/packages/monitoring_repository/lib/src/models/wind_speed.dart @@ -1,31 +1,38 @@ class MyWindSpeed { - final double speed; // realtime: speed_ms | history: avg_ms - final double maxSpeed; // history only (0 jika dari realtime) - final double kFaktor; + final double windwegKm; + final double maxWindwegKm; final int sampleCount; final DateTime timestamp; MyWindSpeed({ - required this.speed, - this.maxSpeed = 0.0, - this.kFaktor = 1.0, + required this.windwegKm, + this.maxWindwegKm = 0.0, this.sampleCount = 0, required this.timestamp, }); + // Getter alias untuk kompatibilitas dengan bloc/chart yang pakai .speed + double get speed => windwegKm; + double get maxSpeed => maxWindwegKm; + static final empty = MyWindSpeed( - speed: 0.0, + windwegKm: 0.0, timestamp: DateTime.fromMillisecondsSinceEpoch(0), ); - // Dipakai untuk realtime node factory MyWindSpeed.fromJson(Map json) { return MyWindSpeed( - // support field lama 'speed' dan baru 'speed_ms'/'avg_ms' - speed: ((json['speed_ms'] ?? json['avg_ms'] ?? json['speed']) ?? 0) - .toDouble(), - maxSpeed: (json['max_ms'] ?? 0).toDouble(), - kFaktor: (json['k_faktor'] ?? 1.0).toDouble(), + // realtime → windweg_km + // history → avg_windweg_km + // average → windweg_km + windwegKm: + ((json['windweg_km'] ?? + json['avg_windweg_km'] ?? + json['speed_ms'] ?? // fallback data lama + json['speed'] ?? + 0)) + .toDouble(), + maxWindwegKm: (json['max_windweg_km'] ?? 0).toDouble(), sampleCount: (json['sample_count'] ?? 0) as int, timestamp: DateTime.fromMillisecondsSinceEpoch( (json['timestamp'] ?? 0) * 1000, diff --git a/packages/monitoring_repository/lib/src/monitoring_repo.dart b/packages/monitoring_repository/lib/src/monitoring_repo.dart index 542001a..f99685c 100644 --- a/packages/monitoring_repository/lib/src/monitoring_repo.dart +++ b/packages/monitoring_repository/lib/src/monitoring_repo.dart @@ -17,18 +17,11 @@ abstract class MonitoringRepository { T Function(Map json) mapper, ); - // ── Anemometer Settings ────────────────────────────────────── - /// Baca semua settings dari /anemometer/settings/ - /// Return map dengan keys: - /// k_faktor (double), radius_m (double), - /// interval_realtime_ms (int), interval_history_ms (int) Future> getAnemometerSettings(); - /// Tulis settings ke /anemometer/settings/ (field yang null tidak ditulis) Future updateAnemometerSettings({ - double? kFaktor, - double? radiusM, int? intervalRealtimeMs, + int? intervalAverageMs, int? intervalHistoryMs, int? magnetCount, }); @@ -41,15 +34,11 @@ abstract class MonitoringRepository { }); Future sendRemoteRestart(String deviceId); - Future deleteDeviceLogs(String deviceId, List keys); - Future> getSensorHistoryWithKeys( String path, T Function(Map json) mapper, ); - Future deleteHistoryByKeys(String path, List keys); - Future deleteAllHistory(String path); }