From 31218385c67beaacbaab9c7c8784a721f8634d4b Mon Sep 17 00:00:00 2001 From: kleponijo Date: Thu, 28 May 2026 15:44:21 +0700 Subject: [PATCH] update magnet count --- .../device_setup/blocs/device_setup_bloc.dart | 4 + .../blocs/device_setup_event.dart | 20 +-- .../blocs/device_setup_state.dart | 40 +++--- .../views/device_setup_screen.dart | 129 +++++++++++++++++- .../lib/src/firebase_monitoring_repo.dart | 1 + .../lib/src/monitoring_repo.dart | 1 + 6 files changed, 153 insertions(+), 42 deletions(-) diff --git a/lib/screens/monitoring/device_setup/blocs/device_setup_bloc.dart b/lib/screens/monitoring/device_setup/blocs/device_setup_bloc.dart index bb1c890..65c71b3 100644 --- a/lib/screens/monitoring/device_setup/blocs/device_setup_bloc.dart +++ b/lib/screens/monitoring/device_setup/blocs/device_setup_bloc.dart @@ -32,6 +32,8 @@ class DeviceSetupBloc extends Bloc { (e, emit) => emit(state.copyWith(intervalRealtimeMs: e.ms))); on( (e, emit) => emit(state.copyWith(intervalHistoryMs: e.ms))); + on( + (e, emit) => emit(state.copyWith(magnetCount: e.count))); on(_onSettingsSaved); on(_onLogsRefreshed); on(_onRestartRequested); @@ -64,6 +66,7 @@ class DeviceSetupBloc extends Bloc { radiusM: s['radius_m'] as double, intervalRealtimeMs: s['interval_realtime_ms'] as int, intervalHistoryMs: s['interval_history_ms'] as int, + magnetCount: (s['magnet_count'] as int?) ?? 1, logs: logs, )); } catch (e) { @@ -101,6 +104,7 @@ class DeviceSetupBloc extends Bloc { radiusM: state.radiusM, intervalRealtimeMs: state.intervalRealtimeMs, intervalHistoryMs: state.intervalHistoryMs, + magnetCount: state.magnetCount, ); emit(state.copyWith(status: DeviceSetupStatus.settingsSaved)); await Future.delayed(const Duration(seconds: 2)); 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 e426d38..e7c9df8 100644 --- a/lib/screens/monitoring/device_setup/blocs/device_setup_event.dart +++ b/lib/screens/monitoring/device_setup/blocs/device_setup_event.dart @@ -2,59 +2,51 @@ part of 'device_setup_bloc.dart'; abstract class DeviceSetupEvent {} -/// Cek apakah HP sedang terhubung ke hotspot ESP class CheckEspConnectionEvent extends DeviceSetupEvent {} -/// Kirim SSID + password baru ke ESP via HTTP class SendWifiCredentialsEvent extends DeviceSetupEvent { final String ssid; final String password; - SendWifiCredentialsEvent({required this.ssid, required this.password}); } -/// Reset state ke awal (misalnya saat user keluar screen) class ResetDeviceSetupEvent extends DeviceSetupEvent {} -// ── Sensor Settings events (new) ───────────────────────────── - -/// Load device ID dari SharedPreferences + settings dari Firebase class DeviceSettingsStarted extends DeviceSetupEvent {} -/// User pilih/ubah device ID (misal: esp_percobaan → esp_lapangan) class DeviceIdChanged extends DeviceSetupEvent { final String deviceId; DeviceIdChanged(this.deviceId); } -/// User ubah k_faktor di form class KFaktorChanged extends DeviceSetupEvent { final double value; KFaktorChanged(this.value); } -/// User ubah radius_m di form class RadiusChanged extends DeviceSetupEvent { final double value; RadiusChanged(this.value); } -/// User ubah interval realtime (ms) class IntervalRealtimeChanged extends DeviceSetupEvent { final int ms; IntervalRealtimeChanged(this.ms); } -/// User ubah interval history (ms) class IntervalHistoryChanged extends DeviceSetupEvent { final int ms; IntervalHistoryChanged(this.ms); } -/// Simpan settings ke Firebase +/// User ubah jumlah magnet (1 atau 3) +class MagnetCountChanged extends DeviceSetupEvent { + final int count; + MagnetCountChanged(this.count); +} + class DeviceSettingsSaved extends DeviceSetupEvent {} -/// Refresh logs dari Firebase class DeviceLogsRefreshed extends DeviceSetupEvent {} class DeviceRestartRequested extends DeviceSetupEvent {} 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 e3bb71d..86d29c1 100644 --- a/lib/screens/monitoring/device_setup/blocs/device_setup_state.dart +++ b/lib/screens/monitoring/device_setup/blocs/device_setup_state.dart @@ -1,14 +1,13 @@ part of 'device_setup_bloc.dart'; enum DeviceSetupStatus { - idle, // belum ada aksi - checkingConn, // sedang cek koneksi ke ESP - notConnected, // HP belum konek ke hotspot ESP - connected, // HP sudah konek ke hotspot ESP, siap kirim - sending, // sedang kirim SSID+password ke ESP - success, // ESP berhasil terima & akan restart - failure, // gagal (timeout, ESP tidak respond, dll) - // Settings + idle, + checkingConn, + notConnected, + connected, + sending, + success, + failure, settingsLoading, settingsLoaded, settingsSaving, @@ -21,25 +20,17 @@ class DeviceSetupState { final String? errorMessage; final String? successMessage; final String espIp; - // ── Sensor settings ────────────────────────────────────────── - /// ID device aktif — menentukan path Firebase yang dibaca app. - /// Default 'esp_lapangan' (ESP utama di lapangan). - /// Disimpan lokal di SharedPreferences. + final String deviceId; - - /// Konstanta kalibrasi. Default 50.0 (hasil estimasi vs AWS). final double kFaktor; - - /// Jari-jari lengan anemometer dalam meter. Default 0.08 (8 cm). final double radiusM; - - /// Interval pengiriman realtime ke Firebase (ms). Default 1000 = 1 detik. final int intervalRealtimeMs; - - /// Interval push history ke Firebase (ms). Default 3600000 = 1 jam. final int intervalHistoryMs; - // ── Logs ───────────────────────────────────────────────────── + /// Jumlah magnet pada anemometer. 1 = default, 3 = resolusi lebih tinggi. + /// Disimpan di Firebase: /anemometer/settings/magnet_count + final int magnetCount; + final List> logs; final bool logsLoading; @@ -47,14 +38,13 @@ class DeviceSetupState { this.status = DeviceSetupStatus.idle, this.errorMessage, this.successMessage, - this.espIp = '192.168.4.1', // default IP ESP saat AP mode - // Settings — default sama dengan cfg_config.h di ESP + this.espIp = '192.168.4.1', this.deviceId = 'esp_lapangan', this.kFaktor = 50.0, this.radiusM = 0.08, this.intervalRealtimeMs = 1000, this.intervalHistoryMs = 3600000, - // Logs + this.magnetCount = 1, // ← default 1 magnet this.logs = const [], this.logsLoading = false, }); @@ -69,6 +59,7 @@ class DeviceSetupState { double? radiusM, int? intervalRealtimeMs, int? intervalHistoryMs, + int? magnetCount, List>? logs, bool? logsLoading, }) { @@ -82,6 +73,7 @@ class DeviceSetupState { radiusM: radiusM ?? this.radiusM, intervalRealtimeMs: intervalRealtimeMs ?? this.intervalRealtimeMs, intervalHistoryMs: intervalHistoryMs ?? this.intervalHistoryMs, + magnetCount: magnetCount ?? this.magnetCount, logs: logs ?? this.logs, logsLoading: logsLoading ?? this.logsLoading, ); diff --git a/lib/screens/monitoring/device_setup/views/device_setup_screen.dart b/lib/screens/monitoring/device_setup/views/device_setup_screen.dart index 2caa196..8b5f1f2 100644 --- a/lib/screens/monitoring/device_setup/views/device_setup_screen.dart +++ b/lib/screens/monitoring/device_setup/views/device_setup_screen.dart @@ -289,6 +289,21 @@ class _SensorSettingsTab extends StatelessWidget { ]), const SizedBox(height: 24), + // ── Jumlah Magnet ───────────────────────────────────── + _sectionTitle('Jumlah Magnet'), + const SizedBox(height: 4), + Text( + 'Sesuaikan dengan jumlah magnet yang terpasang di lengan anemometer. ' + 'Lebih banyak magnet = resolusi lebih tinggi.', + style: TextStyle(fontSize: 12, color: Colors.grey.shade500), + ), + const SizedBox(height: 10), + _MagnetCountSelector( + selected: state.magnetCount, + onSelected: (count) => bloc.add(MagnetCountChanged(count)), + ), + const SizedBox(height: 24), + // ── Interval Realtime ───────────────────────────────── _sectionTitle('Interval Pengiriman Realtime'), const SizedBox(height: 4), @@ -325,6 +340,7 @@ class _SensorSettingsTab extends StatelessWidget { _FormulaPreview( kFaktor: state.kFaktor, radiusM: state.radiusM, + magnetCount: state.magnetCount, ), const SizedBox(height: 32), @@ -879,12 +895,18 @@ class _SettingsCard extends StatelessWidget { class _FormulaPreview extends StatelessWidget { final double kFaktor; final double radiusM; - const _FormulaPreview({required this.kFaktor, required this.radiusM}); + final int magnetCount; + + const _FormulaPreview({ + required this.kFaktor, + required this.radiusM, + required this.magnetCount, + }); @override Widget build(BuildContext context) { // Contoh: 1 pulsa per detik - final exampleRps = 1.0; + final exampleRps = 1.0 / magnetCount; final exampleSpeed = 2 * 3.14159 * radiusM * exampleRps * kFaktor; return Container( @@ -908,7 +930,7 @@ class _FormulaPreview extends StatelessWidget { ]), const SizedBox(height: 10), Text( - 'speed = 2π × radius × RPS × k_faktor', + 'speed = 2π × radius × (pulsa / (dt × magnet)) × k', style: TextStyle( fontSize: 12, color: Colors.grey.shade400, @@ -916,7 +938,7 @@ class _FormulaPreview extends StatelessWidget { ), const SizedBox(height: 6), Text( - ' = 2π × ${radiusM.toStringAsFixed(3)}m × RPS × ${kFaktor.toStringAsFixed(1)}', + ' = 2π × ${radiusM.toStringAsFixed(3)}m × (p / (dt × $magnetCount)) × ${kFaktor.toStringAsFixed(1)}', style: const TextStyle( fontSize: 12, color: Colors.white70, fontFamily: 'monospace'), ), @@ -935,6 +957,105 @@ class _FormulaPreview extends StatelessWidget { } } +// ── WIDGET BARU: _MagnetCountSelector ───────────────────────── +// Tambahkan class baru ini di bagian bawah file, +// setelah class _FormulaPreview + +class _MagnetCountSelector extends StatelessWidget { + final int selected; + final ValueChanged onSelected; + + const _MagnetCountSelector({ + required this.selected, + required this.onSelected, + }); + + @override + Widget build(BuildContext context) { + const options = [1, 2, 3]; + + 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: Row( + children: options.map((count) { + final isSelected = count == selected; + return Expanded( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 4), + child: InkWell( + onTap: () => onSelected(count), + borderRadius: BorderRadius.circular(10), + child: AnimatedContainer( + duration: const Duration(milliseconds: 200), + padding: const EdgeInsets.symmetric(vertical: 14), + decoration: BoxDecoration( + color: isSelected + ? Colors.blue.shade700 + : Colors.grey.shade100, + borderRadius: BorderRadius.circular(10), + border: Border.all( + color: isSelected + ? Colors.blue.shade700 + : Colors.grey.shade300, + ), + ), + child: Column( + children: [ + // Gambar titik magnet + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: List.generate( + count, + (_) => Container( + margin: const EdgeInsets.symmetric(horizontal: 2), + width: 8, + height: 8, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: isSelected + ? Colors.white + : Colors.blue.shade400, + ), + ), + ), + ), + const SizedBox(height: 6), + Text( + '$count magnet', + style: TextStyle( + fontSize: 13, + fontWeight: FontWeight.bold, + color: isSelected ? Colors.white : Colors.black87, + ), + ), + Text( + count == 1 ? 'default' : 'resolusi ${count}×', + style: TextStyle( + fontSize: 10, + color: isSelected + ? Colors.white70 + : Colors.grey.shade500, + ), + ), + ], + ), + ), + ), + ), + ); + }).toList(), + ), + ); + } +} + // ══════════════════════════════════════════════════════════════════ // Widgets internal // ══════════════════════════════════════════════════════════════════ diff --git a/packages/monitoring_repository/lib/src/firebase_monitoring_repo.dart b/packages/monitoring_repository/lib/src/firebase_monitoring_repo.dart index a0f9f43..c8de923 100644 --- a/packages/monitoring_repository/lib/src/firebase_monitoring_repo.dart +++ b/packages/monitoring_repository/lib/src/firebase_monitoring_repo.dart @@ -98,6 +98,7 @@ class FirebaseMonitoringRepo implements MonitoringRepository { double? radiusM, int? intervalRealtimeMs, int? intervalHistoryMs, + int? magnetCount, }) async { final updates = {}; if (kFaktor != null) updates['k_faktor'] = kFaktor; diff --git a/packages/monitoring_repository/lib/src/monitoring_repo.dart b/packages/monitoring_repository/lib/src/monitoring_repo.dart index 7db0f76..760b54d 100644 --- a/packages/monitoring_repository/lib/src/monitoring_repo.dart +++ b/packages/monitoring_repository/lib/src/monitoring_repo.dart @@ -30,6 +30,7 @@ abstract class MonitoringRepository { double? radiusM, int? intervalRealtimeMs, int? intervalHistoryMs, + int? magnetCount, }); // ── Device Logs ──────────────────────────────────────────────