TKK_E32222869/script.js

577 lines
21 KiB
JavaScript

// FIREBASE KONFIGURASI
import { initializeApp } from "https://www.gstatic.com/firebasejs/9.6.1/firebase-app.js";
import { getDatabase, ref, set, onValue, push } from "https://www.gstatic.com/firebasejs/9.6.1/firebase-database.js";
// Firebase Configuration
const firebaseConfig = {
apiKey: "AIzaSyAS1qxaUCFRZ1Fe25qGK27JtwCWEPuaYys",
authDomain: "parkingslot-c5fba.firebaseapp.com",
databaseURL: "https://parkingslot-c5fba-default-rtdb.firebaseio.com",
projectId: "parkingslot-c5fba",
storageBucket: "parkingslot-c5fba.firebasestorage.app",
messagingSenderId: "675813222621",
appId: "1:675813222621:web:2986b33a1e5fd10c5b9334"
};
// Initialize Firebase
const app = initializeApp(firebaseConfig);
const db = getDatabase(app);
console.log("Firebase berhasil terhubung:", db);
// Global Variables
let isAdminMode = false;
let previousSlotStatus = {
slot_1: null, // null berarti belum pernah di-set
slot_2: null,
slot_3: null
};
let slotStatus = {
slot_1: "Tersedia",
slot_2: "Tersedia",
slot_3: "Tersedia"
};
let isSystemInitialized = false;
// Set isAdminMode true jika di halaman admin.html
if (window.location.pathname.includes('admin.html')) {
isAdminMode = true;
console.log("Mode Admin aktif");
} else {
console.log("Mode User aktif");
}
// Initialize previous status dari Firebase saat startup
function initializePreviousStatus() {
console.log("🔄 Menginisialisasi status sebelumnya dari Firebase...");
return new Promise((resolve, reject) => {
let loadedSlots = 0;
const totalSlots = 3;
// Load status sebelumnya dari Firebase untuk setiap slot
for (let i = 1; i <= 3; i++) {
const slotKey = `slot_${i}`;
onValue(ref(db, `parkir/slot_${i}`), function(snapshot) {
const data = snapshot.val();
if (data && data.status) {
previousSlotStatus[slotKey] = data.status;
console.log(`✅ Status awal slot ${i}: ${data.status}`);
} else {
// Jika tidak ada data, set default
previousSlotStatus[slotKey] = "Tersedia";
console.log(`⚠️ Slot ${i} tidak ada data, set default: Tersedia`);
}
loadedSlots++;
if (loadedSlots === totalSlots) {
isSystemInitialized = true;
console.log("✅ Semua slot berhasil diinisialisasi");
console.log("Previous Status:", previousSlotStatus);
resolve();
}
}, { onlyOnce: true });
}
// Timeout fallback jika Firebase lambat
setTimeout(() => {
if (!isSystemInitialized) {
console.log("⏰ Timeout inisialisasi, menggunakan default values");
previousSlotStatus = {
slot_1: "Tersedia",
slot_2: "Tersedia",
slot_3: "Tersedia"
};
isSystemInitialized = true;
resolve();
}
}, 5000);
});
}
// Setup MQTT
const client = mqtt.connect('ws://broker.hivemq.com:8000/mqtt');
client.on('connect', function () {
console.log("🔗 Terhubung ke Broker MQTT");
client.subscribe('parkir/slot_1');
client.subscribe('parkir/slot_2');
client.subscribe('parkir/slot_3');
client.subscribe('parkir/status');
// Initialize previous status setelah MQTT connect
setTimeout(async () => {
await initializePreviousStatus();
console.log("🚀 Sistem siap menerima data MQTT");
}, 1000);
});
client.on('message', function (topic, message) {
// Jangan proses jika sistem belum diinisialisasi
if (!isSystemInitialized) {
console.log("⏳ Sistem belum siap, mengabaikan pesan MQTT");
return;
}
console.log(`📨 Pesan MQTT diterima: ${message.toString()} di topik: ${topic}`);
const distance = parseInt(message.toString());
if (isNaN(distance)) {
console.error('❌ Data tidak valid diterima:', message.toString());
return;
}
// Process each slot
if (topic === 'parkir/slot_1') {
processSlotUpdate(1, distance, 'mqtt');
}
if (topic === 'parkir/slot_2') {
processSlotUpdate(2, distance, 'mqtt');
}
if (topic === 'parkir/slot_3') {
processSlotUpdate(3, distance, 'mqtt');
}
checkParkingStatus();
});
// Fungsi utama untuk memproses update slot - DIPERBAIKI
function processSlotUpdate(slotNumber, distance, source = 'unknown') {
const newStatus = distance > 5 ? "Tersedia" : "Terisi";
const slotKey = `slot_${slotNumber}`;
console.log(`🔄 Processing slot ${slotNumber}: distance=${distance}, newStatus=${newStatus}, source=${source}`);
console.log(`📊 Previous status slot ${slotNumber}: ${previousSlotStatus[slotKey]}`);
// Update status slot di UI
updateSlotStatus(slotNumber, distance);
// Cek perubahan status - LOGIKA DIPERBAIKI
const prevStatus = previousSlotStatus[slotKey];
// Jika ini adalah data pertama (previous status null), langsung log
if (prevStatus === null) {
console.log(`🆕 DATA PERTAMA - Slot ${slotNumber}: null -> ${newStatus}`);
logParkingHistory(slotNumber, newStatus, isAdminMode, source);
previousSlotStatus[slotKey] = newStatus;
}
// Jika ada perubahan status dari status sebelumnya
else if (prevStatus !== newStatus) {
console.log(`🔥 PERUBAHAN TERDETEKSI - Slot ${slotNumber}: ${prevStatus} -> ${newStatus}`);
console.log(`📝 Mode saat ini: ${isAdminMode ? 'Admin' : 'User'}`);
// SELALU log parking history baik di user maupun admin mode
logParkingHistory(slotNumber, newStatus, isAdminMode, source);
// Update previous status SETELAH logging
previousSlotStatus[slotKey] = newStatus;
console.log(`✅ Previous status slot ${slotNumber} diupdate ke: ${newStatus}`);
} else {
console.log(`➡️ Tidak ada perubahan status slot ${slotNumber}: ${newStatus}`);
}
// Update current status
slotStatus[slotKey] = newStatus;
}
// Fungsi untuk update indikator pada denah
function updateMapIndicator(slot, status) {
const indicator = document.getElementById('indicator_' + slot);
if (indicator) {
indicator.classList.remove('available', 'occupied');
if (status === "Tersedia") {
indicator.classList.add('available');
indicator.style.backgroundColor = '#4CAF50';
} else {
indicator.classList.add('occupied');
indicator.style.backgroundColor = '#f44336';
}
indicator.title = `Slot ${slot} - ${status}`;
console.log(`🗺️ Indikator denah slot ${slot} diupdate: ${status}`);
}
}
function updateSlotStatus(slot, distance) {
const statusElement = document.getElementById('status_' + slot);
const slotElement = document.getElementById('sensor_' + slot);
let status;
if (distance > 5) {
status = "Tersedia";
if (statusElement) {
statusElement.textContent = "Status: Tersedia";
statusElement.className = "status empty";
}
if (slotElement) {
slotElement.style.backgroundColor = "lightgreen";
}
} else {
status = "Terisi";
if (statusElement) {
statusElement.textContent = "Status: Terisi";
statusElement.className = "status occupied";
}
if (slotElement) {
slotElement.style.backgroundColor = "lightcoral";
}
}
updateMapIndicator(slot, status);
// Simpan ke Firebase tanpa memanggil processSlotUpdate lagi untuk menghindari loop
updateFirebaseStatusOnly(slot, distance);
}
function checkParkingStatus() {
const allFull = slotStatus.slot_1 === "Terisi" && slotStatus.slot_2 === "Terisi" && slotStatus.slot_3 === "Terisi";
if (allFull) {
console.log("🚫 Parkir Penuh - Palang tetap tertutup.");
client.publish('palang/status', 'Tutup');
} else {
console.log("✅ Ada slot tersedia - Palang dibuka.");
client.publish('palang/status', 'Buka');
}
}
// Fungsi khusus untuk update Firebase tanpa trigger logging
function updateFirebaseStatusOnly(slot, distance) {
const status = distance > 5 ? "Tersedia" : "Terisi";
console.log(`💾 Menyimpan ke Firebase - Slot ${slot}: distance=${distance}, status=${status}`);
set(ref(db, 'parkir/slot_' + slot), {
distance: distance,
status: status,
lastUpdate: new Date().toISOString(),
updatedBy: isAdminMode ? 'admin' : 'user'
})
.then(() => {
console.log(`✅ Status slot ${slot} berhasil disimpan ke Firebase`);
})
.catch((error) => {
console.error("❌ Gagal menyimpan status ke Firebase:", error);
});
}
// Fungsi log parking history - DIPERBAIKI DENGAN LOGGING DETAIL
function logParkingHistory(slot, status, isAdmin, source = 'unknown') {
const timestamp = new Date().toLocaleString('id-ID');
const historyData = {
slot: slot,
status: status,
timestamp: timestamp,
date: new Date().toISOString(),
source: isAdmin ? 'admin' : 'user',
dataSource: source, // mqtt, firebase, manual
sessionId: generateSessionId()
};
console.log(`📊 MENYIMPAN RIWAYAT PARKIR:`);
console.log(` - Slot: ${slot}`);
console.log(` - Status: ${status}`);
console.log(` - Mode: ${historyData.source}`);
console.log(` - Data Source: ${source}`);
console.log(` - Timestamp: ${timestamp}`);
push(ref(db, 'parking_history'), historyData)
.then(() => {
console.log(`✅ SUKSES! Riwayat parkir slot ${slot} tersimpan (${historyData.source})`);
// Hanya update tampilan jika di mode admin
if (isAdmin && typeof updateHistoryDisplay === 'function') {
console.log(`🔄 Mengupdate tampilan riwayat admin...`);
updateHistoryDisplay();
}
})
.catch((error) => {
console.error(`❌ GAGAL! Menyimpan riwayat parkir slot ${slot}:`, error);
});
}
// Generate session ID untuk tracking
function generateSessionId() {
return Date.now() + '_' + Math.random().toString(36).substr(2, 9);
}
// Fungsi untuk update tampilan riwayat (hanya di admin mode)
function updateHistoryDisplay() {
// Hanya jalankan jika di admin mode
if (!isAdminMode) {
console.log("updateHistoryDisplay diabaikan - bukan mode admin");
return;
}
onValue(ref(db, 'parking_history'), function(snapshot) {
const historyContainer = document.getElementById('history-container');
if (!historyContainer) {
console.log("Element history-container tidak ditemukan");
return;
}
const data = snapshot.val();
if (!data) {
historyContainer.innerHTML = '<div class="loading-message">📝 Belum ada riwayat parkir</div>';
return;
}
const historyArray = Object.values(data).sort((a, b) => new Date(b.date) - new Date(a.date));
let historyHTML = '<div class="history-list">';
historyArray.forEach(record => {
const statusClass = record.status === 'Terisi' ? 'entry' : 'exit';
const statusText = record.status === 'Terisi' ? 'Masuk' : 'Keluar';
const sourceIcon = record.source === 'admin' ? '🔧' : '👤';
const dataSourceIcon = record.dataSource === 'mqtt' ? '📡' : '💾';
historyHTML += `
<div class="history-item ${statusClass}">
<div class="history-info">
<strong>Slot ${record.slot}</strong> - ${statusText}
<small>${sourceIcon} ${record.source} ${dataSourceIcon} ${record.dataSource || 'unknown'}</small>
</div>
<div class="history-time">${record.timestamp}</div>
</div>
`;
});
historyHTML += '</div>';
historyContainer.innerHTML = historyHTML;
console.log(`📋 Tampilan riwayat diupdate dengan ${historyArray.length} record`);
});
}
// Listener realtime Firebase untuk semua slot - DIPERBAIKI
function setupRealtimeListeners() {
console.log("🔥 Setting up Firebase realtime listeners...");
// Listener untuk slot 1
onValue(ref(db, 'parkir/slot_1'), function(snapshot) {
const data = snapshot.val();
if (data) {
console.log(`🔥 Firebase realtime update slot 1: distance=${data.distance}, status=${data.status}`);
// Hanya update UI, jangan trigger processSlotUpdate untuk menghindari double logging
const statusElement = document.getElementById('status_1');
const slotElement = document.getElementById('sensor_1');
if (data.distance > 5) {
if (statusElement) {
statusElement.textContent = "Status: Tersedia";
statusElement.className = "status empty";
}
if (slotElement) {
slotElement.style.backgroundColor = "lightgreen";
}
} else {
if (statusElement) {
statusElement.textContent = "Status: Terisi";
statusElement.className = "status occupied";
}
if (slotElement) {
slotElement.style.backgroundColor = "lightcoral";
}
}
updateMapIndicator(1, data.status);
slotStatus.slot_1 = data.status;
}
});
// Listener untuk slot 2
onValue(ref(db, 'parkir/slot_2'), function(snapshot) {
const data = snapshot.val();
if (data) {
console.log(`🔥 Firebase realtime update slot 2: distance=${data.distance}, status=${data.status}`);
const statusElement = document.getElementById('status_2');
const slotElement = document.getElementById('sensor_2');
if (data.distance > 5) {
if (statusElement) {
statusElement.textContent = "Status: Tersedia";
statusElement.className = "status empty";
}
if (slotElement) {
slotElement.style.backgroundColor = "lightgreen";
}
} else {
if (statusElement) {
statusElement.textContent = "Status: Terisi";
statusElement.className = "status occupied";
}
if (slotElement) {
slotElement.style.backgroundColor = "lightcoral";
}
}
updateMapIndicator(2, data.status);
slotStatus.slot_2 = data.status;
}
});
// Listener untuk slot 3
onValue(ref(db, 'parkir/slot_3'), function(snapshot) {
const data = snapshot.val();
if (data) {
console.log(`🔥 Firebase realtime update slot 3: distance=${data.distance}, status=${data.status}`);
const statusElement = document.getElementById('status_3');
const slotElement = document.getElementById('sensor_3');
if (data.distance > 5) {
if (statusElement) {
statusElement.textContent = "Status: Tersedia";
statusElement.className = "status empty";
}
if (slotElement) {
slotElement.style.backgroundColor = "lightcoral";
}
} else {
if (statusElement) {
statusElement.textContent = "Status: Terisi";
statusElement.className = "status occupied";
}
if (slotElement) {
slotElement.style.backgroundColor = "lightcoral";
}
}
updateMapIndicator(3, data.status);
slotStatus.slot_3 = data.status;
}
});
console.log("✅ Realtime listeners untuk Firebase telah diatur");
}
// Mode switching functions (Admin login modal etc.)
window.showAdminLogin = function () {
const modal = document.getElementById('admin-modal');
if (modal) modal.style.display = 'flex';
}
window.closeAdminModal = function () {
const modal = document.getElementById('admin-modal');
const input = document.getElementById('admin-password');
if (modal) modal.style.display = 'none';
if (input) input.value = '';
}
window.loginAdmin = function () {
const input = document.getElementById('admin-password');
if (input && input.value === 'admin123') {
window.location.href = 'admin.html';
} else {
alert('Password salah!');
}
}
window.handlePasswordKeypress = function (event) {
if (event.key === 'Enter') {
loginAdmin();
}
}
// Update admin status display
function updateAdminStatus() {
if (isAdminMode) {
for (let i = 1; i <= 3; i++) {
const userStatus = document.getElementById('status_' + i)?.textContent;
const adminElement = document.getElementById('admin-status-' + i);
if (adminElement && userStatus) {
adminElement.textContent = userStatus.replace('Status: ', '');
adminElement.style.color = userStatus.includes('Tersedia') ? '#4CAF50' : '#f44336';
adminElement.style.fontWeight = 'bold';
}
}
}
}
// FUNGSI DEBUG YANG DITINGKATKAN
window.debugParkingSystem = function() {
console.log("=== 🔍 DEBUG PARKING SYSTEM ===");
console.log("Mode:", isAdminMode ? "Admin" : "User");
console.log("System Initialized:", isSystemInitialized);
console.log("Current Slot Status:", slotStatus);
console.log("Previous Slot Status:", previousSlotStatus);
console.log("Firebase DB Connected:", !!db);
console.log("MQTT Client Connected:", client.connected);
console.log("===============================");
}
// Fungsi untuk testing manual - DIPERBAIKI
window.testHistoryLogging = function(slot, status) {
console.log(`🧪 MANUAL TEST - Logging history for slot ${slot} with status ${status}`);
// Simulasi perubahan status
const slotKey = `slot_${slot}`;
const currentPrev = previousSlotStatus[slotKey];
console.log(`📊 Before test - Previous: ${currentPrev}, New: ${status}`);
// Force log tanpa cek perubahan
logParkingHistory(slot, status, isAdminMode, 'manual-test');
// Update previous status
previousSlotStatus[slotKey] = status;
console.log(`✅ Test completed - Previous status updated to: ${status}`);
}
// FUNGSI KHUSUS UNTUK FORCE LOGGING (Testing)
window.forceLogHistory = function(slot, status) {
console.log(`🚨 FORCE LOGGING - Slot ${slot}, Status: ${status}`);
logParkingHistory(slot, status, isAdminMode, 'force-test');
}
// Inisialisasi saat DOM ready
window.addEventListener('DOMContentLoaded', function() {
console.log(`🚀 Sistem parkir dimulai - Mode: ${isAdminMode ? '🔧 Admin' : '👤 User'}`);
// Inisialisasi indikator denah
for (let i = 1; i <= 3; i++) {
updateMapIndicator(i, "Tersedia");
}
console.log("🗺️ Indikator denah telah diinisialisasi");
// Setup realtime listeners
setupRealtimeListeners();
// Jika admin mode, setup tampilan riwayat dan update berkala
if (isAdminMode) {
console.log("🔧 Mengaktifkan fitur admin...");
updateHistoryDisplay();
// Update admin status setiap detik
setInterval(updateAdminStatus, 1000);
console.log("✅ Admin mode: Pemantauan riwayat realtime aktif");
} else {
console.log("👤 User mode: Siap mengirim data riwayat ke Firebase");
// Test functions untuk user mode
window.testUserMode = function() {
console.log("🧪 Testing user mode logging...");
console.log("🔍 Current state:", {
initialized: isSystemInitialized,
previousStatus: previousSlotStatus,
currentStatus: slotStatus
});
// Test dengan force logging
forceLogHistory(1, "Terisi");
setTimeout(() => forceLogHistory(1, "Tersedia"), 3000);
};
// Fungsi untuk simulasi sensor
window.simulateSensor = function(slot, distance) {
console.log(`🎯 Simulasi sensor slot ${slot} dengan jarak ${distance}cm`);
processSlotUpdate(slot, distance, 'simulation');
};
}
console.log("✅ Inisialisasi sistem parkir selesai");
console.log("💡 Fungsi debugging:");
console.log(" - debugParkingSystem() : Info sistem");
console.log(" - forceLogHistory(slot, status) : Force log history");
if (!isAdminMode) {
console.log(" - testUserMode() : Test user mode logging");
console.log(" - simulateSensor(slot, distance) : Simulasi sensor");
}
});