// import 'package:workmanager/workmanager.dart'; // TODO: Enable when workmanager is added back import 'package:flutter/foundation.dart'; import 'package:firebase_database/firebase_database.dart'; // import '../providers/panen_provider.dart'; // TODO: Enable when workmanager is added back final FirebaseDatabase _database = FirebaseDatabase.instance; /// Scheduler service untuk otomasi capture panen pada jam 09:00 dan 15:00 class PanenScheduler { static const String pagiTaskId = 'panen_pagi_task'; static const String soreTaskId = 'panen_sore_task'; /// Inisialisasi Workmanager dan setup background tasks /// Panggil ini di main.dart saat app start static Future initialize() async { try { // TODO: Enable when workmanager is added back // await Workmanager().initialize( // callbackDispatcher, // isInDebugMode: kDebugMode, // ); // Setup periodic tasks await setupPanenSchedules(); if (kDebugMode) { print('✅ PanenScheduler initialized successfully'); } } catch (e) { if (kDebugMode) { print('❌ Error initializing PanenScheduler: $e'); } } } /// Setup scheduled tasks untuk panen pagi (09:00) dan sore (15:00) static Future setupPanenSchedules() async { try { // TODO: Enable when workmanager is added back // Panen Pagi: Setiap hari jam 09:00 // await Workmanager().registerPeriodicTask( // pagiTaskId, // 'panenPagiTask', // frequency: const Duration(hours: 24), // initialDelay: _calculateDelay(9, 0), // Delay ke jam 09:00 berikutnya // constraints: Constraints(networkType: NetworkType.connected), // backoffPolicy: BackoffPolicy.exponential, // backoffPolicyDelay: const Duration(minutes: 15), // ); // Panen Sore: Setiap hari jam 15:00 // await Workmanager().registerPeriodicTask( // soreTaskId, // 'panenSoreTask', // frequency: const Duration(hours: 24), // initialDelay: _calculateDelay(15, 0), // Delay ke jam 15:00 berikutnya // constraints: Constraints(networkType: NetworkType.connected), // backoffPolicy: BackoffPolicy.exponential, // backoffPolicyDelay: const Duration(minutes: 15), // ); if (kDebugMode) { print('✅ Panen schedules registered:'); print(' - Panen Pagi: 09:00 setiap hari'); print(' - Panen Sore: 15:00 setiap hari'); } } catch (e) { if (kDebugMode) { print('❌ Error setting up schedules: $e'); } } } /// Hitung delay sampai waktu target (hour:minute) berikutnya // ignore: unused_element static Duration _calculateDelay(int targetHour, int targetMinute) { final now = DateTime.now(); final target = DateTime( now.year, now.month, now.day, targetHour, targetMinute, ); // Jika waktu target sudah lewat hari ini, target untuk besok if (target.isBefore(now)) { return target.add(const Duration(days: 1)).difference(now); } return target.difference(now); } /// Hentikan semua scheduler static Future cancelAllTasks() async { try { // TODO: Enable when workmanager is added back // await Workmanager().cancelAll(); if (kDebugMode) { print('✅ All tasks cancelled'); } } catch (e) { if (kDebugMode) { print('❌ Error cancelling tasks: $e'); } } } /// Hentikan task tertentu static Future cancelTask(String taskId) async { try { // TODO: Enable when workmanager is added back // await Workmanager().cancelByTag(taskId); if (kDebugMode) { print('✅ Task $taskId cancelled'); } } catch (e) { if (kDebugMode) { print('❌ Error cancelling task $taskId: $e'); } } } // ========== DEBUG METHODS ========== /// Manual trigger untuk panen pagi (untuk testing/debug) static Future> triggerPanenPagiManual() async { try { if (kDebugMode) { print('🔧 [DEBUG] Manual trigger: Panen Pagi'); } await _executePanenPagi(); return { 'success': true, 'message': 'Panen Pagi berhasil disimpan', 'timestamp': DateTime.now().toIso8601String(), }; } catch (e) { if (kDebugMode) { print('❌ [DEBUG] Error manual panen pagi: $e'); } return { 'success': false, 'message': 'Error: $e', 'timestamp': DateTime.now().toIso8601String(), }; } } /// Manual trigger untuk panen sore (untuk testing/debug) static Future> triggerPanenSoreManual() async { try { if (kDebugMode) { print('🔧 [DEBUG] Manual trigger: Panen Sore'); } await _executePanenSore(); return { 'success': true, 'message': 'Panen Sore berhasil disimpan', 'timestamp': DateTime.now().toIso8601String(), }; } catch (e) { if (kDebugMode) { print('❌ [DEBUG] Error manual panen sore: $e'); } return { 'success': false, 'message': 'Error: $e', 'timestamp': DateTime.now().toIso8601String(), }; } } /// Get snapshot panen hari ini (untuk debug/monitoring) static Future?> getTodaySnapshot() async { try { final snapshotRef = _database.ref('panen_snapshot/${_getTodayKey()}'); final snapshot = await snapshotRef.get(); if (snapshot.exists) { return Map.from(snapshot.value as Map); } return null; } catch (e) { if (kDebugMode) { print('❌ Error getting snapshot: $e'); } return null; } } /// Get next scheduled time (untuk tampilan UI) static Map getNextScheduledTimes() { final now = DateTime.now(); // Calculate next pagi (09:00) DateTime nextPagi = DateTime(now.year, now.month, now.day, 9, 0); if (nextPagi.isBefore(now)) { nextPagi = nextPagi.add(const Duration(days: 1)); } // Calculate next sore (15:00) DateTime nextSore = DateTime(now.year, now.month, now.day, 15, 0); if (nextSore.isBefore(now)) { nextSore = nextSore.add(const Duration(days: 1)); } return { 'pagi': '${nextPagi.year}-${nextPagi.month.toString().padLeft(2, '0')}-${nextPagi.day.toString().padLeft(2, '0')} 09:00', 'sore': '${nextSore.year}-${nextSore.month.toString().padLeft(2, '0')}-${nextSore.day.toString().padLeft(2, '0')} 15:00', }; } } /// Callback dispatcher untuk background tasks /// PENTING: Ini harus top-level function atau static method /// Tidak bisa di-refactor menjadi method di class @pragma('vm:entry-point') void callbackDispatcher() { // TODO: Enable when workmanager is added back /* Workmanager().executeTask((taskName, inputData) async { try { if (kDebugMode) { print('🔔 Running task: $taskName at ${DateTime.now()}'); } switch (taskName) { case 'panenPagiTask': await _executePanenPagi(); break; case 'panenSoreTask': await _executePanenSore(); break; default: if (kDebugMode) { print('⚠️ Unknown task: $taskName'); } } return true; // Task berhasil } catch (e) { if (kDebugMode) { print('❌ Error executing task $taskName: $e'); } return false; // Task gagal, akan di-retry } }); */ } /// Eksekusi panen pagi: ambil nilai sensor saat ini Future _executePanenPagi() async { try { if (kDebugMode) { print('📍 Executing Panen Pagi (09:00)'); } // Baca nilai sensor current dari Firebase final dataRef = _database.ref('data'); final snapshot = await dataRef.get(); if (snapshot.exists) { final data = Map.from(snapshot.value as Map); // Ambil setiap kandang dan capture sensor value // Data mapping: infra1 untuk kandang_1, infra2 untuk kandang_2, dst final infra1Value = data['infra1'] ?? 0; final infra2Value = data['infra2'] ?? 0; // Simpan snapshot pagi di Firebase untuk reference await _database.ref('panen_snapshot/${_getTodayKey()}').set({ 'pagi': { 'kandang_1': infra1Value, 'kandang_2': infra2Value, 'timestamp': DateTime.now().toIso8601String(), }, }); if (kDebugMode) { print( '✅ Panen Pagi snapshot saved: kandang_1=$infra1Value, kandang_2=$infra2Value', ); } // TODO: Trigger PanenProvider.addPanenForSchedule() untuk kandang_1 dan kandang_2 // (Akan dipanggil dari main app via PanenProvider) } } catch (e) { if (kDebugMode) { print('❌ Error in _executePanenPagi: $e'); } } } /// Eksekusi panen sore: hitung delta dari pagi Future _executePanenSore() async { try { if (kDebugMode) { print('📍 Executing Panen Sore (15:00)'); } // Baca nilai sensor current final dataRef = _database.ref('data'); final snapshot = await dataRef.get(); if (snapshot.exists) { final data = Map.from(snapshot.value as Map); final infra1Value = data['infra1'] ?? 0; final infra2Value = data['infra2'] ?? 0; // Baca snapshot pagi hari ini untuk delta calculation final pagiSnapshotRef = _database.ref( 'panen_snapshot/${_getTodayKey()}/pagi', ); final pagiSnapshot = await pagiSnapshotRef.get(); int nilaiPagiKandang1 = 0; int nilaiPagiKandang2 = 0; if (pagiSnapshot.exists) { final pagiData = Map.from(pagiSnapshot.value as Map); nilaiPagiKandang1 = pagiData['kandang_1'] ?? 0; nilaiPagiKandang2 = pagiData['kandang_2'] ?? 0; } // Hitung delta final deltaKandang1 = infra1Value - nilaiPagiKandang1; final deltaKandang2 = infra2Value - nilaiPagiKandang2; // Simpan snapshot sore di Firebase await _database.ref('panen_snapshot/${_getTodayKey()}').update({ 'sore': { 'kandang_1': infra1Value, 'kandang_2': infra2Value, 'delta_kandang_1': deltaKandang1, 'delta_kandang_2': deltaKandang2, 'timestamp': DateTime.now().toIso8601String(), }, }); if (kDebugMode) { print( '✅ Panen Sore calculated: delta_kandang_1=$deltaKandang1, delta_kandang_2=$deltaKandang2', ); } // TODO: Trigger PanenProvider.addPanenForSchedule() dengan delta values } } catch (e) { if (kDebugMode) { print('❌ Error in _executePanenSore: $e'); } } } /// Helper: Get today's date key (format: yyyy-MM-dd) String _getTodayKey() { final now = DateTime.now(); return '${now.year}-${now.month.toString().padLeft(2, '0')}-${now.day.toString().padLeft(2, '0')}'; }