getConnection(); if (!function_exists('kirimWA')) { function kirimWA($pesan) { $token = "XjC9oSr4jECWG93JQRWf"; $data = [ "target" => "120363407747513164@g.us", "message" => $pesan, ]; $curl = curl_init(); curl_setopt_array($curl, [ CURLOPT_URL => "https://api.fonnte.com/send", CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => http_build_query($data), CURLOPT_HTTPHEADER => [ "Authorization: $token" ], ]); curl_exec($curl); curl_close($curl); } } // ✅ Ambil semua ODP (KHUSUS USER 22) $stmt = $conn->query(" SELECT i.id, i.name, i.latitude, i.longitude FROM ftth_items i LEFT JOIN item_types it ON i.item_type_id = it.id WHERE LOWER(it.name) = 'odp' AND i.user_id = 22 "); $odps = $stmt->fetchAll(PDO::FETCH_ASSOC); foreach ($odps as $odp) { // 🔥 ambil status terakhir $cek = $conn->prepare(" SELECT last_status FROM notif_log WHERE odp_id = ? "); $cek->execute([$odp['id']]); $last = $cek->fetch(PDO::FETCH_ASSOC); $lastStatus = $last['last_status'] ?? null; // 🔥 ambil client $stmtClient = $conn->prepare(" SELECT fi.id, fi.name, fi.serial_number FROM cable_routes cr JOIN ftth_items fi ON (cr.to_item_id = fi.id OR cr.from_item_id = fi.id) WHERE (cr.from_item_id = :id OR cr.to_item_id = :id) AND fi.id != :id "); $stmtClient->bindParam(':id', $odp['id']); $stmtClient->execute(); $clients = $stmtClient->fetchAll(PDO::FETCH_ASSOC); $rxValues = []; $clientBad = []; foreach ($clients as $c) { if (empty($c['serial_number'])) continue; $query = json_encode(["_id" => $c['serial_number']]); $url = "http://rmtstb.megadataisp.net:7557/devices/?query=" . urlencode($query); $res = @file_get_contents($url); if (!$res) continue; $json = json_decode($res, true); if ( isset($json[0]['VirtualParameters']['RXPower']['_value']) && is_numeric($json[0]['VirtualParameters']['RXPower']['_value']) ) { $rx = floatval($json[0]['VirtualParameters']['RXPower']['_value']); $rxValues[] = $rx; if ($rx < -23) { $clientBad[] = $c['name'] . " (" . $rx . " dBm)"; } } } // 🔴 skip kalau tidak ada data if (count($rxValues) == 0) continue; // 🔥 HITUNG (SAMA PERSIS KAYAK MANUAL) $total = count($rxValues); $bad = count($clientBad); $good = $total - $bad; $avg = round(array_sum($rxValues) / $total, 2); $min = min($rxValues); // 🔥 STATUS (SAMA PERSIS) $selisih = abs($avg - $min); $status = "BAIK"; if ($selisih > 1.5) { $status = "WARNING"; } if ($avg < -23 || $bad > ($total * 0.3)) { $status = "WARNING"; } if ($avg < -25 || $bad > ($total * 0.5)) { $status = "BURUK"; } if ($min < -27) { $status = "KRITIS"; } // ❌ ANTI SPAM (kalau status sama, skip) if ($status == $lastStatus) continue; // 🔥 FORMAT WA (PERSIS KAYAK MANUAL) $maps = "https://www.google.com/maps?q={$odp['latitude']},{$odp['longitude']}"; // ============================== // 🚨 KIRIM SAAT BERMASALAH // ============================== if ($status != "BAIK") { $pesan = "🚨 *AUTO LAPORAN ODP*\n\n"; $pesan .= "📍 *ODP:* {$odp['name']}\n"; $pesan .= "📊 *Status:* {$status}\n\n"; $pesan .= "👥 *Total Client:* {$total}\n"; $pesan .= "🟢 Client Baik: {$good}\n"; $pesan .= "🔴 Client Buruk: {$bad}\n\n"; $pesan .= "📶 *AVG RX:* {$avg} dBm\n"; $pesan .= "📉 *MIN RX:* {$min} dBm\n\n"; if (!empty($clientBad)) { $pesan .= "⚠ *Client Bermasalah:*\n"; $pesan .= implode("\n", array_slice($clientBad, 0, 10)); $pesan .= "\n\n"; } $pesan .= "📌 Lokasi:\n$maps\n\n"; $pesan .= "🔎 Silakan cek detail di dashboard."; kirimWA($pesan); } // ============================== // ✅ OPSIONAL: KIRIM SAAT NORMAL // ============================== elseif ($status == "BAIK" && $lastStatus != "BAIK") { $pesan = "✅ *ODP SUDAH NORMAL*\n\n"; $pesan .= "📍 ODP: {$odp['name']}\n"; $pesan .= "Status sekarang: BAIK\n"; $pesan .= "AVG RX: {$avg} dBm\n"; $pesan .= "MIN RX: {$min} dBm\n\n"; $pesan .= "Jaringan sudah stabil 👍"; kirimWA($pesan); } // 🔥 UPDATE STATUS TERAKHIR $update = $conn->prepare(" INSERT INTO notif_log (odp_id, last_status, last_sent) VALUES (?, ?, NOW()) ON DUPLICATE KEY UPDATE last_status = VALUES(last_status), last_sent = NOW() "); $update->execute([$odp['id'], $status]); }