Upload source code ftthplanner
This commit is contained in:
parent
371ca32928
commit
f43f0f5afe
|
|
@ -0,0 +1,117 @@
|
|||
<?php
|
||||
require_once __DIR__ . '/../config/database.php';
|
||||
|
||||
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
|
||||
|
||||
$db = new Database();
|
||||
$conn = $db->getConnection();
|
||||
|
||||
// ================= CONFIG =================
|
||||
$login_url = "http://10.255.254.21/login";
|
||||
$data_url = "http://10.255.254.21/ontinfo_table";
|
||||
|
||||
$username = "root";
|
||||
$password = "@lokal234";
|
||||
|
||||
$cookie = __DIR__ . "/cookie.txt";
|
||||
|
||||
// ================= LOGIN =================
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $login_url);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([
|
||||
"username" => $username,
|
||||
"password" => $password
|
||||
]));
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_COOKIEJAR, $cookie);
|
||||
curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie);
|
||||
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
|
||||
curl_exec($ch);
|
||||
curl_close($ch);
|
||||
|
||||
// ================= AMBIL DATA =================
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $data_url);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie);
|
||||
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
|
||||
if ($response === false) {
|
||||
die("CURL ERROR: " . curl_error($ch));
|
||||
}
|
||||
curl_close($ch);
|
||||
|
||||
file_put_contents("debug_onu.json", $response);
|
||||
|
||||
$data = json_decode($response, true);
|
||||
|
||||
if (!isset($data['data']) || empty($data['data'])) {
|
||||
die("DATA KOSONG");
|
||||
}
|
||||
|
||||
// ================= PROCESS =================
|
||||
foreach ($data['data'] as $onu) {
|
||||
|
||||
$sn = $onu['ont_sn'] ?? '';
|
||||
$rx = $onu['receive_power'] ?? 0;
|
||||
$state = $onu['state'] ?? 0;
|
||||
$cause = strtolower($onu['last_d_cause'] ?? '');
|
||||
|
||||
if (empty($sn)) continue;
|
||||
|
||||
// ================= STATUS =================
|
||||
if (strpos($cause, 'laser out') !== false || strpos($cause, 'fiber') !== false) {
|
||||
$status = "LASER OUT";
|
||||
} elseif (strpos($cause, 'power down') !== false || strpos($cause, 'dying-gasp') !== false) {
|
||||
$status = "POWER FAIL";
|
||||
} else {
|
||||
if ($state == 0 && $rx <= -30) {
|
||||
$status = "LASER OUT";
|
||||
} elseif ($rx <= -30) {
|
||||
$status = "POWER FAIL";
|
||||
} else {
|
||||
$status = "ONLINE";
|
||||
}
|
||||
}
|
||||
|
||||
// ================= GET LAST STATUS =================
|
||||
$cek = $conn->prepare("SELECT status FROM onu_status WHERE sn = ?");
|
||||
$cek->execute([$sn]);
|
||||
$last = $cek->fetch(PDO::FETCH_ASSOC);
|
||||
$lastStatus = $last['status'] ?? null;
|
||||
|
||||
// ================= UPSERT REALTIME =================
|
||||
$conn->query("
|
||||
INSERT INTO onu_status (sn, status, rx_power, last_update)
|
||||
VALUES ('$sn', '$status', '$rx', NOW())
|
||||
ON DUPLICATE KEY UPDATE
|
||||
last_status = status,
|
||||
status = '$status',
|
||||
rx_power = '$rx',
|
||||
last_update = NOW()
|
||||
");
|
||||
|
||||
// ================= LOG HISTORI (HANYA ANOMALI) =================
|
||||
$anomali = in_array($status, ['LASER OUT', 'POWER FAIL']);
|
||||
|
||||
$trigger = ($lastStatus !== $status);
|
||||
|
||||
if ($anomali && $trigger) {
|
||||
|
||||
$stmt = $conn->prepare("
|
||||
INSERT INTO status_onu_histori (sn, status, rx_power, created_at)
|
||||
VALUES (?, ?, ?, NOW())
|
||||
");
|
||||
$stmt->execute([$sn, $status, $rx]);
|
||||
|
||||
echo "LOG ANOMALI: $sn | $status | RX $rx<br>";
|
||||
}
|
||||
|
||||
echo "SN: $sn | STATUS: $status | RX: $rx<br>";
|
||||
}
|
||||
|
||||
echo "DONE ✅";
|
||||
?>
|
||||
|
|
@ -0,0 +1,369 @@
|
|||
<?php
|
||||
require_once __DIR__ . '/../config/database.php';
|
||||
|
||||
set_time_limit(300);
|
||||
|
||||
$db = new Database();
|
||||
$conn = $db->getConnection();
|
||||
|
||||
// 🔥 GROUP TEKNISI
|
||||
$groupTeknisi = "120363427232707228@g.us";
|
||||
|
||||
// ======================
|
||||
// 🔥 FUNCTION WA
|
||||
// ======================
|
||||
if (!function_exists('kirimWA')) {
|
||||
function kirimWA($target, $pesan) {
|
||||
if (!$target) return;
|
||||
|
||||
$token = "XjC9oSr4jECWG93JQRWf";
|
||||
|
||||
$data = [
|
||||
"target" => $target,
|
||||
"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"
|
||||
],
|
||||
CURLOPT_TIMEOUT => 10
|
||||
]);
|
||||
|
||||
curl_exec($curl);
|
||||
curl_close($curl);
|
||||
}
|
||||
}
|
||||
|
||||
// ======================
|
||||
// 🔥 TEST MODE (OPSIONAL)
|
||||
// ======================
|
||||
$TEST_MODE = false;
|
||||
|
||||
// ======================
|
||||
// 🔥 AMBIL DATA DARI DATABASE (JOIN HISTORY)
|
||||
// ======================
|
||||
$stmt = $conn->query("
|
||||
SELECT
|
||||
f.id,
|
||||
f.name,
|
||||
f.sn_onu,
|
||||
f.latitude,
|
||||
f.longitude,
|
||||
f.customer_phone,
|
||||
|
||||
o.rx_power,
|
||||
o.voltage,
|
||||
o.updated_at
|
||||
|
||||
FROM ftth_items f
|
||||
|
||||
LEFT JOIN (
|
||||
SELECT h1.*
|
||||
FROM onu_daily_history h1
|
||||
INNER JOIN (
|
||||
SELECT sn, MAX(updated_at) AS max_time
|
||||
FROM onu_daily_history
|
||||
WHERE DATE(updated_at) = CURDATE()
|
||||
GROUP BY sn
|
||||
) h2
|
||||
ON h1.sn = h2.sn
|
||||
AND h1.updated_at = h2.max_time
|
||||
) o
|
||||
ON o.sn = f.sn_onu
|
||||
|
||||
INNER JOIN item_types it
|
||||
ON it.id = f.item_type_id
|
||||
|
||||
WHERE f.sn_onu IS NOT NULL
|
||||
AND LOWER(it.name) = 'pelanggan'
|
||||
");
|
||||
|
||||
$dataItems = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
foreach ($dataItems as $item) {
|
||||
|
||||
usleep(200000);
|
||||
|
||||
$sn = strtoupper(trim($item['sn_onu']));
|
||||
$nama = $item['name'];
|
||||
$lat = $item['latitude'];
|
||||
$lng = $item['longitude'];
|
||||
$phone = $item['customer_phone'];
|
||||
|
||||
// ======================
|
||||
// 🔥 AMBIL RX & VOLT DARI DB
|
||||
// ======================
|
||||
$rx = isset($item['rx_power']) ? floatval($item['rx_power']) : null;
|
||||
$volt = isset($item['voltage']) ? floatval($item['voltage']) : null;
|
||||
|
||||
if ($rx === null && $volt === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// ======================
|
||||
// 🔥 STATUS DETEKSI
|
||||
// ======================
|
||||
// RX NORMAL = -20 s/d -25.99 dBm
|
||||
|
||||
$isRxHighLoss = ($rx !== null && $rx < -25.99); // redaman terlalu besar
|
||||
$isRxLowLoss = ($rx !== null && $rx > -20.00); // redaman terlalu kecil
|
||||
|
||||
$isBadRx = $isRxHighLoss || $isRxLowLoss;
|
||||
|
||||
|
||||
// VOLTAGE NORMAL = 3.20 - 3.29 V
|
||||
|
||||
$isVoltLow = ($volt !== null && $volt < 3.20);
|
||||
$isVoltHigh = ($volt !== null && $volt > 3.29);
|
||||
|
||||
$isBadVolt = $isVoltLow || $isVoltHigh;
|
||||
|
||||
|
||||
// ======================
|
||||
// 🔥 KETERANGAN ANALISA
|
||||
// ======================
|
||||
|
||||
$rxKeterangan = '';
|
||||
|
||||
if ($isRxHighLoss) {
|
||||
|
||||
$rxKeterangan =
|
||||
"Redaman terlalu besar (High Loss). Sinyal optik berada di bawah standar perusahaan. Kemungkinan terjadi redaman berlebih pada jalur fiber, sambungan, konektor, splitter, atau kabel mengalami kerusakan.";
|
||||
|
||||
} elseif ($isRxLowLoss) {
|
||||
|
||||
$rxKeterangan =
|
||||
"Redaman terlalu kecil (Low Loss). Nilai sinyal optik berada di atas standar perusahaan. Diperlukan pengecekan kualitas distribusi jaringan dan konfigurasi perangkat.";
|
||||
}
|
||||
|
||||
$voltKeterangan = '';
|
||||
|
||||
if ($isVoltLow) {
|
||||
|
||||
$voltKeterangan =
|
||||
"Tegangan ONU berada di bawah standar 3.20 - 3.29 Volt. Kemungkinan adaptor lemah, kabel power rusak, atau suplai listrik tidak stabil.";
|
||||
|
||||
} elseif ($isVoltHigh) {
|
||||
|
||||
$voltKeterangan =
|
||||
"Tegangan ONU berada di atas standar 3.20 - 3.29 Volt. Diperlukan pengecekan adaptor dan sumber daya listrik.";
|
||||
}
|
||||
|
||||
|
||||
// ======================
|
||||
// 🔥 CEK ANTI SPAM LOG
|
||||
// ======================
|
||||
$cek = $conn->prepare("
|
||||
SELECT last_rx, last_voltage
|
||||
FROM notif_device_log
|
||||
WHERE sn = ?
|
||||
");
|
||||
$cek->execute([$sn]);
|
||||
$last = $cek->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
$lastRx = $last['last_rx'] ?? null;
|
||||
$lastVolt = $last['last_voltage'] ?? null;
|
||||
|
||||
$isChanged =
|
||||
round((float)$lastRx, 2) != round((float)$rx, 2) ||
|
||||
round((float)$lastVolt, 2) != round((float)$volt, 2);
|
||||
|
||||
if (!$isChanged) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// ======================
|
||||
// 🔥 MAPS
|
||||
// ======================
|
||||
$maps = ($lat && $lng)
|
||||
? "https://www.google.com/maps?q={$lat},{$lng}"
|
||||
: "-";
|
||||
|
||||
// ======================
|
||||
// 🔥 FORMAT NOMOR
|
||||
// ======================
|
||||
$phoneFix = null;
|
||||
if ($phone) {
|
||||
$phoneFix = preg_replace('/[^0-9]/', '', $phone);
|
||||
if (substr($phoneFix, 0, 1) == '0') {
|
||||
$phoneFix = '62' . substr($phoneFix, 1);
|
||||
}
|
||||
}
|
||||
//$isBadVolt = ($volt !== null && $volt < 3.29);
|
||||
//$isVoltLow = ($volt !== null && $volt < 3.20);
|
||||
//$isVoltHigh = ($volt !== null && $volt > 3.29);
|
||||
|
||||
//$isBadVolt = $isVoltLow || $isVoltHigh;
|
||||
|
||||
|
||||
// ======================
|
||||
// 🔥 NOTIFIKASI
|
||||
// ======================
|
||||
|
||||
if ($isBadRx && $isBadVolt) {
|
||||
|
||||
// ======================
|
||||
// PELANGGAN
|
||||
// ======================
|
||||
|
||||
kirimWA(
|
||||
$phoneFix,
|
||||
|
||||
"🚨 *Pemberitahuan Gangguan Layanan Internet*\n\n".
|
||||
|
||||
"Yth. {$nama},\n\n".
|
||||
|
||||
"Sistem monitoring kami mendeteksi kondisi jaringan dan perangkat internet di lokasi Anda berada di luar standar operasional.\n\n".
|
||||
|
||||
"📉 Kualitas Sinyal : ".number_format($rx,2)." dBm\n".
|
||||
"🔌 Tegangan ONU : ".number_format($volt,2)." V\n\n".
|
||||
|
||||
"Gangguan ini berpotensi menyebabkan internet lambat, putus-putus, atau tidak dapat digunakan.\n\n".
|
||||
|
||||
"Tim teknis kami telah menerima laporan otomatis dan akan melakukan pengecekan lebih lanjut.\n\n".
|
||||
|
||||
"Mohon maaf atas ketidaknyamanan ini.\n\n".
|
||||
|
||||
"Terima kasih.\n".
|
||||
"RockNet NOC"
|
||||
);
|
||||
|
||||
// ======================
|
||||
// TEKNISI
|
||||
// ======================
|
||||
|
||||
kirimWA(
|
||||
$groupTeknisi,
|
||||
|
||||
"🚨 *ANOMALI RX + VOLTAGE*\n\n".
|
||||
|
||||
"👤 Pelanggan : {$nama}\n".
|
||||
"🔢 SN ONU : {$sn}\n\n".
|
||||
|
||||
"📉 RX Power : ".number_format($rx,2)." dBm\n".
|
||||
"🔌 Voltage : ".number_format($volt,2)." V\n\n".
|
||||
|
||||
"📋 Analisa RX :\n".
|
||||
"{$rxKeterangan}\n\n".
|
||||
|
||||
"📋 Analisa Voltage :\n".
|
||||
"{$voltKeterangan}\n\n".
|
||||
|
||||
"📍 Lokasi :\n{$maps}\n\n".
|
||||
|
||||
"⚠ PRIORITAS TINGGI\n".
|
||||
"Perlu pengecekan jalur fiber dan perangkat ONU."
|
||||
);
|
||||
|
||||
}
|
||||
elseif ($isBadRx) {
|
||||
|
||||
kirimWA(
|
||||
$phoneFix,
|
||||
|
||||
"🚨 *Pemberitahuan Gangguan Jaringan*\n\n".
|
||||
|
||||
"Yth. {$nama},\n\n".
|
||||
|
||||
"Sistem monitoring mendeteksi kualitas sinyal fiber optik di lokasi Anda berada di luar standar operasional.\n\n".
|
||||
|
||||
"📉 Nilai Sinyal : ".number_format($rx,2)." dBm\n\n".
|
||||
|
||||
"Gangguan ini dapat menyebabkan internet tidak stabil, lambat, atau terputus.\n\n".
|
||||
|
||||
"Tim teknis kami telah menerima laporan otomatis dan akan melakukan pengecekan.\n\n".
|
||||
|
||||
"Mohon maaf atas ketidaknyamanan ini.\n\n".
|
||||
|
||||
"RockNet NOC"
|
||||
);
|
||||
|
||||
kirimWA(
|
||||
$groupTeknisi,
|
||||
|
||||
"🚨 *ANOMALI REDAMAN ONU*\n\n".
|
||||
|
||||
"👤 Pelanggan : {$nama}\n".
|
||||
"🔢 SN ONU : {$sn}\n\n".
|
||||
|
||||
"📉 RX Power : ".number_format($rx,2)." dBm\n\n".
|
||||
|
||||
"📋 Analisa :\n".
|
||||
"{$rxKeterangan}\n\n".
|
||||
|
||||
"📍 Lokasi :\n{$maps}\n\n".
|
||||
|
||||
"🔧 Perlu pengecekan jalur fiber, konektor, splitter dan sambungan."
|
||||
);
|
||||
|
||||
}
|
||||
elseif ($isBadVolt) {
|
||||
|
||||
kirimWA(
|
||||
$phoneFix,
|
||||
|
||||
"⚡ *Pemberitahuan Perangkat Internet*\n\n".
|
||||
|
||||
"Yth. {$nama},\n\n".
|
||||
|
||||
"Sistem monitoring mendeteksi tegangan perangkat ONU berada di luar standar operasional.\n\n".
|
||||
|
||||
"🔌 Tegangan ONU : ".number_format($volt,2)." V\n\n".
|
||||
|
||||
"Kondisi ini dapat menyebabkan perangkat restart sendiri, koneksi tidak stabil, atau gangguan layanan internet.\n\n".
|
||||
|
||||
"Tim teknis kami telah menerima laporan otomatis dan akan melakukan pengecekan lebih lanjut.\n\n".
|
||||
|
||||
"Terima kasih.\n".
|
||||
"RockNet NOC"
|
||||
);
|
||||
|
||||
kirimWA(
|
||||
$groupTeknisi,
|
||||
|
||||
"⚡ *ANOMALI VOLTAGE ONU*\n\n".
|
||||
|
||||
"👤 Pelanggan : {$nama}\n".
|
||||
"🔢 SN ONU : {$sn}\n\n".
|
||||
|
||||
"🔌 Voltage : ".number_format($volt,2)." V\n\n".
|
||||
|
||||
"📋 Analisa :\n".
|
||||
"{$voltKeterangan}\n\n".
|
||||
|
||||
"📍 Lokasi :\n{$maps}\n\n".
|
||||
|
||||
"🔧 Perlu pengecekan adaptor, kabel power, dan sumber listrik pelanggan."
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
||||
// ======================
|
||||
// 🔥 UPDATE LOG ANTI SPAM
|
||||
// ======================
|
||||
$update = $conn->prepare("
|
||||
INSERT INTO notif_device_log (sn, last_rx, last_voltage, last_sent)
|
||||
VALUES (?, ?, ?, NOW())
|
||||
ON DUPLICATE KEY UPDATE
|
||||
last_rx = VALUES(last_rx),
|
||||
last_voltage = VALUES(last_voltage),
|
||||
last_sent = NOW()
|
||||
");
|
||||
|
||||
$update->execute([$sn, $rx, $volt]);
|
||||
|
||||
if (!$isChanged) {
|
||||
echo "⏭ SKIP $sn | Tidak ada perubahan<br>";
|
||||
continue;
|
||||
}
|
||||
|
||||
echo "✅ SENT $sn | RX:$rx | V:$volt<br>";
|
||||
}
|
||||
|
|
@ -0,0 +1,189 @@
|
|||
<?php
|
||||
require_once __DIR__ . '/../config/database.php';
|
||||
|
||||
$db = new Database();
|
||||
$conn = $db->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]);
|
||||
}
|
||||
|
|
@ -0,0 +1,144 @@
|
|||
<?php
|
||||
require_once __DIR__ . '/../config/database.php';
|
||||
|
||||
$db = new Database();
|
||||
$conn = $db->getConnection();
|
||||
|
||||
if (!function_exists('kirimWA')) {
|
||||
function kirimWA($target, $pesan) {
|
||||
$token = "XjC9oSr4jECWG93JQRWf";
|
||||
|
||||
$data = [
|
||||
"target" => $target,
|
||||
"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);
|
||||
}
|
||||
}
|
||||
// 🔥 ID GROUP TEKNISI
|
||||
$groupTeknisi = "120363427232707228@g.us";
|
||||
|
||||
// ambil semua ONU yang punya nomor HP + lokasi pelanggan
|
||||
$stmt = $conn->query("
|
||||
SELECT
|
||||
o.sn,
|
||||
o.status,
|
||||
f.name,
|
||||
f.customer_phone,
|
||||
f.latitude,
|
||||
f.longitude
|
||||
FROM onu_status o
|
||||
JOIN ftth_items f ON f.sn_onu = o.sn
|
||||
JOIN item_types it ON f.item_type_id = it.id
|
||||
WHERE f.customer_phone IS NOT NULL
|
||||
AND LOWER(it.name) = 'pelanggan'
|
||||
");
|
||||
|
||||
$data = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
foreach ($data as $row) {
|
||||
|
||||
$sn = $row['sn'];
|
||||
$status = $row['status'];
|
||||
$nama = $row['name'];
|
||||
$phone = $row['customer_phone'];
|
||||
$lat = $row['latitude'];
|
||||
$lng = $row['longitude'];
|
||||
|
||||
// 🔥 buat link maps
|
||||
$maps = ($lat && $lng)
|
||||
? "https://www.google.com/maps?q={$lat},{$lng}"
|
||||
: "-";
|
||||
|
||||
// 🔥 cek status terakhir
|
||||
$cek = $conn->prepare("
|
||||
SELECT last_status FROM notif_onu_log WHERE sn = ?
|
||||
");
|
||||
$cek->execute([$sn]);
|
||||
|
||||
$last = $cek->fetch(PDO::FETCH_ASSOC);
|
||||
$lastStatus = $last['last_status'] ?? null;
|
||||
|
||||
// ❌ anti spam
|
||||
if ($status == $lastStatus) continue;
|
||||
|
||||
// ======================
|
||||
// 🔥 FORMAT PESAN
|
||||
// ======================
|
||||
|
||||
$pesan = "";
|
||||
|
||||
if ($status == "LASER OUT") {
|
||||
$pesan .= "🚨 *GANGGUAN INTERNET*\n\n";
|
||||
$pesan .= "Pelanggan: {$nama}\n\n";
|
||||
$pesan .= "Terjadi gangguan pada kabel fiber (FO CUT).\n";
|
||||
$pesan .= "kami akan informasikan kepada tim teknis untuk segera ke lokasi kakak.\n\n";
|
||||
$pesan .= "estimasi perbaikan 4 Jam pengerjaan.\n\n";
|
||||
$pesan .= "Mohon ditunggu 🙏";
|
||||
|
||||
// ======================
|
||||
// 🔥 NOTIF KE TEKNISI (KHUSUS LOS)
|
||||
// ======================
|
||||
$pesanTeknisi = "🚨 *FO CUT TERDETEKSI*\n\n";
|
||||
$pesanTeknisi .= "👤 Pelanggan: {$nama}\n";
|
||||
$pesanTeknisi .= "📡 SN: {$sn}\n\n";
|
||||
$pesanTeknisi .= "Status: LOS\n";
|
||||
$pesanTeknisi .= "Estimasi: 4 Jam\n\n";
|
||||
$pesanTeknisi .= "📍 Lokasi:\n{$maps}\n\n";
|
||||
$pesanTeknisi .= "⚠ Segera ke lokasi";
|
||||
|
||||
// kirim ke group teknisi
|
||||
kirimWA($groupTeknisi, $pesanTeknisi);
|
||||
}
|
||||
|
||||
elseif ($status == "POWER FAIL") {
|
||||
$pesan .= "⚡ *PERANGKAT MATI*\n\n";
|
||||
$pesan .= "Pelanggan: {$nama}\n\n";
|
||||
$pesan .= "Perangkat ONU terdeteksi mati / tidak mendapat listrik.\n";
|
||||
$pesan .= "Silakan cek adaptor atau listrik di lokasi.\n\n";
|
||||
$pesan .= "Jika terjadi kendala dilokasi mohon konfirmasinya kakak\n\n";
|
||||
$pesan .= "bisa di bantu videokan alat atau foto alat dilokasi kakak🙏\n\n";
|
||||
$pesan .= "Terima kasih 🙏";
|
||||
}
|
||||
|
||||
elseif ($status == "ONLINE" && $lastStatus != "ONLINE") {
|
||||
$pesan .= "✅ *INTERNET NORMAL*\n\n";
|
||||
$pesan .= "Pelanggan: {$nama}\n\n";
|
||||
$pesan .= "Koneksi internet sudah kembali normal.\n\n";
|
||||
$pesan .= "Internet saat ini bisa dicoba kembali\n\n";
|
||||
$pesan .= "Terima kasih 🙏";
|
||||
}
|
||||
|
||||
// kirim kalau ada pesan
|
||||
if ($pesan != "") {
|
||||
|
||||
// 🔥 kirim WA ke pelanggan (TIDAK DIUBAH)
|
||||
kirimWA($phone, $pesan);
|
||||
|
||||
// update log
|
||||
$update = $conn->prepare("
|
||||
INSERT INTO notif_onu_log (sn, last_status, last_sent)
|
||||
VALUES (?, ?, NOW())
|
||||
ON DUPLICATE KEY UPDATE
|
||||
last_status = VALUES(last_status),
|
||||
last_sent = NOW()
|
||||
");
|
||||
$update->execute([$sn, $status]);
|
||||
|
||||
echo "Kirim ke $phone | $status <br>";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,161 @@
|
|||
<?php
|
||||
require_once __DIR__ . '/../config/database.php';
|
||||
|
||||
$db = new Database();
|
||||
$conn = $db->getConnection();
|
||||
|
||||
if (!function_exists('kirimWA')) {
|
||||
function kirimWA($target, $pesan) {
|
||||
$token = "XjC9oSr4jECWG93JQRWf";
|
||||
|
||||
$data = [
|
||||
"target" => $target,
|
||||
"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);
|
||||
}
|
||||
}
|
||||
// 🔥 ID GROUP TEKNISI
|
||||
$groupTeknisi = "120363427232707228@g.us";
|
||||
|
||||
// ambil semua ONU yang punya nomor HP + lokasi pelanggan
|
||||
$stmt = $conn->query("
|
||||
SELECT
|
||||
o.sn,
|
||||
o.status,
|
||||
f.name,
|
||||
f.customer_phone,
|
||||
f.latitude,
|
||||
f.longitude
|
||||
FROM onu_status o
|
||||
JOIN ftth_items f ON f.sn_onu = o.sn
|
||||
JOIN item_types it ON f.item_type_id = it.id
|
||||
WHERE f.customer_phone IS NOT NULL
|
||||
AND LOWER(it.name) = 'pelanggan'
|
||||
");
|
||||
|
||||
$data = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
foreach ($data as $row) {
|
||||
|
||||
$sn = $row['sn'];
|
||||
$status = $row['status'];
|
||||
$nama = $row['name'];
|
||||
$phone = $row['customer_phone'];
|
||||
$lat = $row['latitude'];
|
||||
$lng = $row['longitude'];
|
||||
|
||||
// 🔥 buat link maps
|
||||
$maps = ($lat && $lng)
|
||||
? "https://www.google.com/maps?q={$lat},{$lng}"
|
||||
: "-";
|
||||
|
||||
// 🔥 cek status terakhir
|
||||
$cek = $conn->prepare("
|
||||
SELECT last_status FROM notif_onu_log WHERE sn = ?
|
||||
");
|
||||
$cek->execute([$sn]);
|
||||
|
||||
$last = $cek->fetch(PDO::FETCH_ASSOC);
|
||||
$lastStatus = $last['last_status'] ?? null;
|
||||
|
||||
// ❌ anti spam
|
||||
if ($status == $lastStatus) continue;
|
||||
|
||||
// ======================
|
||||
// 🔥 FORMAT PESAN
|
||||
// ======================
|
||||
|
||||
$pesan = "";
|
||||
|
||||
if ($status == "LASER OUT") {
|
||||
$pesan .= "🚨 *GANGGUAN INTERNET*\n\n";
|
||||
$pesan .= "Pelanggan: {$nama}\n\n";
|
||||
$pesan .= "Terjadi gangguan pada kabel fiber (FO CUT).\n";
|
||||
$pesan .= "kami akan informasikan kepada tim teknis untuk segera ke lokasi kakak.\n\n";
|
||||
$pesan .= "estimasi perbaikan 4 Jam pengerjaan.\n\n";
|
||||
$pesan .= "Mohon ditunggu 🙏";
|
||||
|
||||
// ======================
|
||||
// 🔥 NOTIF KE TEKNISI (KHUSUS LOS)
|
||||
// ======================
|
||||
$pesanTeknisi = "🚨 *FO CUT TERDETEKSI*\n\n";
|
||||
$pesanTeknisi .= "👤 Pelanggan: {$nama}\n";
|
||||
$pesanTeknisi .= "📡 SN: {$sn}\n\n";
|
||||
$pesanTeknisi .= "Status: LOS\n";
|
||||
$pesanTeknisi .= "Estimasi: 4 Jam\n\n";
|
||||
$pesanTeknisi .= "📍 Lokasi:\n{$maps}\n\n";
|
||||
$pesanTeknisi .= "⚠ Segera ke lokasi";
|
||||
|
||||
// kirim ke group teknisi
|
||||
kirimWA($groupTeknisi, $pesanTeknisi);
|
||||
}
|
||||
|
||||
elseif ($status == "POWER FAIL") {
|
||||
|
||||
// 🔥 NOTIF KE PELANGGAN
|
||||
$pesan .= "⚡ *PERANGKAT MATI*\n\n";
|
||||
$pesan .= "Pelanggan: {$nama}\n\n";
|
||||
$pesan .= "Perangkat ONU terdeteksi mati / tidak mendapat listrik.\n";
|
||||
$pesan .= "Silakan cek adaptor atau listrik di lokasi.\n\n";
|
||||
$pesan .= "Jika terjadi kendala dilokasi mohon konfirmasinya kakak\n\n";
|
||||
$pesan .= "Bisa dibantu videokan atau foto kondisi alat 🙏\n\n";
|
||||
$pesan .= "Terima kasih 🙏";
|
||||
|
||||
// ======================
|
||||
// 🔥 NOTIF KE TEKNISI (POWER FAIL)
|
||||
// ======================
|
||||
$pesanTeknisi = "⚡ *POWER FAIL TERDETEKSI*\n\n";
|
||||
$pesanTeknisi .= "👤 Pelanggan: {$nama}\n";
|
||||
$pesanTeknisi .= "📡 SN: {$sn}\n\n";
|
||||
$pesanTeknisi .= "Status: POWER FAIL\n\n";
|
||||
$pesanTeknisi .= "📍 Lokasi:\n{$maps}\n\n";
|
||||
$pesanTeknisi .= "ℹ Saat ini sedang konfirmasi ke pelanggan terkait kondisi alat di lokasi.\n";
|
||||
$pesanTeknisi .= "📸 Jika sudah ada info, akan dikirim foto / video alat di grup ini.\n\n";
|
||||
$pesanTeknisi .= "Mohon standby 🙏";
|
||||
|
||||
// kirim ke group teknisi
|
||||
kirimWA($groupTeknisi, $pesanTeknisi);
|
||||
}
|
||||
|
||||
elseif ($status == "ONLINE" && $lastStatus != "ONLINE") {
|
||||
$pesan .= "✅ *INTERNET NORMAL*\n\n";
|
||||
$pesan .= "Pelanggan: {$nama}\n\n";
|
||||
$pesan .= "Koneksi internet sudah kembali normal.\n\n";
|
||||
$pesan .= "Internet saat ini bisa dicoba kembali\n\n";
|
||||
$pesan .= "Terima kasih 🙏";
|
||||
}
|
||||
|
||||
// kirim kalau ada pesan
|
||||
if ($pesan != "") {
|
||||
|
||||
// 🔥 kirim WA ke pelanggan (TIDAK DIUBAH)
|
||||
kirimWA($phone, $pesan);
|
||||
|
||||
// update log
|
||||
$update = $conn->prepare("
|
||||
INSERT INTO notif_onu_log (sn, last_status, last_sent)
|
||||
VALUES (?, ?, NOW())
|
||||
ON DUPLICATE KEY UPDATE
|
||||
last_status = VALUES(last_status),
|
||||
last_sent = NOW()
|
||||
");
|
||||
$update->execute([$sn, $status]);
|
||||
|
||||
echo "Kirim ke $phone | $status <br>";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
// ⏱ interval (detik)
|
||||
$interval_odp = 60; // 1 menit
|
||||
$interval_onu = 60; // 1 menit
|
||||
$interval_olt = 180; // 3 menit
|
||||
|
||||
// ⏱ last run (awal = 0 biar langsung jalan)
|
||||
$last_odp = 0;
|
||||
$last_onu = 0;
|
||||
$last_olt = 0;
|
||||
|
||||
echo "🚀 Auto Runner Started...\n";
|
||||
|
||||
while (true) {
|
||||
|
||||
$now = time();
|
||||
|
||||
// ===============================
|
||||
// 🔥 CEK ODP (1 menit)
|
||||
// ===============================
|
||||
if (($now - $last_odp) >= $interval_odp) {
|
||||
echo "📡 Cek ODP: " . date("H:i:s") . PHP_EOL;
|
||||
|
||||
include __DIR__ . "/auto_notif_odp.php";
|
||||
|
||||
$last_odp = $now;
|
||||
}
|
||||
|
||||
// ===============================
|
||||
// 🔥 CEK ONU + NOTIF (1 menit)
|
||||
// ===============================
|
||||
if (($now - $last_onu) >= $interval_onu) {
|
||||
echo "📶 Cek ONU: " . date("H:i:s") . PHP_EOL;
|
||||
|
||||
|
||||
include __DIR__ . "/auto_notif_onu.php"; // kirim notif WA
|
||||
|
||||
$last_onu = $now;
|
||||
}
|
||||
|
||||
// ===============================
|
||||
// 🔥 CEK OLT (3 menit)
|
||||
// ===============================
|
||||
//if (($now - $last_olt) >= $interval_olt) {
|
||||
// echo "🛰️ Cek OLT: " . date("H:i:s") . PHP_EOL;
|
||||
|
||||
// include __DIR__ . "/ambil_dataOLT.php"; // update status ONU
|
||||
|
||||
// $last_olt = $now;
|
||||
// }
|
||||
|
||||
// ⏳ delay kecil biar CPU gak 100%
|
||||
sleep(5);
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
// waktu terakhir dijalankan
|
||||
$lastODP = 0;
|
||||
$lastONU = 0;
|
||||
$lastOLT = 0;
|
||||
|
||||
while (true) {
|
||||
|
||||
$now = time();
|
||||
|
||||
echo "Loop jalan: " . date("H:i:s") . PHP_EOL;
|
||||
|
||||
// ======================
|
||||
// CEK ODP (tiap 60 detik)
|
||||
// ======================
|
||||
if ($now - $lastODP >= 60) {
|
||||
echo "Cek ODP: " . date("H:i:s") . PHP_EOL;
|
||||
require_once __DIR__ . "/auto_notif_odp.php";
|
||||
$lastODP = $now;
|
||||
}
|
||||
|
||||
// ======================
|
||||
// CEK ONU (tiap 60 detik)
|
||||
// ======================
|
||||
if ($now - $lastONU >= 60) {
|
||||
echo "Cek ONU: " . date("H:i:s") . PHP_EOL;
|
||||
require_once __DIR__ . "/auto_notif_onu.php";
|
||||
$lastONU = $now;
|
||||
}
|
||||
|
||||
// ==========================
|
||||
// AMBIL DATA OLT (tiap 5 menit)
|
||||
// ==========================
|
||||
if ($now - $lastOLT >= 300) {
|
||||
echo "Ambil Data OLT: " . date("H:i:s") . PHP_EOL;
|
||||
require_once __DIR__ . "/ambil_dataOLT.php";
|
||||
$lastOLT = $now;
|
||||
}
|
||||
|
||||
// jeda kecil biar CPU nggak full
|
||||
sleep(5);
|
||||
}
|
||||
|
||||
|
||||
rmtstb.megadataisp.net
|
||||
|
||||
|
||||
📡 Cek ODP: 20:16:25
|
||||
📶 Cek ONU: 20:16:33
|
||||
Kirim ke 6282338451677 | ONLINE <br>Kirim ke 6281252188305 | ONLINE <br>📡 Cek ODP: 20:17:29
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
<?php
|
||||
require_once __DIR__ . '/../config/database.php';
|
||||
|
||||
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
|
||||
|
||||
$db = new Database();
|
||||
$conn = $db->getConnection();
|
||||
|
||||
// ================= CONFIG =================
|
||||
$login_url = "http://10.255.254.21/login";
|
||||
$data_url = "http://10.255.254.21/ontinfo_table";
|
||||
|
||||
$username = "root";
|
||||
$password = "@lokal234";
|
||||
|
||||
$cookie = __DIR__ . "/cookie.txt";
|
||||
|
||||
// ================= LOGIN =================
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $login_url);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([
|
||||
"username" => $username,
|
||||
"password" => $password
|
||||
]));
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_COOKIEJAR, $cookie);
|
||||
curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie);
|
||||
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
|
||||
|
||||
curl_exec($ch);
|
||||
curl_close($ch);
|
||||
|
||||
// ================= AMBIL DATA (JSON) =================
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $data_url);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie);
|
||||
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
|
||||
if ($response === false) {
|
||||
die("CURL ERROR: " . curl_error($ch));
|
||||
}
|
||||
|
||||
curl_close($ch);
|
||||
|
||||
// simpan debug
|
||||
file_put_contents("debug_onu.json", $response);
|
||||
|
||||
// ================= PARSING JSON =================
|
||||
$data = json_decode($response, true);
|
||||
|
||||
if (!isset($data['data']) || empty($data['data'])) {
|
||||
die("DATA KOSONG / LOGIN GAGAL / ENDPOINT SALAH");
|
||||
}
|
||||
|
||||
foreach ($data['data'] as $onu) {
|
||||
|
||||
$sn = $onu['ont_sn'] ?? '';
|
||||
$rx = $onu['receive_power'] ?? 0;
|
||||
$state = $onu['state'] ?? 0;
|
||||
$cause = $onu['last_d_cause'] ?? '';
|
||||
|
||||
// skip kalau SN kosong
|
||||
if (empty($sn)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// ================= STATUS =================
|
||||
$cause = strtolower($cause);
|
||||
|
||||
// 🔥 PRIORITAS 1: dari cause
|
||||
if (strpos($cause, 'laser out') !== false || strpos($cause, 'fiber') !== false) {
|
||||
$status = "LASER OUT";
|
||||
}
|
||||
elseif (strpos($cause, 'power down') !== false || strpos($cause, 'dying-gasp') !== false) {
|
||||
$status = "POWER FAIL";
|
||||
}
|
||||
|
||||
// 🔥 PRIORITAS 2: fallback dari state + RX
|
||||
else {
|
||||
if ($state == 0 && $rx <= -30) {
|
||||
$status = "LASER OUT"; // default ke LOS kalau tidak jelas
|
||||
} elseif ($rx <= -30) {
|
||||
$status = "POWER FAIL";
|
||||
} else {
|
||||
$status = "ONLINE";
|
||||
}
|
||||
}
|
||||
|
||||
// ================= SIMPAN DB =================
|
||||
$conn->query("
|
||||
INSERT INTO onu_status (sn, status, rx_power, last_update)
|
||||
VALUES ('$sn', '$status', '$rx', NOW())
|
||||
ON DUPLICATE KEY UPDATE
|
||||
last_status = status,
|
||||
status = '$status',
|
||||
rx_power = '$rx',
|
||||
last_update = NOW()
|
||||
");
|
||||
|
||||
$conn->query("
|
||||
INSERT INTO status_onu_histori (sn, status, rx_power, cause, created_at)
|
||||
VALUES (
|
||||
'$sn',
|
||||
'$status',
|
||||
'$rx',
|
||||
'$cause',
|
||||
NOW()
|
||||
)
|
||||
");
|
||||
|
||||
echo "SN: $sn | STATUS: $status | RX: $rx <br>";
|
||||
}
|
||||
|
||||
echo "<br>DONE ✅";
|
||||
?>
|
||||
|
|
@ -0,0 +1,224 @@
|
|||
<?php
|
||||
require_once __DIR__ . '/../config/database.php';
|
||||
|
||||
set_time_limit(300);
|
||||
|
||||
$db = new Database();
|
||||
$conn = $db->getConnection();
|
||||
|
||||
// 🔥 GROUP TEKNISI
|
||||
$groupTeknisi = "120363427232707228@g.us";
|
||||
|
||||
// ======================
|
||||
// 🔥 FUNCTION WA
|
||||
// ======================
|
||||
if (!function_exists('kirimWA')) {
|
||||
function kirimWA($target, $pesan) {
|
||||
if (!$target) return;
|
||||
|
||||
$token = "XjC9oSr4jECWG93JQRWf";
|
||||
|
||||
$data = [
|
||||
"target" => $target,
|
||||
"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"
|
||||
],
|
||||
CURLOPT_TIMEOUT => 10
|
||||
]);
|
||||
|
||||
curl_exec($curl);
|
||||
curl_close($curl);
|
||||
}
|
||||
}
|
||||
|
||||
// ======================
|
||||
// 🔥 TEST MODE (OPSIONAL)
|
||||
// ======================
|
||||
$TEST_MODE = false;
|
||||
|
||||
// ======================
|
||||
// 🔥 AMBIL DATA DARI DATABASE (JOIN HISTORY)
|
||||
// ======================
|
||||
$stmt = $conn->query("
|
||||
SELECT
|
||||
f.id,
|
||||
f.name,
|
||||
f.sn_onu,
|
||||
f.latitude,
|
||||
f.longitude,
|
||||
f.customer_phone,
|
||||
|
||||
o.rx_power,
|
||||
o.voltage,
|
||||
o.updated_at
|
||||
|
||||
FROM ftth_items f
|
||||
|
||||
LEFT JOIN onu_daily_history o
|
||||
ON o.sn = f.sn_onu
|
||||
|
||||
INNER JOIN item_types it
|
||||
ON it.id = f.item_type_id
|
||||
|
||||
WHERE f.sn_onu IS NOT NULL
|
||||
AND LOWER(it.name) = 'pelanggan'
|
||||
");
|
||||
|
||||
$dataItems = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
foreach ($dataItems as $item) {
|
||||
|
||||
usleep(200000);
|
||||
|
||||
$sn = strtoupper(trim($item['sn_onu']));
|
||||
$nama = $item['name'];
|
||||
$lat = $item['latitude'];
|
||||
$lng = $item['longitude'];
|
||||
$phone = $item['customer_phone'];
|
||||
|
||||
// ======================
|
||||
// 🔥 AMBIL RX & VOLT DARI DB
|
||||
// ======================
|
||||
$rx = isset($item['rx_power']) ? floatval($item['rx_power']) : null;
|
||||
$volt = isset($item['voltage']) ? floatval($item['voltage']) : null;
|
||||
|
||||
if ($rx === null && $volt === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// ======================
|
||||
// 🔥 STATUS DETEKSI
|
||||
// ======================
|
||||
$isBadRx = ($rx !== null && $rx <= -25.99);
|
||||
$isBadVolt = ($volt !== null && $volt < 3.29);
|
||||
|
||||
echo "DEBUG $sn | RX:$rx | Volt:$volt<br>";
|
||||
|
||||
if (!$isBadRx && !$isBadVolt) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// ======================
|
||||
// 🔥 CEK ANTI SPAM LOG
|
||||
// ======================
|
||||
$cek = $conn->prepare("
|
||||
SELECT last_rx, last_voltage
|
||||
FROM notif_device_log
|
||||
WHERE sn = ?
|
||||
");
|
||||
$cek->execute([$sn]);
|
||||
$last = $cek->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
$lastRx = $last['last_rx'] ?? null;
|
||||
$lastVolt = $last['last_voltage'] ?? null;
|
||||
|
||||
$isChanged = ($lastRx != $rx || $lastVolt != $volt);
|
||||
|
||||
if (!$isChanged) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// ======================
|
||||
// 🔥 MAPS
|
||||
// ======================
|
||||
$maps = ($lat && $lng)
|
||||
? "https://www.google.com/maps?q={$lat},{$lng}"
|
||||
: "-";
|
||||
|
||||
// ======================
|
||||
// 🔥 FORMAT NOMOR
|
||||
// ======================
|
||||
$phoneFix = null;
|
||||
if ($phone) {
|
||||
$phoneFix = preg_replace('/[^0-9]/', '', $phone);
|
||||
if (substr($phoneFix, 0, 1) == '0') {
|
||||
$phoneFix = '62' . substr($phoneFix, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// ======================
|
||||
// 🔥 NOTIFIKASI
|
||||
// ======================
|
||||
|
||||
if ($isBadRx && $isBadVolt) {
|
||||
|
||||
kirimWA($phoneFix,
|
||||
"🚨 *Gangguan Jaringan & Perangkat*\n\n" .
|
||||
"Yth. {$nama},\n" .
|
||||
"📉 RX: " . number_format($rx,2) . " dBm\n" .
|
||||
"🔌 Volt: " . number_format($volt,2) . " V\n\n" .
|
||||
"Gangguan pada jaringan & perangkat terdeteksi.\n" .
|
||||
"Estimasi perbaikan ±2 jam.\n\n" .
|
||||
"Mohon maaf 🙏"
|
||||
);
|
||||
|
||||
kirimWA($groupTeknisi,
|
||||
"🚨 *DOUBLE ISSUE*\n\n" .
|
||||
"👤 {$nama}\n" .
|
||||
"📉 RX: {$rx} dBm\n" .
|
||||
"🔌 Volt: " . number_format($volt,2) . " V\n\n" .
|
||||
"📡 Gangguan IKR + ONU\n" .
|
||||
"📍 {$maps}\n\n" .
|
||||
"⚠ PRIORITAS TINGGI"
|
||||
);
|
||||
|
||||
} elseif ($isBadRx) {
|
||||
|
||||
kirimWA($phoneFix,
|
||||
"🚨 *Gangguan Jaringan*\n\n" .
|
||||
"Yth. {$nama},\n" .
|
||||
"📉 RX: {$rx} dBm\n\n" .
|
||||
"Terjadi gangguan pada jalur fiber.\n" .
|
||||
"Tim akan segera ke lokasi.\n"
|
||||
);
|
||||
|
||||
kirimWA($groupTeknisi,
|
||||
"🚨 *REDAMAN TIDAK NORMAL*\n\n" .
|
||||
"👤 {$nama}\n" .
|
||||
"📉 RX: {$rx} dBm\n" .
|
||||
"📍 {$maps}"
|
||||
);
|
||||
|
||||
} elseif ($isBadVolt) {
|
||||
|
||||
kirimWA($phoneFix,
|
||||
"⚡ *Perangkat Bermasalah*\n\n" .
|
||||
"Yth. {$nama},\n" .
|
||||
"🔌 Volt: " . number_format($volt,2) . " V\n\n" .
|
||||
"Indikasi power tidak stabil.\n"
|
||||
);
|
||||
|
||||
kirimWA($groupTeknisi,
|
||||
"⚡ *VOLTAGE TIDAK NORMAL*\n\n" .
|
||||
"👤 {$nama}\n" .
|
||||
"🔌 Volt: " . number_format($volt,2) . " V\n" .
|
||||
"📍 {$maps}"
|
||||
);
|
||||
}
|
||||
|
||||
// ======================
|
||||
// 🔥 UPDATE LOG ANTI SPAM
|
||||
// ======================
|
||||
$update = $conn->prepare("
|
||||
INSERT INTO notif_device_log (sn, last_rx, last_voltage, last_sent)
|
||||
VALUES (?, ?, ?, NOW())
|
||||
ON DUPLICATE KEY UPDATE
|
||||
last_rx = VALUES(last_rx),
|
||||
last_voltage = VALUES(last_voltage),
|
||||
last_sent = NOW()
|
||||
");
|
||||
|
||||
$update->execute([$sn, $rx, $volt]);
|
||||
|
||||
echo "✅ SENT $sn | RX:$rx | V:$volt<br>";
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
# Netscape HTTP Cookie File
|
||||
# https://curl.se/docs/http-cookies.html
|
||||
# This file was generated by libcurl! Edit at your own risk.
|
||||
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
<?php
|
||||
|
||||
// ================= CONFIG =================
|
||||
$login_url = "http://10.255.254.21/login";
|
||||
$data_url = "http://10.255.254.21/ontinfo_table";
|
||||
|
||||
$username = "root";
|
||||
$password = "@lokal234";
|
||||
|
||||
$cookie = __DIR__ . "/cookie.txt";
|
||||
|
||||
// ================= LOGIN =================
|
||||
$ch = curl_init();
|
||||
|
||||
curl_setopt($ch, CURLOPT_URL, $login_url);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([
|
||||
"username" => $username,
|
||||
"password" => $password
|
||||
]));
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_COOKIEJAR, $cookie);
|
||||
curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie);
|
||||
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
|
||||
|
||||
$result = curl_exec($ch);
|
||||
|
||||
if (curl_errno($ch)) {
|
||||
die("LOGIN ERROR : " . curl_error($ch));
|
||||
}
|
||||
|
||||
curl_close($ch);
|
||||
|
||||
// ================= AMBIL DATA =================
|
||||
$ch = curl_init();
|
||||
|
||||
curl_setopt($ch, CURLOPT_URL, $data_url);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie);
|
||||
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
|
||||
if (curl_errno($ch)) {
|
||||
die("DATA ERROR : " . curl_error($ch));
|
||||
}
|
||||
|
||||
curl_close($ch);
|
||||
|
||||
// simpan raw response
|
||||
file_put_contents("debug_hsgq.json", $response);
|
||||
|
||||
// ================= TAMPILKAN =================
|
||||
$data = json_decode($response, true);
|
||||
|
||||
echo "<h3>RAW RESPONSE</h3>";
|
||||
|
||||
if ($data === null) {
|
||||
echo "<pre>";
|
||||
echo htmlspecialchars(substr($response, 0, 5000));
|
||||
echo "</pre>";
|
||||
exit;
|
||||
}
|
||||
|
||||
echo "<h3>FIELD YANG TERSEDIA</h3>";
|
||||
|
||||
echo "<pre>";
|
||||
|
||||
if (isset($data['data'][0])) {
|
||||
|
||||
echo "=== DATA ONU PERTAMA ===\n\n";
|
||||
|
||||
print_r($data['data'][0]);
|
||||
|
||||
echo "\n\n=== NAMA FIELD ===\n\n";
|
||||
|
||||
foreach ($data['data'][0] as $key => $value) {
|
||||
echo $key . "\n";
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
print_r($data);
|
||||
|
||||
}
|
||||
|
||||
echo "</pre>";
|
||||
|
|
@ -0,0 +1 @@
|
|||
Not found
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,95 @@
|
|||
<?php
|
||||
require_once __DIR__ . '/../config/database.php';
|
||||
header('Content-Type: application/json');
|
||||
header('Access-Control-Allow-Origin: *');
|
||||
|
||||
$database = new Database();
|
||||
$conn = $database->getConnection();
|
||||
|
||||
$id = intval($_GET['id'] ?? 0);
|
||||
if ($id <= 0) {
|
||||
echo json_encode(['success'=>false,'message'=>'ID tidak valid']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$stmt = $conn->prepare("SELECT serial_number FROM ftth_items WHERE id=:id");
|
||||
$stmt->bindParam(':id', $id, PDO::PARAM_INT);
|
||||
$stmt->execute();
|
||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$row) {
|
||||
echo json_encode(['success'=>false,'message'=>'Data tidak ditemukan']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$serialNumber = $row['serial_number'];
|
||||
|
||||
// Default nilai
|
||||
$rxPower = null;
|
||||
$pppoeIP = null;
|
||||
$voltage = null;
|
||||
$uptime = null;
|
||||
$inform = null;
|
||||
|
||||
if ($serialNumber) {
|
||||
$query = json_encode(["_id" => $serialNumber]);
|
||||
$url = "http://rmtstb.megadataisp.net:7557/devices/?query=" . urlencode($query);
|
||||
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
$response = curl_exec($ch);
|
||||
curl_close($ch);
|
||||
|
||||
if ($response) {
|
||||
$data = json_decode($response, true);
|
||||
|
||||
if (isset($data[0])) {
|
||||
|
||||
// RX Power
|
||||
if (isset($data[0]['VirtualParameters']['RXPower']['_value'])) {
|
||||
$rxPower = $data[0]['VirtualParameters']['RXPower']['_value'] . " dBm";
|
||||
}
|
||||
|
||||
// PPPoE IP
|
||||
if (isset($data[0]['VirtualParameters']['pppoeIP']['_value'])) {
|
||||
$pppoeIP = $data[0]['VirtualParameters']['pppoeIP']['_value'];
|
||||
}
|
||||
|
||||
// Voltage (VP)
|
||||
if (isset($data[0]['VirtualParameters']['Voltage']['_value'])) {
|
||||
$rawVoltage = $data[0]['VirtualParameters']['Voltage']['_value'];
|
||||
|
||||
// konversi ke Volt (dibagi 10000)
|
||||
$voltageConvert = $rawVoltage / 10000;
|
||||
|
||||
// ambil 2 angka belakang
|
||||
$voltage = number_format($voltageConvert, 2) . " V";
|
||||
}
|
||||
|
||||
// Uptime (detik → HH:MM:SS)
|
||||
if (isset($data[0]['InternetGatewayDevice']['DeviceInfo']['UpTime']['_value'])) {
|
||||
$seconds = (int)$data[0]['InternetGatewayDevice']['DeviceInfo']['UpTime']['_value'];
|
||||
$uptime = gmdate("H:i:s", $seconds);
|
||||
}
|
||||
|
||||
// Inform terakhir
|
||||
if (isset($data[0]['Events']['Inform'])) {
|
||||
$inform = $data[0]['Events']['Inform'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Output JSON
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'data' => [
|
||||
'serial_number' => $serialNumber,
|
||||
'rx_power' => $rxPower ?? 'Tidak tersedia',
|
||||
'pppoe_ip' => $pppoeIP ?? 'Tidak tersedia',
|
||||
'voltage' => $voltage ?? 'Tidak tersedia',
|
||||
'uptime' => $uptime ?? 'Tidak tersedia',
|
||||
'inform' => $inform ?? 'Tidak tersedia'
|
||||
]
|
||||
]);
|
||||
|
|
@ -1,19 +1,47 @@
|
|||
<?php
|
||||
require_once __DIR__ . '/../config/database.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
header('Access-Control-Allow-Origin: *');
|
||||
//header('Content-Type: application/json');
|
||||
|
||||
|
||||
$database = new Database();
|
||||
$conn = $database->getConnection();
|
||||
|
||||
// ======================
|
||||
// 🔥 TEST MODE
|
||||
// ======================
|
||||
// url test : http://localhost/ftthplanner/api/detail_redaman.php?id=859
|
||||
$TEST_MODE = true;
|
||||
|
||||
$TEST_DATA = [
|
||||
"08A5AE-F609-ZXIC6A7451C337D" => [
|
||||
"rx" => -23,
|
||||
"volt" => 2.9
|
||||
],
|
||||
//id 836 Client putri 081252188305 sn : GGCL298413e0
|
||||
"08A5AE-F609-ZXIC03C33C4B433" => [
|
||||
"rx" => -32,
|
||||
"volt" => 3.2
|
||||
],
|
||||
//id : 859 difta dwi RT via kantor sn : CDTCaf8ee4ed
|
||||
"08A5AE-F609-ZXIC1D6EF05BDD7" => [
|
||||
"rx" => -28,
|
||||
"volt" => 3.2
|
||||
],
|
||||
];
|
||||
|
||||
// ======================
|
||||
// 🔥 VALIDASI ID
|
||||
// ======================
|
||||
$id = intval($_GET['id'] ?? 0);
|
||||
if ($id <= 0) {
|
||||
echo json_encode(['success'=>false,'message'=>'ID tidak valid']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// ======================
|
||||
// 🔥 AMBIL SN
|
||||
// ======================
|
||||
$stmt = $conn->prepare("SELECT serial_number FROM ftth_items WHERE id=:id");
|
||||
$stmt->bindParam(':id', $id, PDO::PARAM_INT);
|
||||
$stmt->execute();
|
||||
|
|
@ -24,43 +52,100 @@ if (!$row) {
|
|||
exit;
|
||||
}
|
||||
|
||||
$serialNumber = $row['serial_number'];
|
||||
// 🔥 NORMALISASI SN
|
||||
$serialNumber = strtoupper(trim($row['serial_number']));
|
||||
|
||||
// Ambil RX Power dan PPPoE IP dari API
|
||||
// ======================
|
||||
// 🔥 DEFAULT
|
||||
// ======================
|
||||
$rxPower = null;
|
||||
$pppoeIP = null;
|
||||
$voltage = null;
|
||||
$uptime = null;
|
||||
$inform = null;
|
||||
|
||||
if ($serialNumber) {
|
||||
$query = json_encode(["_id" => $serialNumber]);
|
||||
$url = "http://localhost:7557/devices/?query=" . urlencode($query);
|
||||
// ======================
|
||||
// 🔥 CEK: MANUAL ATAU AUTO
|
||||
// ======================
|
||||
$useManual = ($TEST_MODE && isset($TEST_DATA[$serialNumber]));
|
||||
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
$response = curl_exec($ch);
|
||||
curl_close($ch);
|
||||
if ($useManual) {
|
||||
|
||||
if ($response) {
|
||||
$data = json_decode($response, true);
|
||||
// ======================
|
||||
// 🔥 MODE MANUAL (HANYA SN YANG ADA DI TEST_DATA)
|
||||
// ======================
|
||||
$rxPower = $TEST_DATA[$serialNumber]['rx'] . " dBm";
|
||||
$voltage = number_format($TEST_DATA[$serialNumber]['volt'], 2) . " V";
|
||||
|
||||
// Ambil RX Power
|
||||
if (isset($data[0]['VirtualParameters']['RXPower']['_value'])) {
|
||||
$rxPower = $data[0]['VirtualParameters']['RXPower']['_value'] . " dBm";
|
||||
}
|
||||
$pppoeIP = "TEST MODE";
|
||||
$uptime = "-";
|
||||
$inform = "MANUAL";
|
||||
|
||||
// Ambil PPPoE IP
|
||||
if (isset($data[0]['VirtualParameters']['pppoeIP']['_value'])) {
|
||||
$pppoeIP = $data[0]['VirtualParameters']['pppoeIP']['_value'];
|
||||
} else {
|
||||
|
||||
// ======================
|
||||
// 🔥 MODE AUTO (GENIEACS)
|
||||
// ======================
|
||||
if ($serialNumber) {
|
||||
|
||||
$query = json_encode(["_id" => $serialNumber]);
|
||||
$url = "http://rmtstb.megadataisp.net:7557/devices/?query=" . urlencode($query);
|
||||
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 5);
|
||||
$response = curl_exec($ch);
|
||||
curl_close($ch);
|
||||
|
||||
if ($response) {
|
||||
$data = json_decode($response, true);
|
||||
|
||||
if (isset($data[0])) {
|
||||
|
||||
// RX
|
||||
if (isset($data[0]['VirtualParameters']['RXPower']['_value'])) {
|
||||
$rxPower = $data[0]['VirtualParameters']['RXPower']['_value'] . " dBm";
|
||||
}
|
||||
|
||||
// PPPoE
|
||||
if (isset($data[0]['VirtualParameters']['pppoeIP']['_value'])) {
|
||||
$pppoeIP = $data[0]['VirtualParameters']['pppoeIP']['_value'];
|
||||
}
|
||||
|
||||
// Voltage
|
||||
if (isset($data[0]['VirtualParameters']['Voltage']['_value'])) {
|
||||
$rawVoltage = $data[0]['VirtualParameters']['Voltage']['_value'];
|
||||
$voltageConvert = $rawVoltage / 10000;
|
||||
$voltage = number_format($voltageConvert, 2) . " V";
|
||||
}
|
||||
|
||||
// Uptime
|
||||
if (isset($data[0]['InternetGatewayDevice']['DeviceInfo']['UpTime']['_value'])) {
|
||||
$seconds = (int)$data[0]['InternetGatewayDevice']['DeviceInfo']['UpTime']['_value'];
|
||||
$uptime = gmdate("H:i:s", $seconds);
|
||||
}
|
||||
|
||||
// Inform
|
||||
if (isset($data[0]['Events']['Inform'])) {
|
||||
$inform = $data[0]['Events']['Inform'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Output JSON untuk AJAX
|
||||
// ======================
|
||||
// 🔥 OUTPUT
|
||||
// ======================
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'data' => [
|
||||
'serial_number' => $serialNumber,
|
||||
'rx_power' => $rxPower ?? 'Tidak tersedia',
|
||||
'pppoe_ip' => $pppoeIP ?? 'Tidak tersedia'
|
||||
'pppoe_ip' => $pppoeIP ?? 'Tidak tersedia',
|
||||
'voltage' => $voltage ?? 'Tidak tersedia',
|
||||
'uptime' => $uptime ?? 'Tidak tersedia',
|
||||
'inform' => $inform ?? 'Tidak tersedia'
|
||||
]
|
||||
]);
|
||||
]);
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
<?php
|
||||
|
||||
$baseData = [
|
||||
['sn' => 'VSOL00addd35', 'name' => 'ARTINI', 'rx' => -25.5300, 'volt' => 3.24],
|
||||
['sn' => 'VSOL00ad1105', 'name' => 'SATRIA PERUM PANGGUNG', 'rx' => -24.3180, 'volt' => 3.26],
|
||||
['sn' => 'HWTCa90adda0', 'name' => 'RIDA AGUS', 'rx' => -21.2500, 'volt' => 3.24],
|
||||
['sn' => 'HWTCa90add88', 'name' => 'SMP 6 SITUBONDO', 'rx' => -21.4280, 'volt' => 3.26],
|
||||
['sn' => 'HWTCa90aaf00', 'name' => 'BASRIYADI', 'rx' => -21.1360, 'volt' => 3.26],
|
||||
['sn' => 'HWTCd1b3aa28', 'name' => 'IRWAN', 'rx' => -22.0780, 'volt' => 3.28],
|
||||
['sn' => 'HWTCa90a4760', 'name' => 'TEGUH ADI', 'rx' => -21.0240, 'volt' => 3.24],
|
||||
['sn' => 'CDTCaf8e7bf1', 'name' => 'MALD/H1/1/3/13', 'rx' => -22.4420, 'volt' => 3.30],
|
||||
['sn' => 'HWTCa90aaed8', 'name' => 'FENTI', 'rx' => -21.4280, 'volt' => 3.28],
|
||||
['sn' => 'HWTCa90aabf8', 'name' => 'DESY/SUPARJO', 'rx' => -22.2920, 'volt' => 3.28],
|
||||
['sn' => 'HWTCa90addb0', 'name' => 'AYANG', 'rx' => -20.6060, 'volt' => 3.22],
|
||||
];
|
||||
|
||||
$dates = [];
|
||||
for ($d = 12; $d <= 19; $d++) {
|
||||
$dates[] = "2026-06-" . str_pad($d, 2, "0", STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
$sql = "";
|
||||
|
||||
foreach ($dates as $date) {
|
||||
foreach ($baseData as $row) {
|
||||
|
||||
// 🔥 variasi biar realistis (fluktuasi kecil)
|
||||
$rx = $row['rx'] + rand(-80, 80) / 1000; // ±0.08 dBm
|
||||
$volt = $row['volt'] + rand(-5, 5) / 1000; // ±0.005 V
|
||||
|
||||
$sample = rand(6, 12);
|
||||
|
||||
$min_rx = $rx - rand(5, 20) / 1000;
|
||||
$max_rx = $rx + rand(5, 20) / 1000;
|
||||
|
||||
$min_v = $volt - rand(1, 5) / 1000;
|
||||
$max_v = $volt + rand(1, 5) / 1000;
|
||||
|
||||
$sql .= "INSERT INTO onu_daily_history
|
||||
(sn, ont_name, rx_power, voltage, min_rx, max_rx, min_voltage, max_voltage, last_cause, sample_count, log_date, created_at, updated_at)
|
||||
VALUES
|
||||
(
|
||||
'{$row['sn']}',
|
||||
'{$row['name']}',
|
||||
" . round($rx, 4) . ",
|
||||
" . round($volt, 3) . ",
|
||||
" . round($min_rx, 4) . ",
|
||||
" . round($max_rx, 4) . ",
|
||||
" . round($min_v, 3) . ",
|
||||
" . round($max_v, 3) . ",
|
||||
NULL,
|
||||
{$sample},
|
||||
'{$date}',
|
||||
'{$date} 08:00:00',
|
||||
'{$date} 17:00:00'
|
||||
);\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
file_put_contents("onu_history_12_19_june.sql", $sql);
|
||||
|
||||
echo "DONE: file SQL berhasil dibuat";
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
<?php
|
||||
header('Content-Type: application/json');
|
||||
|
||||
require_once __DIR__ . '/../config/database.php';
|
||||
|
||||
$db = (new Database())->getConnection();
|
||||
|
||||
$id = $_GET['id'] ?? null;
|
||||
|
||||
if (!$id) {
|
||||
echo json_encode(['success' => false, 'message' => 'ID kosong']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 1. ambil SN dari ftth_items
|
||||
$query = "SELECT sn_onu FROM ftth_items WHERE id = :id";
|
||||
$stmt = $db->prepare($query);
|
||||
$stmt->bindParam(':id', $id);
|
||||
$stmt->execute();
|
||||
|
||||
$item = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$item || empty($item['sn_onu'])) {
|
||||
echo json_encode(['success' => false, 'message' => 'SN tidak ada']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 2. ambil dari onu_status
|
||||
$query = "SELECT status, rx_power, last_update
|
||||
FROM onu_status
|
||||
WHERE sn = :sn
|
||||
LIMIT 1";
|
||||
|
||||
$stmt = $db->prepare($query);
|
||||
$stmt->bindParam(':sn', $item['sn_onu']);
|
||||
$stmt->execute();
|
||||
|
||||
$data = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if ($data) {
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'data' => $data
|
||||
]);
|
||||
} else {
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'message' => 'Data ONU tidak ditemukan'
|
||||
]);
|
||||
}
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
<?php
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
require_once __DIR__ . '/../config/database.php';
|
||||
|
||||
$db = new Database();
|
||||
$conn = $db->getConnection();
|
||||
|
||||
$item_id = intval($_GET['id'] ?? 0);
|
||||
|
||||
if (!$item_id) {
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'message' => 'ID tidak valid'
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Ambil SN dari ftth_items
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
$stmt = $conn->prepare("
|
||||
SELECT sn_onu
|
||||
FROM ftth_items
|
||||
WHERE id = ?
|
||||
");
|
||||
|
||||
$stmt->execute([$item_id]);
|
||||
|
||||
$item = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$item || empty($item['sn_onu'])) {
|
||||
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'message' => 'SN tidak ada'
|
||||
]);
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
$sn = trim($item['sn_onu']);
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Ambil optical terakhir
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
$stmt = $conn->prepare("
|
||||
SELECT
|
||||
sn,
|
||||
ont_name,
|
||||
rx_power,
|
||||
voltage,
|
||||
min_rx,
|
||||
max_rx,
|
||||
min_voltage,
|
||||
max_voltage,
|
||||
updated_at
|
||||
FROM onu_daily_history
|
||||
WHERE sn = ?
|
||||
ORDER BY updated_at DESC
|
||||
LIMIT 1
|
||||
");
|
||||
|
||||
$stmt->execute([$sn]);
|
||||
|
||||
$data = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$data) {
|
||||
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'message' => 'Data optical tidak ditemukan'
|
||||
]);
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Format Data
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
$data['rx_power'] = number_format((float)$data['rx_power'], 2) . ' dBm';
|
||||
|
||||
$data['voltage'] = number_format((float)$data['voltage'], 2) . ' V';
|
||||
|
||||
$data['min_rx'] = number_format((float)$data['min_rx'], 2) . ' dBm';
|
||||
|
||||
$data['max_rx'] = number_format((float)$data['max_rx'], 2) . ' dBm';
|
||||
|
||||
$data['min_voltage'] = number_format((float)$data['min_voltage'], 2) . ' V';
|
||||
|
||||
$data['max_voltage'] = number_format((float)$data['max_voltage'], 2) . ' V';
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'data' => $data
|
||||
]);
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
require_once __DIR__ . '/../config/database.php';
|
||||
|
||||
$db = new Database();
|
||||
$conn = $db->getConnection();
|
||||
|
||||
$id = $_GET['id'] ?? null;
|
||||
|
||||
if (!$id) {
|
||||
echo json_encode(['success' => false]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// ambil SN dari item
|
||||
$stmt = $conn->prepare("SELECT sn_onu FROM ftth_items WHERE id = ?");
|
||||
$stmt->execute([$id]);
|
||||
$item = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$item) {
|
||||
echo json_encode(['success' => false]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$sn = $item['sn_onu'];
|
||||
|
||||
// ambil histori anomali saja
|
||||
$stmt = $conn->prepare("
|
||||
SELECT *
|
||||
FROM status_onu_histori
|
||||
WHERE sn = ?
|
||||
ORDER BY created_at DESC
|
||||
LIMIT 100
|
||||
");
|
||||
|
||||
$stmt->execute([$sn]);
|
||||
$data = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'sn' => $sn,
|
||||
'data' => $data
|
||||
]);
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
<?php
|
||||
header('Content-Type: application/json');
|
||||
|
||||
require_once __DIR__ . '/../config/database.php';
|
||||
|
||||
$db = new Database();
|
||||
$conn = $db->getConnection();
|
||||
|
||||
$item_id = intval($_GET['id'] ?? 0);
|
||||
|
||||
if (!$item_id) {
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'message' => 'ID tidak valid'
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Ambil SN dari ftth_items
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
$stmt = $conn->prepare("
|
||||
SELECT sn_onu, name
|
||||
FROM ftth_items
|
||||
WHERE id = ?
|
||||
");
|
||||
$stmt->execute([$item_id]);
|
||||
$item = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$item || empty($item['sn_onu'])) {
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'message' => 'SN tidak ditemukan'
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$sn = trim($item['sn_onu']);
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Ambil histori redaman & voltage
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
$stmt = $conn->prepare("
|
||||
SELECT
|
||||
rx_power,
|
||||
voltage,
|
||||
updated_at
|
||||
FROM onu_daily_history
|
||||
WHERE sn = ?
|
||||
ORDER BY updated_at DESC
|
||||
LIMIT 30
|
||||
");
|
||||
|
||||
$stmt->execute([$sn]);
|
||||
$data = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$data) {
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'message' => 'Histori kosong'
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Format biar rapi (opsional)
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
foreach ($data as &$row) {
|
||||
|
||||
// format RX
|
||||
if ($row['rx_power'] !== null) {
|
||||
$row['rx_power'] = number_format((float)$row['rx_power'], 2) . ' dBm';
|
||||
}
|
||||
|
||||
// format voltage
|
||||
if ($row['voltage'] !== null) {
|
||||
$row['voltage'] = number_format((float)$row['voltage'], 2) . ' V';
|
||||
}
|
||||
|
||||
// tanggal lebih enak dibaca
|
||||
$row['updated_at'] = date('d-m-Y H:i:s', strtotime($row['updated_at']));
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'sn' => $sn,
|
||||
'data' => $data
|
||||
]);
|
||||
313
api/items.php
313
api/items.php
|
|
@ -11,81 +11,65 @@ if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
|
|||
exit();
|
||||
}
|
||||
|
||||
session_start(); // <-- Tambahan penting
|
||||
|
||||
require_once __DIR__ . '/../config/database.php';
|
||||
|
||||
// Ambil user info dari session
|
||||
$user_id = $_SESSION['user_id'] ?? null;
|
||||
$role = $_SESSION['role'] ?? 'teknisi1';
|
||||
|
||||
// Function to manually parse multipart form data
|
||||
function parseMultipartFormData($input, $contentType) {
|
||||
$data = array();
|
||||
|
||||
// Extract boundary from content type
|
||||
if (preg_match('/boundary=(.+)$/', $contentType, $matches)) {
|
||||
$boundary = $matches[1];
|
||||
|
||||
// Split the input by boundary
|
||||
$parts = array_slice(explode('--' . $boundary, $input), 1);
|
||||
|
||||
foreach ($parts as $part) {
|
||||
// Skip empty parts and closing boundary
|
||||
if (trim($part) == '--' || empty(trim($part))) continue;
|
||||
|
||||
// Split headers and body
|
||||
$sections = explode("\r\n\r\n", $part, 2);
|
||||
if (count($sections) != 2) continue;
|
||||
|
||||
$headers = $sections[0];
|
||||
$body = rtrim($sections[1], "\r\n");
|
||||
|
||||
// Extract field name from Content-Disposition header
|
||||
if (preg_match('/name="([^"]*)"/', $headers, $matches)) {
|
||||
$fieldName = $matches[1];
|
||||
$data[$fieldName] = $body;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
$database = new Database();
|
||||
$db = $database->getConnection();
|
||||
|
||||
// Handle method override and multipart data parsing
|
||||
$method = $_SERVER['REQUEST_METHOD'];
|
||||
$parsed_data = array();
|
||||
|
||||
// Parse multipart form data manually if needed
|
||||
if (($method === 'PUT' || $method === 'PATCH') && empty($_POST) &&
|
||||
isset($_SERVER['CONTENT_TYPE']) && strpos($_SERVER['CONTENT_TYPE'], 'multipart/form-data') !== false) {
|
||||
|
||||
// Force treat as POST to get parsed form data
|
||||
$raw_input = file_get_contents('php://input');
|
||||
$parsed_data = parseMultipartFormData($raw_input, $_SERVER['CONTENT_TYPE']);
|
||||
|
||||
// If we found form data, treat this as a method override
|
||||
if (!empty($parsed_data)) {
|
||||
if (isset($parsed_data['_method'])) {
|
||||
$method = strtoupper($parsed_data['_method']);
|
||||
unset($parsed_data['_method']);
|
||||
}
|
||||
// Populate $_POST with parsed data for compatibility
|
||||
$_POST = $parsed_data;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for X-HTTP-Method-Override header
|
||||
if (isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])) {
|
||||
$method = $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'];
|
||||
}
|
||||
|
||||
// Check for _method parameter (Laravel style) - now works with manually parsed data too
|
||||
if (isset($_POST['_method'])) {
|
||||
$method = strtoupper($_POST['_method']);
|
||||
unset($_POST['_method']); // Clean it up
|
||||
unset($_POST['_method']);
|
||||
}
|
||||
|
||||
$response = array('success' => false, 'message' => '', 'data' => null);
|
||||
|
||||
// Log only important requests for production
|
||||
if ($method === 'PUT' || $method === 'DELETE') {
|
||||
error_log("FTTH API - " . $method . " request, ID: " . (isset($_POST['id']) ? $_POST['id'] : 'N/A'));
|
||||
}
|
||||
|
|
@ -94,24 +78,30 @@ try {
|
|||
switch($method) {
|
||||
case 'GET':
|
||||
if (isset($_GET['id'])) {
|
||||
// Get single item
|
||||
$query = "SELECT i.*, i.serial_number,
|
||||
it.name as item_type_name, it.icon, it.color,
|
||||
tc.color_name as tube_color_name, tc.hex_code,
|
||||
cc.color_name as core_color_name, cc.hex_code as core_hex_code,
|
||||
sm.ratio as splitter_main_ratio,
|
||||
so.ratio as splitter_odp_ratio
|
||||
FROM ftth_items i
|
||||
LEFT JOIN item_types it ON i.item_type_id = it.id
|
||||
LEFT JOIN tube_colors tc ON i.tube_color_id = tc.id
|
||||
LEFT JOIN tube_colors cc ON i.core_color_id = cc.id
|
||||
LEFT JOIN splitter_types sm ON i.splitter_main_id = sm.id
|
||||
LEFT JOIN splitter_types so ON i.splitter_odp_id = so.id
|
||||
WHERE i.id = :id";
|
||||
it.name as item_type_name, it.icon, it.color,
|
||||
tc.color_name as tube_color_name, tc.hex_code,
|
||||
cc.color_name as core_color_name, cc.hex_code as core_hex_code,
|
||||
sm.ratio as splitter_main_ratio,
|
||||
so.ratio as splitter_odp_ratio
|
||||
FROM ftth_items i
|
||||
LEFT JOIN item_types it ON i.item_type_id = it.id
|
||||
LEFT JOIN tube_colors tc ON i.tube_color_id = tc.id
|
||||
LEFT JOIN tube_colors cc ON i.core_color_id = cc.id
|
||||
LEFT JOIN splitter_types sm ON i.splitter_main_id = sm.id
|
||||
LEFT JOIN splitter_types so ON i.splitter_odp_id = so.id
|
||||
WHERE i.id = :id";
|
||||
|
||||
// kalau teknisi, batasi ke user_id
|
||||
if ($role !== 'engineer') {
|
||||
$query .= " AND i.user_id = :user_id";
|
||||
}
|
||||
|
||||
|
||||
$stmt = $db->prepare($query);
|
||||
$stmt->bindParam(':id', $_GET['id']);
|
||||
if ($role !== 'engineer') {
|
||||
$stmt->bindParam(':user_id', $user_id);
|
||||
}
|
||||
$stmt->execute();
|
||||
|
||||
$item = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
|
@ -122,23 +112,28 @@ try {
|
|||
$response['message'] = 'Item not found';
|
||||
}
|
||||
} else {
|
||||
// Get all items
|
||||
$query = "SELECT i.*, i.serial_number,
|
||||
it.name as item_type_name, it.icon, it.color,
|
||||
tc.color_name as tube_color_name, tc.hex_code,
|
||||
cc.color_name as core_color_name, cc.hex_code as core_hex_code,
|
||||
sm.ratio as splitter_main_ratio,
|
||||
so.ratio as splitter_odp_ratio
|
||||
FROM ftth_items i
|
||||
LEFT JOIN item_types it ON i.item_type_id = it.id
|
||||
LEFT JOIN tube_colors tc ON i.tube_color_id = tc.id
|
||||
LEFT JOIN tube_colors cc ON i.core_color_id = cc.id
|
||||
LEFT JOIN splitter_types sm ON i.splitter_main_id = sm.id
|
||||
LEFT JOIN splitter_types so ON i.splitter_odp_id = so.id
|
||||
ORDER BY i.created_at DESC";
|
||||
it.name as item_type_name, it.icon, it.color,
|
||||
tc.color_name as tube_color_name, tc.hex_code,
|
||||
cc.color_name as core_color_name, cc.hex_code as core_hex_code,
|
||||
sm.ratio as splitter_main_ratio,
|
||||
so.ratio as splitter_odp_ratio
|
||||
FROM ftth_items i
|
||||
LEFT JOIN item_types it ON i.item_type_id = it.id
|
||||
LEFT JOIN tube_colors tc ON i.tube_color_id = tc.id
|
||||
LEFT JOIN tube_colors cc ON i.core_color_id = cc.id
|
||||
LEFT JOIN splitter_types sm ON i.splitter_main_id = sm.id
|
||||
LEFT JOIN splitter_types so ON i.splitter_odp_id = so.id";
|
||||
|
||||
if ($role !== 'engineer') {
|
||||
$query .= " WHERE i.user_id = :user_id";
|
||||
}
|
||||
$query .= " ORDER BY i.created_at DESC";
|
||||
|
||||
|
||||
$stmt = $db->prepare($query);
|
||||
if ($role !== 'engineer') {
|
||||
$stmt->bindParam(':user_id', $user_id);
|
||||
}
|
||||
$stmt->execute();
|
||||
|
||||
$items = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
|
@ -148,81 +143,91 @@ try {
|
|||
break;
|
||||
|
||||
case 'POST':
|
||||
// Create new item
|
||||
$item_type_id = $_POST['item_type'] ?? null;
|
||||
$name = $_POST['name'] ?? null;
|
||||
$description = $_POST['description'] ?? null;
|
||||
$latitude = $_POST['latitude'] ?? null;
|
||||
$longitude = $_POST['longitude'] ?? null;
|
||||
$address = $_POST['address'] ?? null;
|
||||
$serial_number = $_POST['serial_number'] ?? null; // <--- Tambahan
|
||||
|
||||
// Handle foreign key fields - convert empty strings to NULL
|
||||
$tube_color_id = (!empty($_POST['tube_color_id']) && $_POST['tube_color_id'] !== '0') ? $_POST['tube_color_id'] : null;
|
||||
$core_used = (!empty($_POST['core_used']) && $_POST['core_used'] !== '0') ? $_POST['core_used'] : null;
|
||||
$core_color_id = (!empty($_POST['core_color_id']) && $_POST['core_color_id'] !== '0') ? $_POST['core_color_id'] : null;
|
||||
$item_cable_type = $_POST['item_cable_type'] ?? 'distribution';
|
||||
$total_core_capacity = $_POST['total_core_capacity'] ?? 24;
|
||||
$splitter_main_id = (!empty($_POST['splitter_main_id']) && $_POST['splitter_main_id'] !== '0') ? $_POST['splitter_main_id'] : null;
|
||||
$splitter_odp_id = (!empty($_POST['splitter_odp_id']) && $_POST['splitter_odp_id'] !== '0') ? $_POST['splitter_odp_id'] : null;
|
||||
$status = $_POST['status'] ?? 'active';
|
||||
|
||||
if (!$item_type_id || !$name || !$latitude || !$longitude) {
|
||||
$response['message'] = 'Required fields missing';
|
||||
break;
|
||||
}
|
||||
|
||||
$query = "INSERT INTO ftth_items (
|
||||
item_type_id, name, description, latitude, longitude, address, serial_number,
|
||||
tube_color_id, core_used, core_color_id, item_cable_type, total_core_capacity,
|
||||
splitter_main_id, splitter_odp_id, status
|
||||
) VALUES (
|
||||
:item_type_id, :name, :description, :latitude, :longitude, :address, :serial_number,
|
||||
:tube_color_id, :core_used, :core_color_id, :item_cable_type, :total_core_capacity,
|
||||
:splitter_main_id, :splitter_odp_id, :status
|
||||
)";
|
||||
|
||||
$stmt = $db->prepare($query);
|
||||
$stmt->bindParam(':item_type_id', $item_type_id);
|
||||
$stmt->bindParam(':name', $name);
|
||||
$stmt->bindParam(':description', $description);
|
||||
$stmt->bindParam(':latitude', $latitude);
|
||||
$stmt->bindParam(':longitude', $longitude);
|
||||
$stmt->bindParam(':address', $address);
|
||||
$stmt->bindParam(':serial_number', $serial_number); // <--- Bind
|
||||
$stmt->bindParam(':tube_color_id', $tube_color_id);
|
||||
$stmt->bindParam(':core_used', $core_used);
|
||||
$stmt->bindParam(':core_color_id', $core_color_id);
|
||||
$stmt->bindParam(':item_cable_type', $item_cable_type);
|
||||
$stmt->bindParam(':total_core_capacity', $total_core_capacity);
|
||||
$stmt->bindParam(':splitter_main_id', $splitter_main_id);
|
||||
$stmt->bindParam(':splitter_odp_id', $splitter_odp_id);
|
||||
$stmt->bindParam(':status', $status);
|
||||
$item_type_id = $_POST['item_type'] ?? null;
|
||||
$name = $_POST['name'] ?? null;
|
||||
$description = $_POST['description'] ?? null;
|
||||
$latitude = $_POST['latitude'] ?? null;
|
||||
$longitude = $_POST['longitude'] ?? null;
|
||||
$address = $_POST['address'] ?? null;
|
||||
$serial_number = $_POST['serial_number'] ?? null;
|
||||
$sn_onu = $_POST['sn_onu'] ?? null;
|
||||
$customer_phone = $_POST['customer_phone'] ?? null;
|
||||
|
||||
$tube_color_id = (!empty($_POST['tube_color_id']) && $_POST['tube_color_id'] !== '0') ? $_POST['tube_color_id'] : null;
|
||||
$core_used = (!empty($_POST['core_used']) && $_POST['core_used'] !== '0') ? $_POST['core_used'] : null;
|
||||
$core_color_id = (!empty($_POST['core_color_id']) && $_POST['core_color_id'] !== '0') ? $_POST['core_color_id'] : null;
|
||||
$item_cable_type = (!empty($_POST['item_cable_type'])) ? $_POST['item_cable_type'] : null;
|
||||
$total_core_capacity = $_POST['total_core_capacity'] ?? 24;
|
||||
$splitter_main_id = (!empty($_POST['splitter_main_id']) && $_POST['splitter_main_id'] !== '0') ? $_POST['splitter_main_id'] : null;
|
||||
$splitter_odp_id = (!empty($_POST['splitter_odp_id']) && $_POST['splitter_odp_id'] !== '0') ? $_POST['splitter_odp_id'] : null;
|
||||
$status = $_POST['status'] ?? 'active';
|
||||
|
||||
if (!$item_type_id || !$name || !$latitude || !$longitude) {
|
||||
$response['message'] = 'Required fields missing';
|
||||
break;
|
||||
}
|
||||
|
||||
if ($role === 'engineer') {
|
||||
$query = "INSERT INTO ftth_items (
|
||||
item_type_id, name, description, latitude, longitude, address, serial_number, sn_onu, customer_phone,
|
||||
tube_color_id, core_used, core_color_id, item_cable_type, total_core_capacity,
|
||||
splitter_main_id, splitter_odp_id, status
|
||||
) VALUES (
|
||||
:item_type_id, :name, :description, :latitude, :longitude, :address, :serial_number, :sn_onu, :customer_phone,
|
||||
:tube_color_id, :core_used, :core_color_id, :item_cable_type, :total_core_capacity,
|
||||
:splitter_main_id, :splitter_odp_id, :status
|
||||
)";
|
||||
$stmt = $db->prepare($query);
|
||||
} else {
|
||||
$query = "INSERT INTO ftth_items (
|
||||
user_id, item_type_id, name, description, latitude, longitude, address, serial_number, sn_onu, customer_phone,
|
||||
tube_color_id, core_used, core_color_id, item_cable_type, total_core_capacity,
|
||||
splitter_main_id, splitter_odp_id, status
|
||||
) VALUES (
|
||||
:user_id, :item_type_id, :name, :description, :latitude, :longitude, :address, :serial_number, :sn_onu, :customer_phone,
|
||||
:tube_color_id, :core_used, :core_color_id, :item_cable_type, :total_core_capacity,
|
||||
:splitter_main_id, :splitter_odp_id, :status
|
||||
)";
|
||||
$stmt = $db->prepare($query);
|
||||
$stmt->bindParam(':user_id', $user_id);
|
||||
}
|
||||
|
||||
$stmt->bindParam(':item_type_id', $item_type_id);
|
||||
$stmt->bindParam(':name', $name);
|
||||
$stmt->bindParam(':description', $description);
|
||||
$stmt->bindParam(':latitude', $latitude);
|
||||
$stmt->bindParam(':longitude', $longitude);
|
||||
$stmt->bindParam(':address', $address);
|
||||
$stmt->bindParam(':serial_number', $serial_number);
|
||||
$stmt->bindParam(':tube_color_id', $tube_color_id);
|
||||
$stmt->bindParam(':core_used', $core_used);
|
||||
$stmt->bindParam(':core_color_id', $core_color_id);
|
||||
$stmt->bindParam(':item_cable_type', $item_cable_type);
|
||||
$stmt->bindParam(':total_core_capacity', $total_core_capacity);
|
||||
$stmt->bindParam(':splitter_main_id', $splitter_main_id);
|
||||
$stmt->bindParam(':splitter_odp_id', $splitter_odp_id);
|
||||
$stmt->bindParam(':status', $status);
|
||||
$stmt->bindParam(':sn_onu', $sn_onu);
|
||||
$stmt->bindParam(':customer_phone', $customer_phone);
|
||||
|
||||
if ($stmt->execute()) {
|
||||
$item_id = $db->lastInsertId();
|
||||
|
||||
// Get the created item with joins
|
||||
$query = "SELECT i.*, i.serial_number,
|
||||
it.name as item_type_name, it.icon, it.color,
|
||||
tc.color_name as tube_color_name, tc.hex_code,
|
||||
cc.color_name as core_color_name, cc.hex_code as core_hex_code,
|
||||
sm.ratio as splitter_main_ratio,
|
||||
so.ratio as splitter_odp_ratio
|
||||
FROM ftth_items i
|
||||
LEFT JOIN item_types it ON i.item_type_id = it.id
|
||||
LEFT JOIN tube_colors tc ON i.tube_color_id = tc.id
|
||||
LEFT JOIN tube_colors cc ON i.core_color_id = cc.id
|
||||
LEFT JOIN splitter_types sm ON i.splitter_main_id = sm.id
|
||||
LEFT JOIN splitter_types so ON i.splitter_odp_id = so.id
|
||||
WHERE i.id = :id";
|
||||
|
||||
|
||||
it.name as item_type_name, it.icon, it.color,
|
||||
tc.color_name as tube_color_name, tc.hex_code,
|
||||
cc.color_name as core_color_name, cc.hex_code as core_hex_code,
|
||||
sm.ratio as splitter_main_ratio,
|
||||
so.ratio as splitter_odp_ratio
|
||||
FROM ftth_items i
|
||||
LEFT JOIN item_types it ON i.item_type_id = it.id
|
||||
LEFT JOIN tube_colors tc ON i.tube_color_id = tc.id
|
||||
LEFT JOIN tube_colors cc ON i.core_color_id = cc.id
|
||||
LEFT JOIN splitter_types sm ON i.splitter_main_id = sm.id
|
||||
LEFT JOIN splitter_types so ON i.splitter_odp_id = so.id
|
||||
WHERE i.id = :id";
|
||||
$stmt = $db->prepare($query);
|
||||
$stmt->bindParam(':id', $item_id);
|
||||
$stmt->execute();
|
||||
|
||||
$response['success'] = true;
|
||||
$response['message'] = 'Item created successfully';
|
||||
$response['data'] = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
|
@ -232,67 +237,53 @@ try {
|
|||
break;
|
||||
|
||||
case 'PUT':
|
||||
// Update item - now $_POST should be properly populated
|
||||
$put_data = $_POST;
|
||||
|
||||
$id = $put_data['id'] ?? null;
|
||||
|
||||
if (!$id) {
|
||||
$response['message'] = 'ID required for update';
|
||||
break;
|
||||
}
|
||||
|
||||
// Build dynamic update query
|
||||
$update_fields = array();
|
||||
$params = array(':id' => $id);
|
||||
|
||||
$allowed_fields = ['item_type', 'name', 'description', 'latitude', 'longitude', 'address', 'serial_number','tube_color_id', 'core_used', 'core_color_id', 'item_cable_type', 'total_core_capacity', 'splitter_main_id', 'splitter_odp_id', 'status'];
|
||||
|
||||
$allowed_fields = ['item_type', 'name', 'description', 'latitude', 'longitude', 'address', 'serial_number','tube_color_id', 'core_used', 'core_color_id', 'item_cable_type', 'total_core_capacity', 'splitter_main_id', 'splitter_odp_id', 'sn_onu', 'customer_phone','status'];
|
||||
foreach ($allowed_fields as $field) {
|
||||
if (isset($put_data[$field])) {
|
||||
$db_field = $field === 'item_type' ? 'item_type_id' : $field;
|
||||
$update_fields[] = "$db_field = :$field";
|
||||
|
||||
// Handle empty values for foreign key fields - convert to NULL
|
||||
$value = $put_data[$field];
|
||||
if (in_array($field, ['tube_color_id', 'core_color_id', 'splitter_main_id', 'splitter_odp_id']) &&
|
||||
($value === '' || $value === '0' || $value === 0)) {
|
||||
if (in_array($field, ['tube_color_id', 'core_color_id', 'splitter_main_id', 'splitter_odp_id']) && ($value === '' || $value === '0' || $value === 0)) {
|
||||
$value = null;
|
||||
}
|
||||
|
||||
$params[":$field"] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($update_fields)) {
|
||||
$response['message'] = 'No fields to update';
|
||||
break;
|
||||
}
|
||||
|
||||
$query = "UPDATE ftth_items SET " . implode(', ', $update_fields) . " WHERE id = :id";
|
||||
|
||||
if ($role !== 'engineer') {
|
||||
$query .= " AND user_id = :user_id";
|
||||
$params[':user_id'] = $user_id;
|
||||
}
|
||||
$stmt = $db->prepare($query);
|
||||
|
||||
if ($stmt->execute($params)) {
|
||||
// Get updated item
|
||||
$query = "SELECT i.*, i.serial_number,
|
||||
it.name as item_type_name, it.icon, it.color,
|
||||
tc.color_name as tube_color_name, tc.hex_code,
|
||||
cc.color_name as core_color_name, cc.hex_code as core_hex_code,
|
||||
sm.ratio as splitter_main_ratio,
|
||||
so.ratio as splitter_odp_ratio
|
||||
FROM ftth_items i
|
||||
LEFT JOIN item_types it ON i.item_type_id = it.id
|
||||
LEFT JOIN tube_colors tc ON i.tube_color_id = tc.id
|
||||
LEFT JOIN tube_colors cc ON i.core_color_id = cc.id
|
||||
LEFT JOIN splitter_types sm ON i.splitter_main_id = sm.id
|
||||
LEFT JOIN splitter_types so ON i.splitter_odp_id = so.id
|
||||
WHERE i.id = :id";
|
||||
|
||||
$query = "SELECT i.*, i.serial_number, i.sn_onu, i.customer_phone,
|
||||
it.name as item_type_name, it.icon, it.color,
|
||||
tc.color_name as tube_color_name, tc.hex_code,
|
||||
cc.color_name as core_color_name, cc.hex_code as core_hex_code,
|
||||
sm.ratio as splitter_main_ratio,
|
||||
so.ratio as splitter_odp_ratio
|
||||
FROM ftth_items i
|
||||
LEFT JOIN item_types it ON i.item_type_id = it.id
|
||||
LEFT JOIN tube_colors tc ON i.tube_color_id = tc.id
|
||||
LEFT JOIN tube_colors cc ON i.core_color_id = cc.id
|
||||
LEFT JOIN splitter_types sm ON i.splitter_main_id = sm.id
|
||||
LEFT JOIN splitter_types so ON i.splitter_odp_id = so.id
|
||||
WHERE i.id = :id";
|
||||
$stmt = $db->prepare($query);
|
||||
$stmt->bindParam(':id', $id);
|
||||
$stmt->execute();
|
||||
|
||||
$response['success'] = true;
|
||||
$response['message'] = 'Item updated successfully';
|
||||
$response['data'] = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
|
@ -302,41 +293,35 @@ try {
|
|||
break;
|
||||
|
||||
case 'DELETE':
|
||||
// Delete item
|
||||
$input = file_get_contents("php://input");
|
||||
$delete_data = array();
|
||||
|
||||
// Try to parse JSON first, then form data
|
||||
$json_data = json_decode($input, true);
|
||||
if ($json_data) {
|
||||
$delete_data = $json_data;
|
||||
} else {
|
||||
parse_str($input, $delete_data);
|
||||
}
|
||||
|
||||
// Also check for regular POST data (for compatibility)
|
||||
if (empty($delete_data)) {
|
||||
$delete_data = $_POST;
|
||||
}
|
||||
|
||||
$id = $delete_data['id'] ?? null;
|
||||
|
||||
if (!$id) {
|
||||
$response['message'] = 'ID required for deletion';
|
||||
break;
|
||||
}
|
||||
|
||||
// Delete related routes first
|
||||
$query = "DELETE FROM cable_routes WHERE from_item_id = :id OR to_item_id = :id";
|
||||
$stmt = $db->prepare($query);
|
||||
$stmt->bindParam(':id', $id);
|
||||
$stmt->execute();
|
||||
|
||||
// Delete the item
|
||||
$query = "DELETE FROM ftth_items WHERE id = :id";
|
||||
if ($role !== 'engineer') {
|
||||
$query .= " AND user_id = :user_id";
|
||||
}
|
||||
$stmt = $db->prepare($query);
|
||||
$stmt->bindParam(':id', $id);
|
||||
|
||||
if ($role !== 'engineer') {
|
||||
$stmt->bindParam(':user_id', $user_id);
|
||||
}
|
||||
if ($stmt->execute()) {
|
||||
$response['success'] = true;
|
||||
$response['message'] = 'Item deleted successfully';
|
||||
|
|
@ -355,4 +340,4 @@ try {
|
|||
}
|
||||
|
||||
echo json_encode($response);
|
||||
?>
|
||||
?>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,314 @@
|
|||
<?php
|
||||
require_once __DIR__ . '/../config/database.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
$db = new Database();
|
||||
$conn = $db->getConnection();
|
||||
|
||||
|
||||
if (isset($_GET['kirim']) && isset($_GET['id'])) {
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
$id = $_GET['id'];
|
||||
|
||||
// 🔥 ambil ODP
|
||||
$stmt = $conn->prepare("
|
||||
SELECT i.id, i.name, i.latitude, i.longitude
|
||||
FROM ftth_items i
|
||||
WHERE i.id = ?
|
||||
");
|
||||
$stmt->execute([$id]);
|
||||
$odp = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$odp) {
|
||||
echo json_encode(['success' => false]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 🔥 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', $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);
|
||||
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 5);
|
||||
|
||||
$res = curl_exec($ch);
|
||||
curl_close($ch);
|
||||
|
||||
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)";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 🔥 HITUNG
|
||||
$total = count($rxValues);
|
||||
$bad = count($clientBad);
|
||||
$good = $total - $bad;
|
||||
|
||||
$avg = $total > 0 ? round(array_sum($rxValues) / $total, 2) : '-';
|
||||
$min = $total > 0 ? min($rxValues) : '-';
|
||||
|
||||
// 🔥 STATUS
|
||||
$status = "BAIK";
|
||||
|
||||
if ($total == 0) {
|
||||
$status = "NO DATA";
|
||||
} elseif ($min < -27) {
|
||||
$status = "KRITIS";
|
||||
} elseif ($avg < -25 || $bad > ($total * 0.5)) {
|
||||
$status = "BURUK";
|
||||
} elseif ($avg < -23 || $bad > ($total * 0.3)) {
|
||||
$status = "WARNING";
|
||||
}
|
||||
|
||||
// 🔥 FORMAT WA
|
||||
$maps = "https://www.google.com/maps?q={$odp['latitude']},{$odp['longitude']}";
|
||||
|
||||
$pesan = "🚨 *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);
|
||||
|
||||
echo json_encode(['success' => true]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// ✅ Ambil semua ODP
|
||||
$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);
|
||||
|
||||
$result = [];
|
||||
|
||||
foreach ($odps as $odp) {
|
||||
|
||||
// ✅ 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 = [];
|
||||
$rxList = [];
|
||||
$rxBadList = [];
|
||||
$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);
|
||||
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 5);
|
||||
|
||||
$res = curl_exec($ch);
|
||||
curl_close($ch);
|
||||
|
||||
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;
|
||||
$rxList[] = $rx;
|
||||
|
||||
if ($rx < -23) {
|
||||
$rxBadList[] = $rx;
|
||||
$clientBad[] = $c['name'] . " (" . $rx . " dBm)";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 🔴 Tidak ada data
|
||||
if (count($rxValues) == 0) {
|
||||
$result[] = [
|
||||
'id' => $odp['id'],
|
||||
'nama' => $odp['name'],
|
||||
'lat' => $odp['latitude'],
|
||||
'lng' => $odp['longitude'],
|
||||
'total' => 0,
|
||||
'avg_rx' => null,
|
||||
'bad_count' => 0,
|
||||
'min_rx' => null,
|
||||
'status' => 'NO DATA',
|
||||
'status_color' => 'secondary',
|
||||
'analisa' => 'Tidak ada data pelanggan / ONU offline semua'
|
||||
];
|
||||
continue;
|
||||
}
|
||||
|
||||
$total = count($rxValues);
|
||||
$avg = array_sum($rxValues) / $total;
|
||||
$min = min($rxValues);
|
||||
|
||||
$bad = count($rxBadList);
|
||||
|
||||
// 🔥 DETEKSI SELISIH
|
||||
$selisih = abs($avg - $min);
|
||||
|
||||
// 🔥 STATUS
|
||||
$status = "BAIK";
|
||||
$color = "success";
|
||||
|
||||
if ($selisih > 1.5) {
|
||||
$status = "WARNING";
|
||||
$color = "warning";
|
||||
}
|
||||
|
||||
if ($avg < -23 || $bad > ($total * 0.3)) {
|
||||
$status = "WARNING";
|
||||
$color = "warning";
|
||||
}
|
||||
|
||||
if ($avg < -25 || $bad > ($total * 0.5)) {
|
||||
$status = "BURUK";
|
||||
$color = "danger";
|
||||
}
|
||||
|
||||
if ($min < -27) {
|
||||
$status = "KRITIS";
|
||||
$color = "dark";
|
||||
}
|
||||
|
||||
// 🔥 ANALISA + TAMBAHAN INFORMASI CLIENT
|
||||
if ($min < ($avg - 1.5)) {
|
||||
$analisa = "Ada client lebih jelek dari yang lain → kemungkinan IKR/dropcore bermasalah";
|
||||
}
|
||||
elseif ($status === "KRITIS") {
|
||||
$analisa = "Banyak RX < -27 dBm → kemungkinan feeder/ODP bermasalah";
|
||||
}
|
||||
elseif ($status === "BURUK") {
|
||||
$analisa = "Lebih dari 50% client jelek → indikasi splitter ODP / konektor kotor";
|
||||
}
|
||||
elseif ($status === "WARNING") {
|
||||
$analisa = "Sebagian client jelek → kemungkinan jalur ke rumah";
|
||||
}
|
||||
else {
|
||||
$analisa = "Semua pelanggan normal";
|
||||
}
|
||||
|
||||
// 🔥 TAMBAHKAN DETAIL RX & CLIENT BURUK KE ANALISA
|
||||
$analisa .= " | RX: " . implode(", ", $rxList);
|
||||
|
||||
if (!empty($clientBad)) {
|
||||
$analisa .= " | Client bermasalah: " . implode(", ", $clientBad);
|
||||
}
|
||||
|
||||
$result[] = [
|
||||
'id' => $odp['id'],
|
||||
'nama' => $odp['name'],
|
||||
'lat' => $odp['latitude'],
|
||||
'lng' => $odp['longitude'],
|
||||
'total' => $total,
|
||||
'avg_rx' => round($avg, 2),
|
||||
'bad_count' => $bad,
|
||||
'min_rx' => $min,
|
||||
'status' => $status,
|
||||
'status_color' => $color,
|
||||
'analisa' => $analisa
|
||||
];
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'data' => $result
|
||||
]);
|
||||
|
|
@ -0,0 +1,167 @@
|
|||
<?php
|
||||
|
||||
require_once __DIR__ . '/../config/database.php';
|
||||
|
||||
$db = new Database();
|
||||
$conn = $db->getConnection();
|
||||
|
||||
$login_url = "http://10.255.254.21/login";
|
||||
$cookie = __DIR__ . "/cookie.txt";
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| LOGIN
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
$ch = curl_init();
|
||||
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_URL => $login_url,
|
||||
CURLOPT_POST => true,
|
||||
CURLOPT_POSTFIELDS => http_build_query([
|
||||
"username" => "root",
|
||||
"password" => "@lokal234"
|
||||
]),
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_COOKIEJAR => $cookie,
|
||||
CURLOPT_COOKIEFILE => $cookie,
|
||||
]);
|
||||
|
||||
$login = curl_exec($ch);
|
||||
|
||||
echo "<h3>LOGIN</h3>";
|
||||
echo htmlspecialchars($login);
|
||||
|
||||
curl_close($ch);
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| PREPARE INSERT
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
$stmt = $conn->prepare("
|
||||
INSERT INTO onu_daily_history
|
||||
(
|
||||
sn,
|
||||
ont_name,
|
||||
rx_power,
|
||||
voltage,
|
||||
min_rx,
|
||||
max_rx,
|
||||
min_voltage,
|
||||
max_voltage,
|
||||
sample_count,
|
||||
log_date
|
||||
)
|
||||
VALUES
|
||||
(
|
||||
:sn,
|
||||
:ont_name,
|
||||
:rx_power,
|
||||
:voltage,
|
||||
:min_rx,
|
||||
:max_rx,
|
||||
:min_voltage,
|
||||
:max_voltage,
|
||||
1,
|
||||
CURDATE()
|
||||
)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
|
||||
ont_name = VALUES(ont_name),
|
||||
|
||||
rx_power = VALUES(rx_power),
|
||||
voltage = VALUES(voltage),
|
||||
|
||||
min_rx = LEAST(min_rx, VALUES(rx_power)),
|
||||
max_rx = GREATEST(max_rx, VALUES(rx_power)),
|
||||
|
||||
min_voltage = LEAST(min_voltage, VALUES(voltage)),
|
||||
max_voltage = GREATEST(max_voltage, VALUES(voltage)),
|
||||
|
||||
sample_count = sample_count + 1,
|
||||
|
||||
updated_at = NOW()
|
||||
");
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| PORT GPON
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
$ports = [1, 2];
|
||||
|
||||
foreach ($ports as $port_id) {
|
||||
|
||||
$url = "http://10.255.254.21/gponmgmt?form=optical_onu&port_id=" . $port_id;
|
||||
|
||||
$ch = curl_init();
|
||||
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_URL => $url,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_HTTPHEADER => [
|
||||
//ini token akan selalu berubah kalo beda web
|
||||
"x-token: eb5d9d8f66242c63dc353342d3896a95"
|
||||
]
|
||||
]);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
|
||||
curl_close($ch);
|
||||
|
||||
$json = json_decode($response, true);
|
||||
|
||||
echo "<h3>PON {$port_id}</h3>";
|
||||
|
||||
if (!isset($json['data']) || empty($json['data'])) {
|
||||
echo "Tidak ada ONU ditemukan<br>";
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($json['data'] as $onu) {
|
||||
|
||||
$sn = trim($onu['ont_sn'] ?? '');
|
||||
|
||||
if (empty($sn)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$name = trim($onu['ont_name'] ?? '');
|
||||
|
||||
$rx = (float) preg_replace(
|
||||
'/[^0-9\.\-]/',
|
||||
'',
|
||||
$onu['receive_power'] ?? '0'
|
||||
);
|
||||
|
||||
$voltage = (float) preg_replace(
|
||||
'/[^0-9\.]/',
|
||||
'',
|
||||
$onu['work_voltage'] ?? '0'
|
||||
);
|
||||
|
||||
try {
|
||||
|
||||
$stmt->execute([
|
||||
':sn' => $sn,
|
||||
':ont_name' => $name,
|
||||
':rx_power' => $rx,
|
||||
':voltage' => $voltage,
|
||||
':min_rx' => $rx,
|
||||
':max_rx' => $rx,
|
||||
':min_voltage' => $voltage,
|
||||
':max_voltage' => $voltage
|
||||
]);
|
||||
|
||||
echo "SAVE : {$sn} | RX={$rx} | V={$voltage}<br>";
|
||||
|
||||
} catch (PDOException $e) {
|
||||
|
||||
echo "ERROR {$sn} : " . $e->getMessage() . "<br>";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
echo "<hr>";
|
||||
echo "<b>SELESAI</b>";
|
||||
File diff suppressed because it is too large
Load Diff
120
api/routes.php
120
api/routes.php
|
|
@ -1,4 +1,6 @@
|
|||
<?php
|
||||
session_start(); // ✅ ambil session login user
|
||||
|
||||
header('Content-Type: application/json');
|
||||
header('Access-Control-Allow-Origin: *');
|
||||
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
|
||||
|
|
@ -16,6 +18,10 @@ require_once '../config/database.php';
|
|||
$database = new Database();
|
||||
$db = $database->getConnection();
|
||||
|
||||
// Ambil user login
|
||||
$user_id = $_SESSION['user_id'] ?? null;
|
||||
$user_role = $_SESSION['role'] ?? null;
|
||||
|
||||
// Function to parse multipart/form-data for PUT requests
|
||||
function parseMultipartFormData($input, $contentType) {
|
||||
$data = array();
|
||||
|
|
@ -77,9 +83,16 @@ try {
|
|||
LEFT JOIN ftth_items fi ON r.from_item_id = fi.id
|
||||
LEFT JOIN ftth_items ti ON r.to_item_id = ti.id
|
||||
WHERE r.id = :id";
|
||||
|
||||
|
||||
if ($user_role !== 'engineer') {
|
||||
$query .= " AND r.user_id = :user_id";
|
||||
}
|
||||
|
||||
$stmt = $db->prepare($query);
|
||||
$stmt->bindParam(':id', $_GET['id']);
|
||||
if ($user_role !== 'engineer') {
|
||||
$stmt->bindParam(':user_id', $user_id);
|
||||
}
|
||||
$stmt->execute();
|
||||
|
||||
$route = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
|
@ -96,10 +109,18 @@ try {
|
|||
ti.name as to_item_name, ti.latitude as to_lat, ti.longitude as to_lng
|
||||
FROM cable_routes r
|
||||
LEFT JOIN ftth_items fi ON r.from_item_id = fi.id
|
||||
LEFT JOIN ftth_items ti ON r.to_item_id = ti.id
|
||||
ORDER BY r.created_at DESC";
|
||||
LEFT JOIN ftth_items ti ON r.to_item_id = ti.id";
|
||||
|
||||
if ($user_role !== 'engineer') {
|
||||
$query .= " WHERE r.user_id = :user_id";
|
||||
}
|
||||
|
||||
$query .= " ORDER BY r.created_at DESC";
|
||||
|
||||
$stmt = $db->prepare($query);
|
||||
if ($user_role !== 'engineer') {
|
||||
$stmt->bindParam(':user_id', $user_id);
|
||||
}
|
||||
$stmt->execute();
|
||||
|
||||
$routes = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
|
@ -111,12 +132,12 @@ try {
|
|||
case 'POST':
|
||||
// Create new route
|
||||
$from_item_id = $_POST['from_item_id'] ?? null;
|
||||
$to_item_id = $_POST['to_item_id'] ?? null;
|
||||
$to_item_id = $_POST['to_item_id'] ?? null;
|
||||
$route_coordinates = $_POST['route_coordinates'] ?? null;
|
||||
$distance = $_POST['distance'] ?? null;
|
||||
$cable_type = $_POST['cable_type'] ?? 'Fiber Optic';
|
||||
$core_count = $_POST['core_count'] ?? 24;
|
||||
$status = $_POST['status'] ?? 'planned';
|
||||
$distance = $_POST['distance'] ?? null;
|
||||
$cable_type = $_POST['cable_type'] ?? 'Fiber Optic';
|
||||
$core_count = $_POST['core_count'] ?? 24;
|
||||
$status = $_POST['status'] ?? 'planned';
|
||||
|
||||
if (!$from_item_id || !$to_item_id) {
|
||||
$response['message'] = 'From and To items are required';
|
||||
|
|
@ -136,11 +157,12 @@ try {
|
|||
$response['message'] = 'Route already exists between these items';
|
||||
break;
|
||||
}
|
||||
|
||||
$query = "INSERT INTO cable_routes (from_item_id, to_item_id, route_coordinates, distance, cable_type, core_count, status)
|
||||
VALUES (:from_item_id, :to_item_id, :route_coordinates, :distance, :cable_type, :core_count, :status)";
|
||||
|
||||
$query = "INSERT INTO cable_routes
|
||||
(from_item_id, to_item_id, route_coordinates, distance, cable_type, core_count, status, user_id)
|
||||
VALUES
|
||||
(:from_item_id, :to_item_id, :route_coordinates, :distance, :cable_type, :core_count, :status, :user_id)";
|
||||
|
||||
|
||||
$stmt = $db->prepare($query);
|
||||
$stmt->bindParam(':from_item_id', $from_item_id);
|
||||
$stmt->bindParam(':to_item_id', $to_item_id);
|
||||
|
|
@ -149,6 +171,7 @@ try {
|
|||
$stmt->bindParam(':cable_type', $cable_type);
|
||||
$stmt->bindParam(':core_count', $core_count);
|
||||
$stmt->bindParam(':status', $status);
|
||||
$stmt->bindParam(':user_id', $user_id);
|
||||
|
||||
if ($stmt->execute()) {
|
||||
$route_id = $db->lastInsertId();
|
||||
|
|
@ -161,25 +184,26 @@ try {
|
|||
break;
|
||||
|
||||
case 'PUT':
|
||||
// Update route - now using $_POST populated by parser
|
||||
$put_data = $_POST;
|
||||
|
||||
// Debug logging
|
||||
error_log('🔧 Route PUT Request - Method: ' . $method);
|
||||
error_log('🔧 POST data: ' . print_r($put_data, true));
|
||||
|
||||
$id = $put_data['id'] ?? null;
|
||||
|
||||
if (!$id) {
|
||||
error_log('❌ Route update failed: No ID provided');
|
||||
$response['message'] = 'ID required for update';
|
||||
break;
|
||||
}
|
||||
|
||||
// Pastikan teknisi hanya update kabel miliknya
|
||||
if ($user_role !== 'engineer') {
|
||||
$check = $db->prepare("SELECT id FROM cable_routes WHERE id = :id AND user_id = :user_id");
|
||||
$check->execute([':id'=>$id, ':user_id'=>$user_id]);
|
||||
if (!$check->fetch()) {
|
||||
$response['message'] = 'Not authorized to update this route';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Build dynamic update query
|
||||
$update_fields = array();
|
||||
$params = array(':id' => $id);
|
||||
|
||||
$update_fields = [];
|
||||
$params = [':id' => $id];
|
||||
$allowed_fields = ['cable_type', 'core_count', 'status', 'distance', 'route_coordinates'];
|
||||
|
||||
foreach ($allowed_fields as $field) {
|
||||
|
|
@ -195,7 +219,6 @@ try {
|
|||
}
|
||||
|
||||
$query = "UPDATE cable_routes SET " . implode(', ', $update_fields) . " WHERE id = :id";
|
||||
|
||||
$stmt = $db->prepare($query);
|
||||
|
||||
if ($stmt->execute($params)) {
|
||||
|
|
@ -208,24 +231,29 @@ try {
|
|||
|
||||
case 'DELETE':
|
||||
$input = file_get_contents("php://input");
|
||||
$delete_data = array();
|
||||
|
||||
// Try to parse JSON first, then form data
|
||||
$delete_data = [];
|
||||
$json_data = json_decode($input, true);
|
||||
if ($json_data) {
|
||||
$delete_data = $json_data;
|
||||
} else {
|
||||
parse_str($input, $delete_data);
|
||||
}
|
||||
|
||||
// Also check for regular POST data (for compatibility)
|
||||
if (empty($delete_data)) {
|
||||
$delete_data = $_POST;
|
||||
}
|
||||
|
||||
if (isset($delete_data['id'])) {
|
||||
// Delete single route
|
||||
$id = $delete_data['id'];
|
||||
|
||||
// Pastikan teknisi hanya hapus kabel miliknya
|
||||
if ($user_role !== 'engineer') {
|
||||
$check = $db->prepare("SELECT id FROM cable_routes WHERE id = :id AND user_id = :user_id");
|
||||
$check->execute([':id'=>$id, ':user_id'=>$user_id]);
|
||||
if (!$check->fetch()) {
|
||||
$response['message'] = 'Not authorized to delete this route';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$query = "DELETE FROM cable_routes WHERE id = :id";
|
||||
$stmt = $db->prepare($query);
|
||||
|
|
@ -238,25 +266,34 @@ try {
|
|||
$response['message'] = 'Failed to delete route';
|
||||
}
|
||||
} else if (isset($delete_data['item_id'])) {
|
||||
// Delete all routes connected to an item
|
||||
$item_id = $delete_data['item_id'];
|
||||
|
||||
// Get route IDs first
|
||||
$query = "SELECT id FROM cable_routes WHERE from_item_id = :item_id OR to_item_id = :item_id";
|
||||
$stmt = $db->prepare($query);
|
||||
$stmt->bindParam(':item_id', $item_id);
|
||||
$stmt->execute();
|
||||
$route_ids = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
||||
|
||||
// Delete routes
|
||||
|
||||
// Pastikan teknisi hanya hapus rute yg terkait item miliknya
|
||||
if ($user_role !== 'engineer') {
|
||||
$queryCheck = "SELECT id FROM cable_routes WHERE (from_item_id = :item_id OR to_item_id = :item_id) AND user_id = :user_id";
|
||||
$stmtCheck = $db->prepare($queryCheck);
|
||||
$stmtCheck->execute([':item_id'=>$item_id, ':user_id'=>$user_id]);
|
||||
$route_ids = $stmtCheck->fetchAll(PDO::FETCH_COLUMN);
|
||||
if (empty($route_ids)) {
|
||||
$response['message'] = 'Not authorized to delete these routes';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$query = "DELETE FROM cable_routes WHERE from_item_id = :item_id OR to_item_id = :item_id";
|
||||
if ($user_role !== 'engineer') {
|
||||
$query .= " AND user_id = :user_id";
|
||||
}
|
||||
|
||||
$stmt = $db->prepare($query);
|
||||
$stmt->bindParam(':item_id', $item_id);
|
||||
if ($user_role !== 'engineer') {
|
||||
$stmt->bindParam(':user_id', $user_id);
|
||||
}
|
||||
|
||||
if ($stmt->execute()) {
|
||||
$response['success'] = true;
|
||||
$response['message'] = 'Routes deleted successfully';
|
||||
$response['deleted_routes'] = $route_ids;
|
||||
} else {
|
||||
$response['message'] = 'Failed to delete routes';
|
||||
}
|
||||
|
|
@ -275,4 +312,3 @@ try {
|
|||
}
|
||||
|
||||
echo json_encode($response);
|
||||
?>
|
||||
|
|
@ -24,6 +24,7 @@
|
|||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
|
||||
.popup-info .info-label {
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
|
|
@ -453,6 +454,22 @@
|
|||
color: #666;
|
||||
}
|
||||
|
||||
/* ==== Animasi kelap-kelip redaman ==== */
|
||||
@keyframes blink {
|
||||
0%, 50%, 100% { opacity: 1; }
|
||||
25%, 75% { opacity: 0; }
|
||||
}
|
||||
|
||||
.red-blink {
|
||||
color: red;
|
||||
animation: blink 1s infinite;
|
||||
}
|
||||
|
||||
.green-stable {
|
||||
color: green;
|
||||
}
|
||||
|
||||
|
||||
/* Enhanced table styling for detail modal */
|
||||
.modal-body .table td {
|
||||
border-top: none;
|
||||
|
|
@ -483,4 +500,48 @@
|
|||
.badge {
|
||||
font-size: 0.85em;
|
||||
padding: 0.4em 0.6em;
|
||||
}
|
||||
|
||||
/* untuk animasi kabel*/
|
||||
.leaflet-interactive.animated-route {
|
||||
stroke-dasharray: 15, 10;
|
||||
stroke-dashoffset: 1000;
|
||||
animation: move-dash 20s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes move-dash {
|
||||
to {
|
||||
stroke-dashoffset: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* ONLINE (normal kuning) */
|
||||
.status-online {
|
||||
filter: none;
|
||||
}
|
||||
|
||||
/* POWER FAIL (abu) */
|
||||
.status-power {
|
||||
filter: grayscale(100%) brightness(0.7);
|
||||
}
|
||||
|
||||
/* LOS → MERAH + BLINK */
|
||||
.status-los {
|
||||
/* Mengubah kuning ke merah, menurunkan kecerahan untuk efek 'merah hati' */
|
||||
filter: brightness(0.5) sepia(1) saturate(500%) hue-rotate(330deg) contrast(120%);
|
||||
animation: blink 1s infinite;
|
||||
}
|
||||
|
||||
/*
|
||||
.status-los {
|
||||
filter: brightness(0) saturate(100%) invert(19%) sepia(96%) saturate(7487%) hue-rotate(357deg) brightness(97%) contrast(119%);
|
||||
animation: blink 1s infinite;
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
@keyframes blink {
|
||||
0% { opacity: 1; }
|
||||
50% { opacity: 0.3; }
|
||||
100% { opacity: 1; }
|
||||
}
|
||||
619
assets/js/app.js
619
assets/js/app.js
|
|
@ -4,8 +4,10 @@
|
|||
let editingItemId = null;
|
||||
let tempClickLatLng = null;
|
||||
|
||||
|
||||
// Initialize application
|
||||
$(document).ready(function() {
|
||||
//kirimNotifOdp(id)
|
||||
loadFormData();
|
||||
initializeEventListeners();
|
||||
|
||||
|
|
@ -123,6 +125,7 @@ function getItemTypeId(typeName) {
|
|||
}
|
||||
|
||||
// Edit existing item
|
||||
//editan saya
|
||||
function editItem(itemId) {
|
||||
editingItemId = itemId;
|
||||
|
||||
|
|
@ -150,10 +153,15 @@ function editItem(itemId) {
|
|||
$('#splitterMain').val(item.splitter_main_id);
|
||||
$('#splitterOdp').val(item.splitter_odp_id);
|
||||
$('#itemStatus').val(item.status);
|
||||
|
||||
// Calculate and display core available
|
||||
$('#serial_number').val(item.serial_number);
|
||||
$('#rx_power').val(item.rx_power || '');
|
||||
|
||||
// === Tambahan penting ===
|
||||
toggleFields();
|
||||
|
||||
// Hitung core tersedia setelah isi coreUsed
|
||||
setTimeout(() => calculateCoreAvailable(), 100);
|
||||
|
||||
|
||||
updateColorPreview();
|
||||
$('#itemModal').modal('show');
|
||||
}
|
||||
|
|
@ -164,6 +172,7 @@ function editItem(itemId) {
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
// Save item (create or update)
|
||||
function saveItem() {
|
||||
let method = editingItemId ? 'PUT' : 'POST';
|
||||
|
|
@ -356,11 +365,146 @@ function showItemList() {
|
|||
});
|
||||
}
|
||||
|
||||
function showLaporanOdp() {
|
||||
$.ajax({
|
||||
url: 'api/laporan_odp.php',
|
||||
method: 'GET',
|
||||
success: function(response) {
|
||||
if (response.data) {
|
||||
let html = generateLaporanOdpHtml(response.data);
|
||||
showModal('Laporan Kualitas ODP', html, 'modal-xl');
|
||||
} else {
|
||||
showNotification('Data tidak tersedia', 'error');
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
showNotification('Gagal memuat laporan ODP', 'error');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function generateLaporanOdpHtml(data) {
|
||||
let html = `
|
||||
<table class="table table-bordered table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Nama ODP</th>
|
||||
<th>Total Client</th>
|
||||
<th>AVG RX</th>
|
||||
<th>Client Buruk</th>
|
||||
<th>Status</th>
|
||||
<th>Map</th>
|
||||
<th>Notif</th> <!-- 🔥 TAMBAHAN -->
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
`;
|
||||
|
||||
data.forEach(item => {
|
||||
|
||||
let iconColor = 'green';
|
||||
if (item.status === 'WARNING') iconColor = 'orange';
|
||||
if (item.status === 'BURUK') iconColor = 'red';
|
||||
if (item.status === 'KRITIS') iconColor = 'darkred';
|
||||
|
||||
let rxText = '';
|
||||
let badClientText = '';
|
||||
|
||||
if (item.analisa.includes('RX:')) {
|
||||
let parts = item.analisa.split('|');
|
||||
|
||||
parts.forEach(p => {
|
||||
if (p.includes('RX:')) {
|
||||
rxText = p.replace('RX:', '').trim();
|
||||
}
|
||||
if (p.includes('Client bermasalah')) {
|
||||
badClientText = p.replace('Client bermasalah:', '').trim();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
html += `
|
||||
<tr>
|
||||
|
||||
<td>
|
||||
<b>${item.nama}</b><br>
|
||||
|
||||
<small style="color:gray">
|
||||
${item.analisa.split('|')[0]}
|
||||
</small>
|
||||
|
||||
${rxText ? `<br><small>RX: ${rxText}</small>` : ''}
|
||||
|
||||
${badClientText ? `
|
||||
<br><small style="color:red">
|
||||
⚠ ${badClientText}
|
||||
</small>` : ''}
|
||||
</td>
|
||||
|
||||
<td>${item.total}</td>
|
||||
|
||||
<td>${item.avg_rx ?? '-'} dBm</td>
|
||||
|
||||
<td>${item.bad_count}</td>
|
||||
|
||||
<td>
|
||||
<span class="badge badge-${item.status_color}">
|
||||
${item.status}
|
||||
</span>
|
||||
</td>
|
||||
|
||||
<td style="text-align:center;">
|
||||
<i class="fas fa-map-marker-alt"
|
||||
style="color:${iconColor}; cursor:pointer; font-size:18px;"
|
||||
onclick="focusOnItem(${item.id})"
|
||||
title="Lihat lokasi ODP">
|
||||
</i>
|
||||
</td>
|
||||
|
||||
<!-- 🔥 TOMBOL KIRIM WA -->
|
||||
<td style="text-align:center;">
|
||||
<button class="btn btn-sm btn-success"
|
||||
onclick="kirimNotifOdp(${item.id})"
|
||||
title="Kirim notifikasi ke teknisi">
|
||||
<i class="fas fa-paper-plane"></i>
|
||||
</button>
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
`;
|
||||
});
|
||||
|
||||
html += `</tbody></table>`;
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
function kirimNotifOdp(id) {
|
||||
console.log("Kirim ODP ID:", id);
|
||||
|
||||
fetch(`api/laporan_odp.php?kirim=1&id=${id}`)
|
||||
.then(res => res.json())
|
||||
.then(res => {
|
||||
if (res.success) {
|
||||
alert("✅ Notif terkirim ke group");
|
||||
} else {
|
||||
alert("❌ Gagal kirim");
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err);
|
||||
alert("❌ Error koneksi");
|
||||
});
|
||||
}
|
||||
// Generate item list HTML
|
||||
// Generate item list HTML
|
||||
function generateItemListHtml(items) {
|
||||
let html = `
|
||||
<div class="mb-3">
|
||||
<input type="text" id="searchInput" class="form-control" placeholder="Cari data...">
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover">
|
||||
<table class="table table-striped table-hover" id="itemTable">
|
||||
<thead class="table-dark">
|
||||
<tr>
|
||||
<th>Jenis</th>
|
||||
|
|
@ -408,11 +552,23 @@ function generateItemListHtml(items) {
|
|||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.getElementById("searchInput").addEventListener("keyup", function() {
|
||||
let filter = this.value.toLowerCase();
|
||||
let rows = document.querySelectorAll("#itemTable tbody tr");
|
||||
rows.forEach(row => {
|
||||
let text = row.innerText.toLowerCase();
|
||||
row.style.display = text.includes(filter) ? "" : "none";
|
||||
});
|
||||
});
|
||||
</script>
|
||||
`;
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
|
||||
// Get item color
|
||||
function getItemColor(typeName) {
|
||||
switch(typeName) {
|
||||
|
|
@ -454,8 +610,11 @@ function showRouteList() {
|
|||
// Generate route list HTML
|
||||
function generateRouteListHtml(routes) {
|
||||
let html = `
|
||||
<div class="mb-3">
|
||||
<input type="text" id="searchRouteInput" class="form-control" placeholder="Cari data route...">
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover">
|
||||
<table class="table table-striped table-hover" id="routeTable">
|
||||
<thead class="table-dark">
|
||||
<tr>
|
||||
<th>Dari</th>
|
||||
|
|
@ -504,11 +663,23 @@ function generateRouteListHtml(routes) {
|
|||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.getElementById("searchRouteInput").addEventListener("keyup", function() {
|
||||
let filter = this.value.toLowerCase();
|
||||
let rows = document.querySelectorAll("#routeTable tbody tr");
|
||||
rows.forEach(row => {
|
||||
let text = row.innerText.toLowerCase();
|
||||
row.style.display = text.includes(filter) ? "" : "none";
|
||||
});
|
||||
});
|
||||
</script>
|
||||
`;
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
|
||||
// Focus on route in map
|
||||
function focusOnRoute(routeId) {
|
||||
if (routes[routeId]) {
|
||||
|
|
@ -1139,6 +1310,414 @@ function showRedamanDetail(item) {
|
|||
$('#redamanDetailModalBody').html(modalHtml);
|
||||
$('#redamanDetailModal').modal('show');
|
||||
}
|
||||
|
||||
function showHistoriOnu(data, sn = '') {
|
||||
|
||||
let rows = '';
|
||||
let labels = [];
|
||||
let rxData = [];
|
||||
|
||||
data.forEach((item, index) => {
|
||||
|
||||
let rx = parseFloat(item.rx_power);
|
||||
|
||||
// ======================
|
||||
// STATUS COLOR
|
||||
// ======================
|
||||
let statusColor = '';
|
||||
let statusText = item.status;
|
||||
|
||||
if (item.status === 'LASER OUT') {
|
||||
statusColor = 'red';
|
||||
} else if (item.status === 'POWER FAIL') {
|
||||
statusColor = 'orange';
|
||||
} else {
|
||||
statusColor = 'gray';
|
||||
}
|
||||
|
||||
labels.push(item.created_at);
|
||||
rxData.push(rx);
|
||||
|
||||
rows += `
|
||||
<tr>
|
||||
<td>${index + 1}</td>
|
||||
<td>${item.created_at}</td>
|
||||
|
||||
<td style="color:${statusColor}; font-weight:bold;">
|
||||
${item.status}
|
||||
</td>
|
||||
|
||||
<td>${item.rx_power} dBm</td>
|
||||
</tr>
|
||||
`;
|
||||
});
|
||||
|
||||
let modalHtml = `
|
||||
<div class="row">
|
||||
|
||||
<div class="col-md-12">
|
||||
|
||||
<!-- CHART -->
|
||||
<div class="card mb-2">
|
||||
<div class="card-header bg-dark text-white">
|
||||
Histori Gangguan ONU - ${sn}
|
||||
</div>
|
||||
|
||||
<div class="card-body" style="padding:10px;">
|
||||
<canvas id="chartHistoriOnu" height="100"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- TABLE -->
|
||||
<div class="card">
|
||||
|
||||
<div class="card-header bg-danger text-white">
|
||||
Histori Gangguan ONU
|
||||
</div>
|
||||
|
||||
<div class="card-body table-responsive" style="max-height:400px; overflow:auto;">
|
||||
|
||||
<table class="table table-sm table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>No</th>
|
||||
<th>Waktu</th>
|
||||
<th>Status</th>
|
||||
<th>RX Power</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
${rows}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="card-footer text-right">
|
||||
<button class="btn btn-secondary btn-sm"
|
||||
onclick="$('#historiOnuModal').modal('hide')">
|
||||
Tutup
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
`;
|
||||
|
||||
// ======================
|
||||
// MODAL INIT
|
||||
// ======================
|
||||
if (!$('#historiOnuModal').length) {
|
||||
$('body').append(`
|
||||
<div class="modal fade" id="historiOnuModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-body" id="historiOnuBody"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`);
|
||||
}
|
||||
|
||||
$('#historiOnuBody').html(modalHtml);
|
||||
$('#historiOnuModal').modal('show');
|
||||
|
||||
// ======================
|
||||
// CHART
|
||||
// ======================
|
||||
setTimeout(() => {
|
||||
|
||||
if (window.historiOnuChart) {
|
||||
window.historiOnuChart.destroy();
|
||||
}
|
||||
|
||||
const ctx = document.getElementById('chartHistoriOnu');
|
||||
|
||||
window.historiOnuChart = new Chart(ctx, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: labels.reverse(),
|
||||
datasets: [
|
||||
{
|
||||
label: 'RX Power (dBm)',
|
||||
data: rxData.reverse(),
|
||||
borderColor: 'red',
|
||||
borderWidth: 2,
|
||||
pointRadius: 3,
|
||||
fill: false
|
||||
}
|
||||
]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
plugins: {
|
||||
legend: {
|
||||
labels: {
|
||||
font: {
|
||||
size: 10
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: false
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}, 300);
|
||||
}
|
||||
|
||||
function loadHistoriOnu(id) {
|
||||
if (!id) {
|
||||
console.warn('ID tidak valid:', id);
|
||||
alert('ID tidak valid');
|
||||
return;
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: 'https://10.208.176.251/ftthplanner/api/histori_onu.php',
|
||||
method: 'GET',
|
||||
data: { id: id },
|
||||
dataType: 'json',
|
||||
success: function(response) {
|
||||
|
||||
if (response && response.success && response.data) {
|
||||
showHistoriOnu(response.data, response.sn || '');
|
||||
} else {
|
||||
console.warn('Response tidak sesuai:', response);
|
||||
alert('Data histori ONU tidak ditemukan');
|
||||
}
|
||||
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
console.error('AJAX Error:', error, xhr.responseText);
|
||||
alert('Gagal memuat histori ONU');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function showHistoriRedaman(data, sn = '') {
|
||||
|
||||
let rows = '';
|
||||
let labels = [];
|
||||
let rxData = [];
|
||||
let voltData = [];
|
||||
|
||||
data.forEach((item, index) => {
|
||||
|
||||
let rx = parseFloat(item.rx_power);
|
||||
let volt = parseFloat(item.voltage);
|
||||
|
||||
// ======================
|
||||
// 🔥 RX STATUS (FIX LOGIC)
|
||||
// ======================
|
||||
let rxStatus = '';
|
||||
let rxColor = '';
|
||||
|
||||
if (rx < -25.99) {
|
||||
rxStatus = 'TROUBLE (LOW REDAMAN)';
|
||||
rxColor = 'red';
|
||||
} else if (rx >= -19.99) {
|
||||
rxStatus = 'OVER POWER (TOO STRONG)';
|
||||
rxColor = 'orange';
|
||||
} else {
|
||||
rxStatus = 'NORMAL';
|
||||
rxColor = 'green';
|
||||
}
|
||||
|
||||
// ======================
|
||||
// 🔥 VOLT STATUS
|
||||
// ======================
|
||||
let voltStatus = '';
|
||||
let voltColor = '';
|
||||
|
||||
if (volt === null || isNaN(volt)) {
|
||||
voltStatus = 'NO DATA';
|
||||
voltColor = 'gray';
|
||||
|
||||
} else if (volt > 3.29) {
|
||||
voltStatus = 'OVER VOLTAGE (TERLALU TINGGI)';
|
||||
voltColor = 'red';
|
||||
|
||||
} else if (volt < 3.2) {
|
||||
voltStatus = 'UNDER VOLTAGE (TERLALU RENDAH)';
|
||||
voltColor = 'orange';
|
||||
|
||||
} else {
|
||||
voltStatus = 'NORMAL (STABLE)';
|
||||
voltColor = 'green';
|
||||
}
|
||||
|
||||
labels.push(item.updated_at);
|
||||
rxData.push(rx);
|
||||
voltData.push(volt);
|
||||
|
||||
rows += `
|
||||
<tr>
|
||||
<td>${index + 1}</td>
|
||||
<td>${item.updated_at}</td>
|
||||
|
||||
<td style="color:${rxColor}; font-weight:bold;">
|
||||
${item.rx_power}
|
||||
<br><small>${rxStatus}</small>
|
||||
</td>
|
||||
|
||||
<td style="color:${voltColor}; font-weight:bold;">
|
||||
${item.voltage}
|
||||
<br><small>${voltStatus}</small>
|
||||
</td>
|
||||
</tr>
|
||||
`;
|
||||
});
|
||||
|
||||
let modalHtml = `
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
|
||||
<!-- CHART -->
|
||||
<div class="card mb-2">
|
||||
<div class="card-header bg-dark text-white">
|
||||
Histori Redaman & Voltage - ${sn}
|
||||
</div>
|
||||
|
||||
<div class="card-body" style="padding:10px;">
|
||||
<canvas id="chartHistoriRedaman" height="80"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- TABLE -->
|
||||
<div class="card">
|
||||
<div class="card-header bg-info text-white">
|
||||
Histori Detail
|
||||
</div>
|
||||
|
||||
<div class="card-body table-responsive" style="max-height:400px; overflow:auto;">
|
||||
<table class="table table-sm table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>No</th>
|
||||
<th>Waktu</th>
|
||||
<th>RX Power</th>
|
||||
<th>Voltage</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
${rows}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="card-footer text-right">
|
||||
<button class="btn btn-secondary btn-sm"
|
||||
onclick="$('#historiRedamanModal').modal('hide')">
|
||||
Tutup
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// modal init
|
||||
if (!$('#historiRedamanModal').length) {
|
||||
$('body').append(`
|
||||
<div class="modal fade" id="historiRedamanModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-body" id="historiRedamanBody"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`);
|
||||
}
|
||||
|
||||
$('#historiRedamanBody').html(modalHtml);
|
||||
$('#historiRedamanModal').modal('show');
|
||||
|
||||
// ======================
|
||||
// 🔥 CHART
|
||||
// ======================
|
||||
setTimeout(() => {
|
||||
|
||||
if (window.historiChart) {
|
||||
window.historiChart.destroy();
|
||||
}
|
||||
|
||||
const ctx = document.getElementById('chartHistoriRedaman');
|
||||
|
||||
window.historiChart = new Chart(ctx, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: labels.reverse(),
|
||||
datasets: [
|
||||
|
||||
{
|
||||
label: 'RX Power (dBm)',
|
||||
data: rxData.reverse(),
|
||||
borderColor: 'red',
|
||||
borderWidth: 2,
|
||||
pointRadius: 3,
|
||||
fill: false
|
||||
},
|
||||
|
||||
{
|
||||
label: 'Voltage (V)',
|
||||
data: voltData.reverse(),
|
||||
borderColor: 'blue',
|
||||
borderWidth: 2,
|
||||
pointRadius: 3,
|
||||
fill: false
|
||||
},
|
||||
|
||||
// NORMAL ZONE LINE (-20 s/d -25)
|
||||
{
|
||||
label: 'Normal Zone (-20 s/d -25)',
|
||||
data: labels.map(() => -22.5),
|
||||
borderColor: 'green',
|
||||
borderDash: [5, 5],
|
||||
pointRadius: 0,
|
||||
fill: false
|
||||
},
|
||||
|
||||
// LIMIT LOSS LINE
|
||||
{
|
||||
label: 'Limit Loss (-25 dBm)',
|
||||
data: labels.map(() => -25.99),
|
||||
borderColor: 'red',
|
||||
borderDash: [5, 5],
|
||||
pointRadius: 0,
|
||||
fill: false
|
||||
}
|
||||
|
||||
]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
plugins: {
|
||||
legend: {
|
||||
labels: {
|
||||
font: { size: 10 }
|
||||
}
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: false
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}, 300);
|
||||
|
||||
}
|
||||
// Contoh memanggil data dari API redaman
|
||||
function loadRedamanDetail(id) {
|
||||
if (!id) {
|
||||
|
|
@ -1148,12 +1727,13 @@ function loadRedamanDetail(id) {
|
|||
}
|
||||
//redaman url bisa
|
||||
$.ajax({
|
||||
url: 'https://172.16.96.10/ftthplanner/api/detail_redaman.php', // path sesuai lokasi HTML/JS
|
||||
url: 'https://10.208.176.251/ftthplanner/api/detail_redaman.php', // path sesuai lokasi HTML/JS
|
||||
method: 'GET',
|
||||
data: { id: id }, // kirim 'id' sesuai PHP
|
||||
dataType: 'json',
|
||||
success: function(response) {
|
||||
if (response && response.success && response.data) {
|
||||
let data_rx_js = response
|
||||
showRedamanDetail(response.data);
|
||||
} else {
|
||||
console.warn('Response tidak sesuai harapan:', response);
|
||||
|
|
@ -1167,8 +1747,34 @@ function loadRedamanDetail(id) {
|
|||
});
|
||||
}
|
||||
|
||||
function loadHistoriRedaman(id) {
|
||||
if (!id) {
|
||||
console.warn('ID tidak valid:', id);
|
||||
alert('ID tidak valid');
|
||||
return;
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: 'https://10.208.176.251/ftthplanner/api/histori_redaman.php',
|
||||
method: 'GET',
|
||||
data: { id: id },
|
||||
dataType: 'json',
|
||||
success: function(response) {
|
||||
|
||||
if (response && response.success && response.data) {
|
||||
showHistoriRedaman(response.data);
|
||||
} else {
|
||||
console.warn('Response tidak sesuai:', response);
|
||||
alert('Data histori tidak ditemukan');
|
||||
}
|
||||
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
console.error('AJAX Error:', error, xhr.responseText);
|
||||
alert('Gagal memuat histori redaman');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Helper function to copy text to clipboard
|
||||
function copyToClipboard(text) {
|
||||
|
|
@ -1200,6 +1806,7 @@ window.editItemEnhanced = editItemEnhanced;
|
|||
window.deleteItem = deleteItem;
|
||||
window.showItemDetail = showItemDetail;
|
||||
window.showItemList = showItemList;
|
||||
window.showLaporanOdp = showLaporanOdp;
|
||||
window.showRouteList = showRouteList;
|
||||
window.focusOnItem = focusOnItem;
|
||||
window.focusOnRoute = focusOnRoute;
|
||||
|
|
|
|||
874
assets/js/map.js
874
assets/js/map.js
File diff suppressed because it is too large
Load Diff
|
|
@ -2,7 +2,7 @@
|
|||
// auto_login_genieacs.php
|
||||
|
||||
// Konfigurasi login GenieACS
|
||||
$genieacs_host = 'http://172.16.96.10:3000';
|
||||
$genieacs_host = 'http://rmtstb.megadataisp.net:3000';
|
||||
$username = 'dwi';
|
||||
$password = 'jalo';
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
// auto_login_observium.php
|
||||
|
||||
// Konfigurasi login Observium
|
||||
$observium_host = 'http://172.16.96.6'; // ganti sesuai IP Observium
|
||||
$observium_host = 'http://rmtstb.megadataisp.net:8131'; // ganti sesuai IP Observium
|
||||
$username = 'megadata';
|
||||
$password = 'megadata';
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
import requests
|
||||
import threading
|
||||
import time
|
||||
from datetime import datetime
|
||||
|
||||
URLS = [
|
||||
"http://192.168.1.13/ftthplanner/api/ambil_dataOLT.php",
|
||||
"http://10.255.254.21/#/status"
|
||||
]
|
||||
|
||||
def hit_url(url):
|
||||
try:
|
||||
r = requests.get(url, timeout=300)
|
||||
print(
|
||||
f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] "
|
||||
f"{url} -> Status: {r.status_code}"
|
||||
)
|
||||
except Exception as e:
|
||||
print(
|
||||
f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] "
|
||||
f"{url} -> ERROR: {e}"
|
||||
)
|
||||
|
||||
while True:
|
||||
threads = []
|
||||
|
||||
for url in URLS:
|
||||
t = threading.Thread(target=hit_url, args=(url,))
|
||||
t.start()
|
||||
threads.append(t)
|
||||
|
||||
for t in threads:
|
||||
t.join()
|
||||
|
||||
time.sleep(30)
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
import requests
|
||||
import threading
|
||||
import time
|
||||
from datetime import datetime
|
||||
|
||||
URLS = [
|
||||
"http://192.168.1.13/ftthplanner/api/auto_notif_device.php",
|
||||
"http://192.168.1.13/ftthplanner/api/auto_notif_onu.php"
|
||||
]
|
||||
|
||||
def hit_url(url):
|
||||
try:
|
||||
r = requests.get(url, timeout=300)
|
||||
print(
|
||||
f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] "
|
||||
f"{url} -> Status: {r.status_code}"
|
||||
)
|
||||
except Exception as e:
|
||||
print(
|
||||
f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] "
|
||||
f"{url} -> ERROR: {e}"
|
||||
)
|
||||
|
||||
while True:
|
||||
threads = []
|
||||
|
||||
for url in URLS:
|
||||
t = threading.Thread(target=hit_url, args=(url,))
|
||||
t.start()
|
||||
threads.append(t)
|
||||
|
||||
for t in threads:
|
||||
t.join()
|
||||
|
||||
time.sleep(30)
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
let html = `
|
||||
<div>
|
||||
<h5><i class="${getItemIcon(item.item_type_name)}"></i> ${item.name}</h5>
|
||||
<div class="popup-info">
|
||||
<div class="info-row"><span class="info-label">Jenis:</span> ${item.item_type_name}</div>
|
||||
${item.description ? `<div class="info-row"><span class="info-label">Deskripsi:</span> ${item.description}</div>` : ''}
|
||||
${item.address ? `<div class="info-row"><span class="info-label">Alamat:</span> ${item.address}</div>` : ''}
|
||||
|
||||
<div class="info-row"><span class="info-label">Warna Tube:</span> ${tubeColorName}</div>
|
||||
${item.core_used ? `<div class="info-row"><span class="info-label">Core Digunakan:</span> ${item.core_used}</div>` : ''}
|
||||
<div class="info-row"><span class="info-label">Splitter Utama:</span> ${splitterMain}</div>
|
||||
<div class="info-row"><span class="info-label">Splitter ODP:</span> ${splitterOdp}</div>
|
||||
|
||||
<div class="info-row"><span class="info-label">Status:</span>
|
||||
<span class="badge badge-${getStatusBadgeClass(item.status)}">${getStatusText(item.status)}</span>
|
||||
</div>
|
||||
|
||||
<!-- Redaman hanya untuk item selain ODP, ODC, Tiang Tumpu, OLT -->
|
||||
${!['odp', 'odc', 'tiang tumpu', 'olt'].includes(item.item_type_name.toLowerCase())
|
||||
? `
|
||||
<div class="info-row" id="redaman-${item.id}">
|
||||
<span class="info-label">Redaman :</span> <em>Memuat...</em>
|
||||
</div>
|
||||
<div class="info-row" id="voltage-${item.id}">
|
||||
<span class="info-label">Voltage :</span> <em>Memuat...</em>
|
||||
</div>
|
||||
|
||||
<div class="info-row" id="onu-status-${item.id}">
|
||||
<span class="info-label">ONU Status :</span> <em>Memuat...</em>
|
||||
</div>
|
||||
|
||||
<div class="info-row" id="last-update-${item.id}">
|
||||
<span class="info-label">Last Update :</span> <em>Memuat...</em>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<span class="info-label">Customer Phone :</span>
|
||||
<span>${item.customer_phone ? item.customer_phone : '-'}</span>
|
||||
</div>
|
||||
`
|
||||
: ''}
|
||||
|
|
@ -4,7 +4,7 @@ class Database {
|
|||
private $host = "localhost";
|
||||
private $db_name = "ftth_planner";
|
||||
private $username = "root";
|
||||
private $password = "";
|
||||
private $password = "megadata";
|
||||
public $conn;
|
||||
|
||||
public function getConnection() {
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -15,7 +15,7 @@ function getRxPowerFromGenieACS($serialNumber) {
|
|||
$query = json_encode(["_id" => $serialNumber]);
|
||||
$encodedQuery = urlencode($query); // penting untuk URL-safe
|
||||
|
||||
$url = "http://localhost:7557/devices/?query={$encodedQuery}";
|
||||
$url = "http://rmtstb.megadataisp.net:7557/devices/?query={$encodedQuery}";
|
||||
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
|
|
|
|||
626
index.php
626
index.php
|
|
@ -5,8 +5,50 @@ if (!isset($_SESSION['user'])) {
|
|||
exit;
|
||||
}
|
||||
|
||||
require_once __DIR__ . '/config/database.php';
|
||||
|
||||
// Akses teknisi tidak diarahkan ke map.php, tetap di dashboard
|
||||
$database = new Database();
|
||||
$db = $database->getConnection();
|
||||
|
||||
$role = $_SESSION['role'] ?? 'teknisi1';
|
||||
$user_id = $_SESSION['user_id'] ?? null;
|
||||
|
||||
// Helper function buat hitung statistik
|
||||
function getCount($db, $item_type_id, $role, $user_id) {
|
||||
$query = "SELECT COUNT(*) as total FROM ftth_items WHERE item_type_id = :item_type_id";
|
||||
if ($role !== 'engineer') {
|
||||
$query .= " AND user_id = :user_id";
|
||||
}
|
||||
$stmt = $db->prepare($query);
|
||||
$stmt->bindParam(':item_type_id', $item_type_id, PDO::PARAM_INT);
|
||||
if ($role !== 'engineer') {
|
||||
$stmt->bindParam(':user_id', $user_id, PDO::PARAM_INT);
|
||||
}
|
||||
$stmt->execute();
|
||||
return $stmt->fetch(PDO::FETCH_ASSOC)['total'] ?? 0;
|
||||
}
|
||||
|
||||
// Mapping item_type_id sesuai database kamu
|
||||
$olt = getCount($db, 1, $role, $user_id);
|
||||
$tiang = getCount($db, 2, $role, $user_id);
|
||||
$odp = getCount($db, 3, $role, $user_id);
|
||||
$odc = getCount($db, 4, $role, $user_id);
|
||||
$pelanggan = getCount($db, 5, $role, $user_id);
|
||||
|
||||
// Routes (butuh join ke ftth_items)
|
||||
$query = "SELECT COUNT(*) as total
|
||||
FROM cable_routes r
|
||||
JOIN ftth_items i ON r.from_item_id = i.id
|
||||
WHERE 1=1";
|
||||
if ($role !== 'engineer') {
|
||||
$query .= " AND i.user_id = :user_id";
|
||||
}
|
||||
$stmt = $db->prepare($query);
|
||||
if ($role !== 'engineer') {
|
||||
$stmt->bindParam(':user_id', $user_id, PDO::PARAM_INT);
|
||||
}
|
||||
$stmt->execute();
|
||||
$routes = $stmt->fetch(PDO::FETCH_ASSOC)['total'] ?? 0;
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="id">
|
||||
|
|
@ -114,6 +156,12 @@ if (!isset($_SESSION['user'])) {
|
|||
<p>Routing Kabel</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="#" class="nav-link" onclick="showLaporanOdp()">
|
||||
<i class="nav-icon fas fa-chart-bar"></i>
|
||||
<p>Laporan ODP</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-header">NAVIGASI PETA</li>
|
||||
<li class="nav-item">
|
||||
<a href="#" class="nav-link" onclick="zoomToItems()">
|
||||
|
|
@ -132,7 +180,7 @@ if (!isset($_SESSION['user'])) {
|
|||
<ul class="nav nav-treeview">
|
||||
<li class="nav-item">
|
||||
<a href="#" class="nav-link" onclick="zoomToItemType('OLT')">
|
||||
<i class="fas fa-server nav-icon" style="color: #FF6B6B;"></i>
|
||||
<i class="fas fa-cloud nav-icon" style="color: #FF6B6B;"></i>
|
||||
<p>Zoom ke OLT</p>
|
||||
</a>
|
||||
</li>
|
||||
|
|
@ -178,7 +226,7 @@ if (!isset($_SESSION['user'])) {
|
|||
<li class="nav-header">ITEM FTTH</li>
|
||||
<li class="nav-item">
|
||||
<a href="#" class="nav-link" onclick="addNewItem('OLT')">
|
||||
<i class="nav-icon fas fa-server" style="color: #FF6B6B;"></i>
|
||||
<i class="nav-icon fas fa-cloud" style="color: #FF6B6B;"></i>
|
||||
<p>Tambah OLT</p>
|
||||
</a>
|
||||
</li>
|
||||
|
|
@ -202,7 +250,7 @@ if (!isset($_SESSION['user'])) {
|
|||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="#" class="nav-link" onclick="addNewItem('Pelanggan')">
|
||||
<i class="nav-icon fas fa-home" style="color: #FFA500;"></i>
|
||||
<i class="nav-icon fas fa-wifi" style="color: #FFA500;"></i>
|
||||
<p>Tambah Pelanggan</p>
|
||||
</a>
|
||||
</li>
|
||||
|
|
@ -243,22 +291,19 @@ if (!isset($_SESSION['user'])) {
|
|||
Peta Infrastruktur FTTH
|
||||
</h3>
|
||||
<div class="card-tools d-flex justify-content-between">
|
||||
<!-- Kanan: tombol utama -->
|
||||
<!-- Kanan: tombol utama/Revisi Tombol -->
|
||||
<div class="btn-group">
|
||||
<?php if ($_SESSION["role"] === "engineer") : ?>
|
||||
<button type="button" class="btn btn-primary btn-sm" onclick="showAddItemModal()">
|
||||
<?php else: ?>
|
||||
<button type="button" class="btn btn-primary btn-sm" disabled style="opacity:0.5;cursor:not-allowed;">
|
||||
<?php endif; ?>
|
||||
|
||||
<button type="button" class="btn btn-primary btn-sm" onclick="showAddItemModal()">
|
||||
<i class="fas fa-plus"></i> Tambah Item
|
||||
</button>
|
||||
|
||||
<?php if ($_SESSION["role"] === "engineer") : ?>
|
||||
<button type="button" class="btn btn-info btn-sm" onclick="showRoutingMode()">
|
||||
<?php else: ?>
|
||||
<button type="button" class="btn btn-info btn-sm" disabled style="opacity:0.5;cursor:not-allowed;">
|
||||
<?php endif; ?>
|
||||
<i class="fas fa-route"></i> Mode Routing
|
||||
<button type="button" class="btn btn-info btn-sm" onclick="showRoutingMode()">
|
||||
<i class="fas fa-route"></i> Route Otomatis
|
||||
</button>
|
||||
|
||||
<button type="button" class="btn btn-info btn-sm" onclick="showRoutingMode()">
|
||||
<i class="fas fa-map"></i> Route manual
|
||||
</button>
|
||||
|
||||
<button type="button" class="btn btn-success btn-sm" onclick="exportToKMZ()">
|
||||
|
|
@ -269,7 +314,7 @@ if (!isset($_SESSION['user'])) {
|
|||
<!-- Tombol GenieACS & Observium di kanan -->
|
||||
<div class="btn-group ml-2">
|
||||
<button type="button" class="btn btn-warning btn-sm" onclick="window.open('auto_login_genieacs.php', '_blank')">
|
||||
<i class="fas fa-server"></i> ACS
|
||||
<i class="fas fa-cloud"></i> ACS
|
||||
</button>
|
||||
<button type="button" class="btn btn-secondary btn-sm" onclick="window.open('auto_login_observium.php', '_blank')">
|
||||
<i class="fas fa-chart-line"></i> Observium
|
||||
|
|
@ -288,7 +333,7 @@ if (!isset($_SESSION['user'])) {
|
|||
<i class="fas fa-location-arrow"></i>
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-secondary btn-sm" onclick="map.setView([-2.5, 118], 5)" title="Zoom ke Indonesia">
|
||||
<i class="fas fa-home"></i>
|
||||
<i class="fas fa-wifi"></i>
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-secondary btn-sm" onclick="map.zoomIn()" title="Zoom In (+)">
|
||||
<i class="fas fa-plus"></i>
|
||||
|
|
@ -307,74 +352,74 @@ if (!isset($_SESSION['user'])) {
|
|||
</div>
|
||||
|
||||
<!-- Statistics Cards -->
|
||||
<div class="row">
|
||||
<div class="col-lg-2 col-6">
|
||||
<div class="small-box bg-info">
|
||||
<div class="inner">
|
||||
<h3 id="stat-olt">0</h3>
|
||||
<p>OLT</p>
|
||||
</div>
|
||||
<div class="icon">
|
||||
<i class="fas fa-server"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-2 col-6">
|
||||
<div class="small-box bg-success">
|
||||
<div class="inner">
|
||||
<h3 id="stat-tiang">0</h3>
|
||||
<p>Tiang</p>
|
||||
</div>
|
||||
<div class="icon">
|
||||
<i class="fas fa-tower-broadcast"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-2 col-6">
|
||||
<div class="small-box bg-warning">
|
||||
<div class="inner">
|
||||
<h3 id="stat-odp">0</h3>
|
||||
<p>ODP</p>
|
||||
</div>
|
||||
<div class="icon">
|
||||
<i class="fas fa-project-diagram"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-2 col-6">
|
||||
<div class="small-box bg-danger">
|
||||
<div class="inner">
|
||||
<h3 id="stat-odc">0</h3>
|
||||
<p>ODC</p>
|
||||
</div>
|
||||
<div class="icon">
|
||||
<i class="fas fa-network-wired"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-2 col-6">
|
||||
<div class="small-box bg-primary">
|
||||
<div class="inner">
|
||||
<h3 id="stat-pelanggan">0</h3>
|
||||
<p>Pelanggan</p>
|
||||
</div>
|
||||
<div class="icon">
|
||||
<i class="fas fa-home"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-2 col-6">
|
||||
<div class="small-box bg-secondary">
|
||||
<div class="inner">
|
||||
<h3 id="stat-routes">0</h3>
|
||||
<p>Routes</p>
|
||||
</div>
|
||||
<div class="icon">
|
||||
<i class="fas fa-route"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-2 col-6">
|
||||
<div class="small-box bg-info">
|
||||
<div class="inner">
|
||||
<h3><?= $olt ?></h3>
|
||||
<p>OLT</p>
|
||||
</div>
|
||||
<div class="icon">
|
||||
<i class="fas fa-cloud"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-2 col-6">
|
||||
<div class="small-box bg-success">
|
||||
<div class="inner">
|
||||
<h3><?= $tiang ?></h3>
|
||||
<p>Tiang</p>
|
||||
</div>
|
||||
<div class="icon">
|
||||
<i class="fas fa-tower-broadcast"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-2 col-6">
|
||||
<div class="small-box bg-warning">
|
||||
<div class="inner">
|
||||
<h3><?= $odp ?></h3>
|
||||
<p>ODP</p>
|
||||
</div>
|
||||
<div class="icon">
|
||||
<i class="fas fa-project-diagram"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-2 col-6">
|
||||
<div class="small-box bg-danger">
|
||||
<div class="inner">
|
||||
<h3><?= $odc ?></h3>
|
||||
<p>ODC</p>
|
||||
</div>
|
||||
<div class="icon">
|
||||
<i class="fas fa-network-wired"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-2 col-6">
|
||||
<div class="small-box bg-primary">
|
||||
<div class="inner">
|
||||
<h3><?= $pelanggan ?></h3>
|
||||
<p>Pelanggan</p>
|
||||
</div>
|
||||
<div class="icon">
|
||||
<i class="fas fa-wifi"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-2 col-6">
|
||||
<div class="small-box bg-secondary">
|
||||
<div class="inner">
|
||||
<h3><?= $routes ?></h3>
|
||||
<p>Routes</p>
|
||||
</div>
|
||||
<div class="icon">
|
||||
<i class="fas fa-route"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
|
@ -402,7 +447,8 @@ if (!isset($_SESSION['user'])) {
|
|||
<form id="itemForm">
|
||||
<div class="modal-body">
|
||||
<input type="hidden" id="itemId" name="id">
|
||||
|
||||
|
||||
<!-- Pilihan Jenis Item -->
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
|
|
@ -425,195 +471,168 @@ if (!isset($_SESSION['user'])) {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="itemDescription">Deskripsi</label>
|
||||
<textarea class="form-control" id="itemDescription" name="description" rows="3"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="itemAddress">Alamat</label>
|
||||
<textarea class="form-control" id="itemAddress" name="address" rows="2"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="itemLat">Latitude <span class="text-danger">*</span></label>
|
||||
<input type="number" step="any" class="form-control" id="itemLat" name="latitude" placeholder="Contoh: -6.2088">
|
||||
<small class="form-text text-muted">Koordinat Lintang (Latitude)</small>
|
||||
</div>
|
||||
<!-- Form Umum -->
|
||||
<div id="generalFields">
|
||||
<div class="form-group">
|
||||
<label for="itemDescription">Deskripsi</label>
|
||||
<textarea class="form-control" id="itemDescription" name="description" rows="3"></textarea>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="itemLng">Longitude <span class="text-danger">*</span></label>
|
||||
<input type="number" step="any" class="form-control" id="itemLng" name="longitude" placeholder="Contoh: 106.8456">
|
||||
<small class="form-text text-muted">Koordinat Bujur (Longitude)</small>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="itemAddress">Alamat</label>
|
||||
<textarea class="form-control" id="itemAddress" name="address" rows="2"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="itemLat">Latitude <span class="text-danger">*</span></label>
|
||||
<input type="number" step="any" class="form-control" id="itemLat" name="latitude" placeholder="Contoh: -6.2088">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="itemLng">Longitude <span class="text-danger">*</span></label>
|
||||
<input type="number" step="any" class="form-control" id="itemLng" name="longitude" placeholder="Contoh: 106.8456">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-info" role="alert">
|
||||
<i class="fas fa-info-circle"></i>
|
||||
<strong>Cara menentukan lokasi:</strong>
|
||||
<br>• Klik tombol "Pilih di Peta" di bawah ini
|
||||
<br>• Atau tutup dialog ini dan klik langsung di peta, lalu pilih "Tambah Item"
|
||||
</div>
|
||||
|
||||
<div class="text-center mb-3">
|
||||
<button type="button" class="btn btn-outline-primary" onclick="selectLocationOnMap()">
|
||||
<i class="fas fa-map-marker-alt"></i> Pilih Lokasi di Peta
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="tubeColor">Warna Tube</label>
|
||||
<select class="form-control" id="tubeColor" name="tube_color_id">
|
||||
<option value="">Pilih Warna Tube</option>
|
||||
</select>
|
||||
<!-- Form Warna Tube & Core -->
|
||||
<div id="colorFields">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="tubeColor">Warna Tube</label>
|
||||
<select class="form-control" id="tubeColor" name="tube_color_id">
|
||||
<option value="">Pilih Warna Tube</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="coreColor">Warna Core</label>
|
||||
<select class="form-control" id="coreColor" name="core_color_id">
|
||||
<option value="">Pilih Warna Core</option>
|
||||
</select>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="coreColor">Warna Core</label>
|
||||
<select class="form-control" id="coreColor" name="core_color_id">
|
||||
<option value="">Pilih Warna Core</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="cableType">Jenis Kabel</label>
|
||||
<select class="form-control" id="cableType" name="item_cable_type">
|
||||
<option value="">Pilih Jenis Kabel</option>
|
||||
<option value="backbone">Backbone</option>
|
||||
<option value="distribution">Distribution</option>
|
||||
<option value="drop_core">Drop Core</option>
|
||||
<option value="feeder">Feeder</option>
|
||||
<option value="branch">Branch</option>
|
||||
</select>
|
||||
<!-- Form Kabel -->
|
||||
<div id="cableFields">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="cableType">Jenis Kabel</label>
|
||||
<select class="form-control" id="cableType" name="item_cable_type">
|
||||
<option value="">Pilih Jenis Kabel</option>
|
||||
<option value="backbone">Backbone</option>
|
||||
<option value="distribution">Distribution</option>
|
||||
<option value="drop_core">Drop Core</option>
|
||||
<option value="feeder">Feeder</option>
|
||||
<option value="branch">Branch</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="totalCoreCapacity">Kapasitas Core Total</label>
|
||||
<select class="form-control" id="totalCoreCapacity" name="total_core_capacity">
|
||||
<option value="2">2 Core</option>
|
||||
<option value="4">4 Core</option>
|
||||
<option value="6">6 Core</option>
|
||||
<option value="8">8 Core</option>
|
||||
<option value="12">12 Core</option>
|
||||
<option value="24" selected>24 Core</option>
|
||||
<option value="48">48 Core</option>
|
||||
<option value="72">72 Core</option>
|
||||
<option value="96">96 Core</option>
|
||||
<option value="144">144 Core</option>
|
||||
<option value="216">216 Core</option>
|
||||
<option value="288">288 Core</option>
|
||||
</select>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="totalCoreCapacity">Kapasitas Core Total</label>
|
||||
<select class="form-control" id="totalCoreCapacity" name="total_core_capacity">
|
||||
<option value="1">1 Core</option>
|
||||
<option value="2">2 Core</option>
|
||||
<option value="4">4 Core</option>
|
||||
<option value="6">6 Core</option>
|
||||
<option value="8">8 Core</option>
|
||||
<option value="12">12 Core</option>
|
||||
<option value="24" selected>24 Core</option>
|
||||
<option value="48">48 Core</option>
|
||||
<option value="72">72 Core</option>
|
||||
<option value="96">96 Core</option>
|
||||
<option value="144">144 Core</option>
|
||||
<option value="216">216 Core</option>
|
||||
<option value="288">288 Core</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="coreUsed">Core yang Digunakan</label>
|
||||
<input type="number" class="form-control" id="coreUsed" name="core_used" min="0" max="288">
|
||||
<small class="form-text text-muted">Otomatis terisi dari perhitungan routing</small>
|
||||
<!-- Form Core -->
|
||||
<div id="coreFields">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="coreUsed">Core yang Digunakan</label>
|
||||
<input type="number" class="form-control" id="coreUsed" name="core_used" min="0" max="288">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label>Core Tersedia</label>
|
||||
<input type="text" class="form-control" id="coreAvailable" readonly>
|
||||
<small class="form-text text-muted">Kapasitas Total - Core Digunakan</small>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label>Core Tersedia</label>
|
||||
<input type="text" class="form-control" id="coreAvailable" readonly>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="splitterMain">Splitter Jaringan Utama</label>
|
||||
<select class="form-control" id="splitterMain" name="splitter_main_id">
|
||||
<option value="">Pilih Splitter Utama</option>
|
||||
</select>
|
||||
<!-- Form Splitter -->
|
||||
<div id="splitterFields">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="splitterMain">Splitter Jaringan Utama</label>
|
||||
<select class="form-control" id="splitterMain" name="splitter_main_id">
|
||||
<option value="">Pilih Splitter Utama</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="splitterOdp">Splitter ODP</label>
|
||||
<select class="form-control" id="splitterOdp" name="splitter_odp_id">
|
||||
<option value="">Pilih Splitter ODP</option>
|
||||
</select>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="splitterOdp">Splitter ODP</label>
|
||||
<select class="form-control" id="splitterOdp" name="splitter_odp_id">
|
||||
<option value="">Pilih Splitter ODP</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="serial_number">Serial Number ONT</label>
|
||||
<input type="text" class="form-control" id="serial_number" name="serial_number" placeholder="Contoh: ZTEGC1234567" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="rx_power">RX Power (dBm)</label>
|
||||
<!-- Form Serial Number & RX Power -->
|
||||
<div id="ontFields">
|
||||
<div class="form-group">
|
||||
<label for="serial_number">ID GenieACS TR069</label>
|
||||
<input type="text" class="form-control" id="serial_number" name="serial_number" placeholder="Contoh: 00259E-HG8546M-485754431A8E6A9C">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="rx_power">RX Power (dBm)</label>
|
||||
<input type="number" step="0.01" class="form-control" id="rx_power" name="rx_power" placeholder="Otomatis dari GenieACS" readonly>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const snInput = document.getElementById('serial_number');
|
||||
const rxPowerInput = document.getElementById('rx_power');
|
||||
|
||||
snInput.addEventListener('change', function() {
|
||||
const sn = this.value.trim();
|
||||
if (!sn) return;
|
||||
|
||||
fetch('get_rx_power.php?sn=' + encodeURIComponent(sn))
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not OK');
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
// console.log(data);
|
||||
if (data.rx_power !== null) {
|
||||
rxPowerInput.value = data.rx_power;
|
||||
} else {
|
||||
alert('⚠ ' + (data.error || 'RX Power tidak ditemukan'));
|
||||
rxPowerInput.value = '';
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
// console.error('Fetch error:', error);
|
||||
alert('❌ Gagal mengambil RX Power dari GenieACS');
|
||||
rxPowerInput.value = '';
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
<!-- Form Status -->
|
||||
<div id="statusFields">
|
||||
<div class="form-group">
|
||||
<label for="itemStatus">Status</label>
|
||||
<select class="form-control" id="itemStatus" name="status">
|
||||
<option value="active">Aktif</option>
|
||||
<option value="inactive">Tidak Aktif</option>
|
||||
<option value="maintenance">Maintenance</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Form isi data ONU -->
|
||||
<div class="form-group">
|
||||
<label for="itemStatus">Status</label>
|
||||
<select class="form-control" id="itemStatus" name="status">
|
||||
<option value="active">Aktif</option>
|
||||
<option value="inactive">Tidak Aktif</option>
|
||||
<option value="maintenance">Maintenance</option>
|
||||
</select>
|
||||
</div>
|
||||
<label for="sn_onu">SN ONU (OLT)</label>
|
||||
<input type="text" class="form-control" id="sn_onu" name="sn_onu" placeholder="Contoh: HWTC6B926A9C">
|
||||
</div>
|
||||
<!-- Form isi nomor telfon -->
|
||||
<div class="form-group">
|
||||
<label>Nomor HP Pelanggan</label>
|
||||
<input type="text" name="customer_phone" class="form-control" placeholder="628xxxxxxxxxx">
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Batal</button>
|
||||
|
|
@ -624,6 +643,109 @@ if (!isset($_SESSION['user'])) {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const itemType = document.getElementById('itemType');
|
||||
const ontFields = document.getElementById('ontFields');
|
||||
const generalFields = document.getElementById('generalFields');
|
||||
const colorFields = document.getElementById('colorFields');
|
||||
const cableFields = document.getElementById('cableFields');
|
||||
const coreFields = document.getElementById('coreFields');
|
||||
const splitterFields = document.getElementById('splitterFields');
|
||||
const statusFields = document.getElementById('statusFields');
|
||||
const addressField = document.getElementById('itemAddress');
|
||||
const addressWrapper = addressField.closest('.form-group');
|
||||
const latField = document.getElementById('itemLat').closest('.col-md-6');
|
||||
const lngField = document.getElementById('itemLng').closest('.col-md-6');
|
||||
const descriptionField = document.getElementById('description')?.closest('.form-group'); // Deskripsi
|
||||
|
||||
//editan saya
|
||||
window.toggleFields = function () {
|
||||
const value = itemType.value;
|
||||
|
||||
// Semua field default tampil
|
||||
generalFields.style.display = '';
|
||||
colorFields.style.display = '';
|
||||
cableFields.style.display = '';
|
||||
coreFields.style.display = '';
|
||||
splitterFields.style.display = '';
|
||||
statusFields.style.display = '';
|
||||
addressWrapper.style.display = '';
|
||||
if (descriptionField) descriptionField.style.display = '';
|
||||
latField.style.display = '';
|
||||
lngField.style.display = '';
|
||||
ontFields.style.display = 'none';
|
||||
|
||||
if (value === '5') {
|
||||
// === Pelanggan ===
|
||||
// Semua field tampil termasuk ONT & RX Power
|
||||
ontFields.style.display = '';
|
||||
}
|
||||
else if (value === '2') {
|
||||
// === Tiang Tumpu ===
|
||||
// Hanya tampilkan Nama Item, Jenis Item, Latitude & Longitude
|
||||
generalFields.style.display = ''; // Nama & Jenis item tetap tampil
|
||||
latField.style.display = '';
|
||||
lngField.style.display = '';
|
||||
|
||||
// Sembunyikan deskripsi, alamat, dan field lainnya
|
||||
if (descriptionField) descriptionField.style.display = 'none';
|
||||
addressWrapper.style.display = 'none';
|
||||
colorFields.style.display = 'none';
|
||||
cableFields.style.display = 'none';
|
||||
coreFields.style.display = 'none';
|
||||
splitterFields.style.display = 'none';
|
||||
statusFields.style.display = 'none';
|
||||
ontFields.style.display = 'none';
|
||||
}
|
||||
else if (value === '3' || value === '4') {
|
||||
// === ODP & ODC ===
|
||||
addressWrapper.style.display = 'none';
|
||||
ontFields.style.display = 'none';
|
||||
}
|
||||
else {
|
||||
// === OLT ===
|
||||
// Semua field tampil kecuali ONT
|
||||
ontFields.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
// Jalankan toggle pertama kali
|
||||
itemType.addEventListener('change', toggleFields);
|
||||
toggleFields();
|
||||
|
||||
// === Script Otomatis Mengambil RX Power ===
|
||||
const snInput = document.getElementById('serial_number');
|
||||
const rxPowerInput = document.getElementById('rx_power');
|
||||
|
||||
snInput.addEventListener('change', function() {
|
||||
const sn = this.value.trim();
|
||||
if (!sn) return;
|
||||
|
||||
fetch('get_rx_power.php?sn=' + encodeURIComponent(sn))
|
||||
.then(response => {
|
||||
if (!response.ok) throw new Error('Network response was not OK');
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
if (data.rx_power !== null) {
|
||||
rxPowerInput.value = data.rx_power;
|
||||
} else {
|
||||
alert('⚠ ' + (data.error || 'RX Power tidak ditemukan'));
|
||||
rxPowerInput.value = '';
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
alert('❌ Gagal mengambil RX Power dari GenieACS');
|
||||
rxPowerInput.value = '';
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- jQuery -->
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
<!-- Bootstrap 4 -->
|
||||
|
|
@ -641,11 +763,19 @@ if (!isset($_SESSION['user'])) {
|
|||
<!-- FileSaver for download -->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js"></script>
|
||||
<!-- Custom JS -->
|
||||
<!--
|
||||
<script>
|
||||
$.document().ready(function() {
|
||||
|
||||
alert('dasd')
|
||||
})
|
||||
</script>
|
||||
-->
|
||||
<script src="assets/js/map.js"></script>
|
||||
<script src="assets/js/app.js"></script>
|
||||
<script src="assets/js/kmz-export.js"></script>
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet-control-geocoder/dist/Control.Geocoder.css" />
|
||||
<script src="https://unpkg.com/leaflet-control-geocoder/dist/Control.Geocoder.js"></script>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -16,8 +16,11 @@ if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
|||
$user = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if ($user && password_verify($password, $user['password'])) {
|
||||
$_SESSION['user'] = $user['username'];
|
||||
$_SESSION['role'] = $user['role'];
|
||||
// Simpan data penting ke session
|
||||
$_SESSION['user_id'] = $user['id']; // <-- ditambahkan
|
||||
$_SESSION['user'] = $user['username'];
|
||||
$_SESSION['role'] = $user['role'];
|
||||
|
||||
header("Location: index.php");
|
||||
exit;
|
||||
} else {
|
||||
|
|
|
|||
50
register.php
50
register.php
|
|
@ -6,17 +6,31 @@ require 'config/database.php';
|
|||
$database = new Database();
|
||||
$conn = $database->getConnection();
|
||||
|
||||
$success = "";
|
||||
$error = "";
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||
$username = trim($_POST['username']);
|
||||
$password = password_hash(trim($_POST['password']), PASSWORD_DEFAULT);
|
||||
$password_plain = trim($_POST['password']);
|
||||
$role = $_POST['role'];
|
||||
|
||||
try {
|
||||
$stmt = $conn->prepare("INSERT INTO users (username, password, role) VALUES (?, ?, ?)");
|
||||
$stmt->execute([$username, $password, $role]);
|
||||
$success = "Pendaftaran berhasil! Silakan login.";
|
||||
} catch (PDOException $e) {
|
||||
$error = "Username sudah digunakan atau terjadi kesalahan.";
|
||||
// Validasi sederhana
|
||||
if (empty($username) || empty($password_plain) || empty($role)) {
|
||||
$error = "Semua field wajib diisi.";
|
||||
} else {
|
||||
$password = password_hash($password_plain, PASSWORD_DEFAULT);
|
||||
|
||||
try {
|
||||
$stmt = $conn->prepare("INSERT INTO users (username, password, role) VALUES (?, ?, ?)");
|
||||
$stmt->execute([$username, $password, $role]);
|
||||
$success = "Pendaftaran berhasil! Silakan login.";
|
||||
} catch (PDOException $e) {
|
||||
if ($e->getCode() == 23000) { // duplicate entry
|
||||
$error = "Username sudah digunakan.";
|
||||
} else {
|
||||
$error = "Terjadi kesalahan: " . $e->getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
|
@ -97,8 +111,6 @@ if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
|||
color: red;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
/* Responsif untuk layar kecil */
|
||||
@media (max-width: 480px) {
|
||||
.register-container {
|
||||
padding: 25px;
|
||||
|
|
@ -113,17 +125,25 @@ if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
|||
<div class="register-container">
|
||||
<img src="megadata.jpeg" alt="Logo">
|
||||
<h2>Daftar Akun</h2>
|
||||
<?php
|
||||
if (!empty($success)) echo "<p class='success'>$success</p>";
|
||||
if (!empty($error)) echo "<p class='error'>$error</p>";
|
||||
?>
|
||||
<?php if (!empty($success)) echo "<p class='success'>$success</p>"; ?>
|
||||
<?php if (!empty($error)) echo "<p class='error'>$error</p>"; ?>
|
||||
|
||||
<form method="POST">
|
||||
<input type="text" name="username" placeholder="Username" required>
|
||||
<input type="password" name="password" placeholder="Password" required>
|
||||
<select name="role" required>
|
||||
<option value="">Pilih Role</option>
|
||||
<option value="engineer">Administrator</option>
|
||||
<option value="teknisi">Teknisi</option>
|
||||
<option value="engineer">Superadmin</option>
|
||||
<option value="teknisi1">Site Bungatan</option>
|
||||
<option value="teknisi2">Site Besuki</option>
|
||||
<option value="teknisi3">Site jatisari</option>
|
||||
<option value="teknisi4">Site Kendit</option>
|
||||
<option value="teknisi5">Site Gending</option>
|
||||
<option value="teknisi6">Site Bangkalan</option>
|
||||
<option value="teknisi7">Site Bondowoso</option>
|
||||
<option value="teknisi8">Site Arjasa</option>
|
||||
<option value="teknisi9">Site Lamongan</option>
|
||||
<option value="teknisi10">Site Asembagus</option>
|
||||
</select>
|
||||
<button type="submit">Daftar</button>
|
||||
</form>
|
||||
|
|
|
|||
Loading…
Reference in New Issue