fix report and add filter
This commit is contained in:
parent
3381eb11b5
commit
beb4d15d11
|
@ -56,6 +56,23 @@ void setup() {
|
||||||
Firebase.begin(&config, &auth);
|
Firebase.begin(&config, &auth);
|
||||||
Firebase.reconnectWiFi(true);
|
Firebase.reconnectWiFi(true);
|
||||||
|
|
||||||
|
// Konfigurasi NTP dan tunggu sinkronisasi
|
||||||
|
configTime(7 * 3600, 0, "pool.ntp.org"); // GMT+7
|
||||||
|
Serial.println("Menunggu sinkronisasi waktu");
|
||||||
|
while (time(nullptr) < 1000000000) {
|
||||||
|
Serial.print(".");
|
||||||
|
delay(100);
|
||||||
|
}
|
||||||
|
Serial.println("\nWaktu tersinkronisasi!");
|
||||||
|
|
||||||
|
// Setelah waktu tersinkronisasi, baru kirim timestamp pertama
|
||||||
|
unsigned long epochTime = time(nullptr);
|
||||||
|
if (Firebase.setInt(firebaseData, "/device/lastActiveWemos", epochTime)) {
|
||||||
|
Serial.println("Initial timestamp sent: " + String(epochTime));
|
||||||
|
} else {
|
||||||
|
Serial.println("Failed to send initial timestamp");
|
||||||
|
}
|
||||||
|
|
||||||
// Inisialisasi RFID
|
// Inisialisasi RFID
|
||||||
SPI.begin();
|
SPI.begin();
|
||||||
mfrc522.PCD_Init();
|
mfrc522.PCD_Init();
|
||||||
|
@ -84,13 +101,6 @@ void setup() {
|
||||||
// Update path untuk restart control
|
// Update path untuk restart control
|
||||||
Firebase.setBool(firebaseData, "/control/restartWemos", false);
|
Firebase.setBool(firebaseData, "/control/restartWemos", false);
|
||||||
|
|
||||||
// Konfigurasi NTP
|
|
||||||
configTime(7 * 3600, 0, "pool.ntp.org"); // GMT+7
|
|
||||||
|
|
||||||
// Kirim timestamp pertama kali
|
|
||||||
unsigned long epochTime = time(nullptr);
|
|
||||||
Firebase.setInt(firebaseData, "/device/lastActiveWemos", epochTime);
|
|
||||||
|
|
||||||
// Update status device saat startup
|
// Update status device saat startup
|
||||||
Firebase.setString(firebaseData, "/logs/systemWemos", "Device Online");
|
Firebase.setString(firebaseData, "/logs/systemWemos", "Device Online");
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,59 +43,135 @@ public function handle()
|
||||||
// Ambil data terakhir jika ada
|
// Ambil data terakhir jika ada
|
||||||
$lastEntry = !empty($historyData) ? end($historyData) : null;
|
$lastEntry = !empty($historyData) ? end($historyData) : null;
|
||||||
|
|
||||||
// Cek apakah ada perubahan pada security, smartcab, control, atau logs
|
// Cek perubahan pada setiap kategori data
|
||||||
|
$securityChanged = false;
|
||||||
|
$smartcabChanged = false;
|
||||||
|
$controlChanged = false;
|
||||||
|
$logsChanged = false;
|
||||||
|
$dht11Changed = false;
|
||||||
|
$deviceChanged = false;
|
||||||
$hasChanges = false;
|
$hasChanges = false;
|
||||||
|
|
||||||
if ($lastEntry === null) {
|
if ($lastEntry === null) {
|
||||||
$hasChanges = true;
|
// Jika belum ada data, anggap semua kategori berubah (kecuali yang dikecualikan)
|
||||||
|
$securityChanged = !empty($securityData);
|
||||||
|
$smartcabChanged = !empty($smartcabData);
|
||||||
|
$controlChanged = !empty($controlData);
|
||||||
|
$logsChanged = !empty($logsData);
|
||||||
|
|
||||||
|
// Untuk DHT11 dan Device, kita hanya anggap berubah jika ada data selain yang dikecualikan
|
||||||
|
$dht11Changed = !empty($dht11Data) && $this->hasNonTrivialDHT11Data($dht11Data);
|
||||||
|
$deviceChanged = !empty($deviceData) && $this->hasNonTrivialDeviceData($deviceData);
|
||||||
|
|
||||||
|
$hasChanges = $securityChanged || $smartcabChanged || $controlChanged ||
|
||||||
|
$logsChanged || $dht11Changed || $deviceChanged;
|
||||||
} else {
|
} else {
|
||||||
|
// Cek perubahan pada setiap kategori
|
||||||
$securityChanged = $this->hasDataChanged($lastEntry['security'] ?? [], $securityData);
|
$securityChanged = $this->hasDataChanged($lastEntry['security'] ?? [], $securityData);
|
||||||
$smartcabChanged = $this->hasDataChanged($lastEntry['smartcab'] ?? [], $smartcabData);
|
$smartcabChanged = $this->hasDataChanged($lastEntry['smartcab'] ?? [], $smartcabData);
|
||||||
$controlChanged = $this->hasDataChanged($lastEntry['control'] ?? [], $controlData);
|
$controlChanged = $this->hasDataChanged($lastEntry['control'] ?? [], $controlData);
|
||||||
$logsChanged = $this->hasDataChanged($lastEntry['logs'] ?? [], $logsData);
|
$logsChanged = $this->hasDataChanged($lastEntry['logs'] ?? [], $logsData);
|
||||||
|
|
||||||
$hasChanges = $securityChanged || $smartcabChanged || $controlChanged || $logsChanged;
|
// Untuk dht11 dan device kita periksa secara khusus, mengabaikan field yang sering berubah
|
||||||
|
$dht11Changed = $this->hasNonTrivialDHT11Changes($lastEntry['dht11'] ?? [], $dht11Data);
|
||||||
|
$deviceChanged = $this->hasNonTrivialDeviceChanges($lastEntry['device'] ?? [], $deviceData);
|
||||||
|
|
||||||
|
$hasChanges = $securityChanged || $smartcabChanged || $controlChanged ||
|
||||||
|
$logsChanged || $dht11Changed || $deviceChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hanya simpan jika ada perubahan
|
// Proses setiap perubahan secara terpisah
|
||||||
if ($hasChanges) {
|
$changesMade = false;
|
||||||
$newData = [
|
|
||||||
'id' => Str::uuid()->toString(), // Generate ID unik
|
// Buat template data lengkap
|
||||||
'timestamp' => now()->toIso8601String(),
|
$fullData = [
|
||||||
'security' => $securityData,
|
'security' => $securityData,
|
||||||
'smartcab' => $smartcabData,
|
'smartcab' => $smartcabData,
|
||||||
'control' => $controlData,
|
'control' => $controlData,
|
||||||
'logs' => $logsData
|
'logs' => $logsData,
|
||||||
];
|
'dht11' => $dht11Data,
|
||||||
|
'device' => $deviceData
|
||||||
|
];
|
||||||
|
|
||||||
if (!empty($dht11Data)) {
|
// Simpan setiap perubahan secara terpisah dengan data lengkap
|
||||||
$newData['dht11'] = $dht11Data;
|
if ($securityChanged && !empty($securityData)) {
|
||||||
}
|
$newEntry = $fullData;
|
||||||
|
$newEntry['id'] = Str::uuid()->toString();
|
||||||
|
$newEntry['timestamp'] = now()->toIso8601String();
|
||||||
|
$newEntry['change_type'] = 'security'; // Tambahkan informasi apa yang berubah
|
||||||
|
|
||||||
if (!empty($deviceData)) {
|
$historyData[] = $newEntry;
|
||||||
$newData['device'] = $deviceData;
|
event(new ReportUpdated($newEntry, 'security'));
|
||||||
}
|
$this->info('Perubahan terdeteksi pada security, ID: ' . $newEntry['id']);
|
||||||
|
$changesMade = true;
|
||||||
|
}
|
||||||
|
|
||||||
$historyData[] = $newData;
|
if ($smartcabChanged && !empty($smartcabData)) {
|
||||||
|
$newEntry = $fullData;
|
||||||
|
$newEntry['id'] = Str::uuid()->toString();
|
||||||
|
$newEntry['timestamp'] = now()->toIso8601String();
|
||||||
|
$newEntry['change_type'] = 'smartcab';
|
||||||
|
|
||||||
|
$historyData[] = $newEntry;
|
||||||
|
event(new ReportUpdated($newEntry, 'smartcab'));
|
||||||
|
$this->info('Perubahan terdeteksi pada smartcab, ID: ' . $newEntry['id']);
|
||||||
|
$changesMade = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($controlChanged && !empty($controlData)) {
|
||||||
|
$newEntry = $fullData;
|
||||||
|
$newEntry['id'] = Str::uuid()->toString();
|
||||||
|
$newEntry['timestamp'] = now()->toIso8601String();
|
||||||
|
$newEntry['change_type'] = 'control';
|
||||||
|
|
||||||
|
$historyData[] = $newEntry;
|
||||||
|
event(new ReportUpdated($newEntry, 'control'));
|
||||||
|
$this->info('Perubahan terdeteksi pada control, ID: ' . $newEntry['id']);
|
||||||
|
$changesMade = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($logsChanged && !empty($logsData)) {
|
||||||
|
$newEntry = $fullData;
|
||||||
|
$newEntry['id'] = Str::uuid()->toString();
|
||||||
|
$newEntry['timestamp'] = now()->toIso8601String();
|
||||||
|
$newEntry['change_type'] = 'logs';
|
||||||
|
|
||||||
|
$historyData[] = $newEntry;
|
||||||
|
event(new ReportUpdated($newEntry, 'logs'));
|
||||||
|
$this->info('Perubahan terdeteksi pada logs, ID: ' . $newEntry['id']);
|
||||||
|
$changesMade = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($dht11Changed && !empty($dht11Data)) {
|
||||||
|
$newEntry = $fullData;
|
||||||
|
$newEntry['id'] = Str::uuid()->toString();
|
||||||
|
$newEntry['timestamp'] = now()->toIso8601String();
|
||||||
|
$newEntry['change_type'] = 'dht11';
|
||||||
|
|
||||||
|
$historyData[] = $newEntry;
|
||||||
|
event(new ReportUpdated($newEntry, 'dht11'));
|
||||||
|
$this->info('Perubahan signifikan terdeteksi pada dht11, ID: ' . $newEntry['id']);
|
||||||
|
$changesMade = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($deviceChanged && !empty($deviceData)) {
|
||||||
|
$newEntry = $fullData;
|
||||||
|
$newEntry['id'] = Str::uuid()->toString();
|
||||||
|
$newEntry['timestamp'] = now()->toIso8601String();
|
||||||
|
$newEntry['change_type'] = 'device';
|
||||||
|
|
||||||
|
$historyData[] = $newEntry;
|
||||||
|
event(new ReportUpdated($newEntry, 'device'));
|
||||||
|
$this->info('Perubahan signifikan terdeteksi pada device, ID: ' . $newEntry['id']);
|
||||||
|
$changesMade = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Jika ada perubahan yang disimpan, update file
|
||||||
|
if ($changesMade) {
|
||||||
Storage::put('reports.json', json_encode($historyData, JSON_PRETTY_PRINT));
|
Storage::put('reports.json', json_encode($historyData, JSON_PRETTY_PRINT));
|
||||||
$this->info('Data baru tersimpan dengan ID: ' . $newData['id']);
|
$this->info('Semua perubahan berhasil disimpan.');
|
||||||
|
|
||||||
// Broadcast event untuk realtime update
|
|
||||||
event(new ReportUpdated($newData));
|
|
||||||
|
|
||||||
if (isset($securityChanged) && $securityChanged) {
|
|
||||||
$this->info('Perubahan terdeteksi pada security');
|
|
||||||
}
|
|
||||||
if (isset($smartcabChanged) && $smartcabChanged) {
|
|
||||||
$this->info('Perubahan terdeteksi pada smartcab');
|
|
||||||
}
|
|
||||||
if (isset($controlChanged) && $controlChanged) {
|
|
||||||
$this->info('Perubahan terdeteksi pada control');
|
|
||||||
}
|
|
||||||
if (isset($logsChanged) && $logsChanged) {
|
|
||||||
$this->info('Perubahan terdeteksi pada logs');
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
$this->info('Tidak ada perubahan pada data, data tidak disimpan');
|
$this->info('Tidak ada perubahan signifikan pada data, data tidak disimpan');
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
|
@ -110,4 +186,96 @@ private function hasDataChanged($oldData, $newData)
|
||||||
|
|
||||||
return $oldJson !== $newJson;
|
return $oldJson !== $newJson;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Memeriksa perubahan pada data dht11 selain humidity dan temperature
|
||||||
|
*/
|
||||||
|
private function hasNonTrivialDHT11Changes($oldData, $newData)
|
||||||
|
{
|
||||||
|
// Buat salinan data untuk perbandingan
|
||||||
|
$oldDataCompare = is_array($oldData) ? $oldData : [];
|
||||||
|
$newDataCompare = is_array($newData) ? $newData : [];
|
||||||
|
|
||||||
|
// Hapus field yang sering berubah
|
||||||
|
if (isset($oldDataCompare['humidity'])) {
|
||||||
|
unset($oldDataCompare['humidity']);
|
||||||
|
}
|
||||||
|
if (isset($oldDataCompare['temperature'])) {
|
||||||
|
unset($oldDataCompare['temperature']);
|
||||||
|
}
|
||||||
|
if (isset($newDataCompare['humidity'])) {
|
||||||
|
unset($newDataCompare['humidity']);
|
||||||
|
}
|
||||||
|
if (isset($newDataCompare['temperature'])) {
|
||||||
|
unset($newDataCompare['temperature']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bandingkan data yang tersisa
|
||||||
|
return json_encode($oldDataCompare) !== json_encode($newDataCompare);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Memeriksa apakah DHT11 data memiliki field selain yang dikecualikan
|
||||||
|
*/
|
||||||
|
private function hasNonTrivialDHT11Data($data)
|
||||||
|
{
|
||||||
|
$dataCopy = is_array($data) ? $data : [];
|
||||||
|
|
||||||
|
// Hapus field yang sering berubah
|
||||||
|
if (isset($dataCopy['humidity'])) {
|
||||||
|
unset($dataCopy['humidity']);
|
||||||
|
}
|
||||||
|
if (isset($dataCopy['temperature'])) {
|
||||||
|
unset($dataCopy['temperature']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Periksa apakah masih ada data lain
|
||||||
|
return !empty($dataCopy);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Memeriksa perubahan pada data device selain lastActive dan lastActiveWemos
|
||||||
|
*/
|
||||||
|
private function hasNonTrivialDeviceChanges($oldData, $newData)
|
||||||
|
{
|
||||||
|
// Buat salinan data untuk perbandingan
|
||||||
|
$oldDataCompare = is_array($oldData) ? $oldData : [];
|
||||||
|
$newDataCompare = is_array($newData) ? $newData : [];
|
||||||
|
|
||||||
|
// Hapus field yang sering berubah
|
||||||
|
if (isset($oldDataCompare['lastActive'])) {
|
||||||
|
unset($oldDataCompare['lastActive']);
|
||||||
|
}
|
||||||
|
if (isset($oldDataCompare['lastActiveWemos'])) {
|
||||||
|
unset($oldDataCompare['lastActiveWemos']);
|
||||||
|
}
|
||||||
|
if (isset($newDataCompare['lastActive'])) {
|
||||||
|
unset($newDataCompare['lastActive']);
|
||||||
|
}
|
||||||
|
if (isset($newDataCompare['lastActiveWemos'])) {
|
||||||
|
unset($newDataCompare['lastActiveWemos']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bandingkan data yang tersisa
|
||||||
|
return json_encode($oldDataCompare) !== json_encode($newDataCompare);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Memeriksa apakah Device data memiliki field selain yang dikecualikan
|
||||||
|
*/
|
||||||
|
private function hasNonTrivialDeviceData($data)
|
||||||
|
{
|
||||||
|
$dataCopy = is_array($data) ? $data : [];
|
||||||
|
|
||||||
|
// Hapus field yang sering berubah
|
||||||
|
if (isset($dataCopy['lastActive'])) {
|
||||||
|
unset($dataCopy['lastActive']);
|
||||||
|
}
|
||||||
|
if (isset($dataCopy['lastActiveWemos'])) {
|
||||||
|
unset($dataCopy['lastActiveWemos']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Periksa apakah masih ada data lain
|
||||||
|
return !empty($dataCopy);
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -15,15 +15,19 @@ class ReportUpdated implements ShouldBroadcast
|
||||||
use Dispatchable, InteractsWithSockets, SerializesModels;
|
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||||
|
|
||||||
public $report;
|
public $report;
|
||||||
|
public $changeType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new event instance.
|
* Create a new event instance.
|
||||||
*
|
*
|
||||||
|
* @param array $report Data report
|
||||||
|
* @param string|null $changeType Tipe perubahan (security, smartcab, dll)
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function __construct($report)
|
public function __construct($report, $changeType = null)
|
||||||
{
|
{
|
||||||
$this->report = $report;
|
$this->report = $report;
|
||||||
|
$this->changeType = $changeType ?? ($report['change_type'] ?? null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -35,4 +39,18 @@ public function broadcastOn()
|
||||||
{
|
{
|
||||||
return new Channel('reports');
|
return new Channel('reports');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the data to broadcast.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function broadcastWith()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'report' => $this->report,
|
||||||
|
'change_type' => $this->changeType,
|
||||||
|
'timestamp' => now()->toIso8601String()
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -29,6 +29,27 @@
|
||||||
50% { background-color: #d1ecf1; }
|
50% { background-color: #d1ecf1; }
|
||||||
100% { background-color: #fff; }
|
100% { background-color: #fff; }
|
||||||
}
|
}
|
||||||
|
/* Styling untuk multiple select */
|
||||||
|
select[multiple] {
|
||||||
|
max-height: 200px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
select[multiple] optgroup {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #374151;
|
||||||
|
padding: 0.25rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
select[multiple] option {
|
||||||
|
padding: 0.25rem 0.5rem;
|
||||||
|
margin: 0.125rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
select[multiple] option:checked {
|
||||||
|
background-color: #2563eb;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body class="bg-gray-50">
|
<body class="bg-gray-50">
|
||||||
|
@ -57,43 +78,59 @@
|
||||||
<ul class="space-y-2 text-sm">
|
<ul class="space-y-2 text-sm">
|
||||||
<li class="flex items-center">
|
<li class="flex items-center">
|
||||||
<span class="w-3 h-3 rounded-full bg-green-500 mr-2"></span>
|
<span class="w-3 h-3 rounded-full bg-green-500 mr-2"></span>
|
||||||
<span><strong>Keamanan Normal:</strong> Sistem keamanan berjalan dengan baik</span>
|
<span><strong>Fan:</strong> Perubahan pada status kipas</span>
|
||||||
</li>
|
</li>
|
||||||
<li class="flex items-center">
|
<li class="flex items-center">
|
||||||
<span class="w-3 h-3 rounded-full bg-red-500 mr-2"></span>
|
<span class="w-3 h-3 rounded-full bg-red-500 mr-2"></span>
|
||||||
<span><strong>Status Bahaya:</strong> Sistem keamanan mendeteksi potensi bahaya</span>
|
<span><strong>Status:</strong> Perubahan status keamanan (aman/bahaya)</span>
|
||||||
</li>
|
</li>
|
||||||
<li class="flex items-center">
|
<li class="flex items-center">
|
||||||
<span class="w-3 h-3 rounded-full bg-yellow-500 mr-2"></span>
|
<span class="w-3 h-3 rounded-full bg-yellow-500 mr-2"></span>
|
||||||
<span><strong>Gerakan Terdeteksi:</strong> Sensor gerakan mendeteksi aktivitas</span>
|
<span><strong>Motion:</strong> Perubahan status gerakan</span>
|
||||||
</li>
|
|
||||||
<li class="flex items-center">
|
|
||||||
<span class="w-3 h-3 rounded-full bg-lime-500 mr-2"></span>
|
|
||||||
<span><strong>Tidak Ada Gerakan:</strong> Tidak ada gerakan yang terdeteksi</span>
|
|
||||||
</li>
|
|
||||||
<li class="flex items-center">
|
|
||||||
<span class="w-3 h-3 rounded-full bg-blue-500 mr-2"></span>
|
|
||||||
<span><strong>Servo Terkunci:</strong> Servo dalam keadaan terkunci</span>
|
|
||||||
</li>
|
</li>
|
||||||
<li class="flex items-center">
|
<li class="flex items-center">
|
||||||
<span class="w-3 h-3 rounded-full bg-cyan-500 mr-2"></span>
|
<span class="w-3 h-3 rounded-full bg-cyan-500 mr-2"></span>
|
||||||
<span><strong>Servo Terbuka:</strong> Servo dalam keadaan terbuka</span>
|
<span><strong>Servo Status:</strong> Perubahan status servo (terbuka/terkunci)</span>
|
||||||
</li>
|
</li>
|
||||||
<li class="flex items-center">
|
<li class="flex items-center">
|
||||||
<span class="w-3 h-3 rounded-full bg-violet-400 mr-2"></span>
|
<span class="w-3 h-3 rounded-full bg-violet-400 mr-2"></span>
|
||||||
<span><strong>Akses Terakhir:</strong> Perubahan pada akses terakhir</span>
|
<span><strong>Last Access:</strong> Perubahan pada akses terakhir</span>
|
||||||
</li>
|
|
||||||
<li class="flex items-center">
|
|
||||||
<span class="w-3 h-3 rounded-full bg-purple-500 mr-2"></span>
|
|
||||||
<span><strong>Kontrol Diubah:</strong> Terjadi perubahan pada kontrol perangkat</span>
|
|
||||||
</li>
|
|
||||||
<li class="flex items-center">
|
|
||||||
<span class="w-3 h-3 rounded-full bg-pink-500 mr-2"></span>
|
|
||||||
<span><strong>Status Perangkat:</strong> Perubahan pada status perangkat</span>
|
|
||||||
</li>
|
</li>
|
||||||
<li class="flex items-center">
|
<li class="flex items-center">
|
||||||
<span class="w-3 h-3 rounded-full bg-orange-500 mr-2"></span>
|
<span class="w-3 h-3 rounded-full bg-orange-500 mr-2"></span>
|
||||||
<span><strong>Error/Warning:</strong> Terdapat kesalahan atau peringatan dalam sistem</span>
|
<span><strong>Restart ESP:</strong> Perangkat ESP direstart</span>
|
||||||
|
</li>
|
||||||
|
<li class="flex items-center">
|
||||||
|
<span class="w-3 h-3 rounded-full bg-amber-500 mr-2"></span>
|
||||||
|
<span><strong>Restart Wemos:</strong> Perangkat Wemos direstart</span>
|
||||||
|
</li>
|
||||||
|
<li class="flex items-center">
|
||||||
|
<span class="w-3 h-3 rounded-full bg-indigo-500 mr-2"></span>
|
||||||
|
<span><strong>RFID:</strong> Perubahan pada status RFID</span>
|
||||||
|
</li>
|
||||||
|
<li class="flex items-center">
|
||||||
|
<span class="w-3 h-3 rounded-full bg-lime-500 mr-2"></span>
|
||||||
|
<span><strong>DHT:</strong> Perubahan pada sensor DHT</span>
|
||||||
|
</li>
|
||||||
|
<li class="flex items-center">
|
||||||
|
<span class="w-3 h-3 rounded-full bg-teal-500 mr-2"></span>
|
||||||
|
<span><strong>MPU:</strong> Perubahan pada sensor MPU</span>
|
||||||
|
</li>
|
||||||
|
<li class="flex items-center">
|
||||||
|
<span class="w-3 h-3 rounded-full bg-sky-500 mr-2"></span>
|
||||||
|
<span><strong>Servo Log:</strong> Perubahan pada log servo</span>
|
||||||
|
</li>
|
||||||
|
<li class="flex items-center">
|
||||||
|
<span class="w-3 h-3 rounded-full bg-rose-500 mr-2"></span>
|
||||||
|
<span><strong>System ESP:</strong> Perubahan status sistem ESP</span>
|
||||||
|
</li>
|
||||||
|
<li class="flex items-center">
|
||||||
|
<span class="w-3 h-3 rounded-full bg-pink-500 mr-2"></span>
|
||||||
|
<span><strong>System Wemos:</strong> Perubahan status sistem Wemos</span>
|
||||||
|
</li>
|
||||||
|
<li class="flex items-center">
|
||||||
|
<span class="w-3 h-3 rounded-full bg-gray-500 mr-2"></span>
|
||||||
|
<span><strong>Device Status:</strong> Perubahan status perangkat lainnya</span>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
@ -169,6 +206,38 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Tambahkan ini di dalam form filter, setelah Time Range Picker -->
|
||||||
|
<div class="md:col-span-2">
|
||||||
|
<label for="categories" class="block mb-2 text-sm font-medium text-gray-700">Filter Kategori</label>
|
||||||
|
<select id="categories" data-te-select-init multiple
|
||||||
|
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5">
|
||||||
|
<optgroup label="Keamanan">
|
||||||
|
<option value="motion">Motion</option>
|
||||||
|
<option value="status">Status Keamanan</option>
|
||||||
|
<option value="fan">Fan</option>
|
||||||
|
</optgroup>
|
||||||
|
<optgroup label="Perangkat">
|
||||||
|
<option value="servo-status">Servo Status</option>
|
||||||
|
<option value="last-access">Last Access</option>
|
||||||
|
<option value="device">Device Status</option>
|
||||||
|
</optgroup>
|
||||||
|
<optgroup label="Kontrol">
|
||||||
|
<option value="restart-esp">Restart ESP</option>
|
||||||
|
<option value="restart-wemos">Restart Wemos</option>
|
||||||
|
</optgroup>
|
||||||
|
<optgroup label="Sensor">
|
||||||
|
<option value="rfid">RFID</option>
|
||||||
|
<option value="dht">DHT</option>
|
||||||
|
<option value="mpu">MPU</option>
|
||||||
|
</optgroup>
|
||||||
|
<optgroup label="Log">
|
||||||
|
<option value="servo-log">Servo Log</option>
|
||||||
|
<option value="system-esp">System ESP</option>
|
||||||
|
<option value="system-wemos">System Wemos</option>
|
||||||
|
</optgroup>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="md:col-span-2 flex justify-end space-x-2">
|
<div class="md:col-span-2 flex justify-end space-x-2">
|
||||||
<button type="button" id="applyFilter" class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5">
|
<button type="button" id="applyFilter" class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5">
|
||||||
Terapkan Filter
|
Terapkan Filter
|
||||||
|
@ -287,6 +356,7 @@
|
||||||
const pageSize = 10; // Jumlah item per halaman
|
const pageSize = 10; // Jumlah item per halaman
|
||||||
let filteredReports = []; // Menyimpan hasil filter
|
let filteredReports = []; // Menyimpan hasil filter
|
||||||
let isFilterActive = false; // Flag untuk menandai apakah filter aktif
|
let isFilterActive = false; // Flag untuk menandai apakah filter aktif
|
||||||
|
let selectedCategories = [];
|
||||||
|
|
||||||
// Variabel untuk menyimpan instance chart
|
// Variabel untuk menyimpan instance chart
|
||||||
let systemOverviewChart = null;
|
let systemOverviewChart = null;
|
||||||
|
@ -367,89 +437,84 @@ function goToNextPage() {
|
||||||
function applyFilters() {
|
function applyFilters() {
|
||||||
const filterDate = document.getElementById('filterDate').value;
|
const filterDate = document.getElementById('filterDate').value;
|
||||||
const timeFilterEnabled = document.getElementById('filterTimeToggle').checked;
|
const timeFilterEnabled = document.getElementById('filterTimeToggle').checked;
|
||||||
|
const categories = Array.from(document.getElementById('categories').selectedOptions).map(opt => opt.value);
|
||||||
// Simpan halaman sebelumnya
|
|
||||||
const prevPage = currentPage;
|
|
||||||
|
|
||||||
// Set flag filter aktif
|
// Set flag filter aktif
|
||||||
isFilterActive = filterDate || timeFilterEnabled;
|
isFilterActive = filterDate || timeFilterEnabled || categories.length > 0;
|
||||||
|
selectedCategories = categories;
|
||||||
|
|
||||||
let startTime = null;
|
let filteredData = allReports;
|
||||||
let endTime = null;
|
|
||||||
|
|
||||||
if (timeFilterEnabled) {
|
// Filter berdasarkan tanggal dan waktu
|
||||||
startTime = document.getElementById('startTime').value;
|
if (filterDate || timeFilterEnabled) {
|
||||||
endTime = document.getElementById('endTime').value;
|
filteredData = filteredData.filter(report => {
|
||||||
|
|
||||||
// Convert to minutes for easier comparison
|
|
||||||
const [startHour, startMinute] = startTime.split(':').map(Number);
|
|
||||||
const [endHour, endMinute] = endTime.split(':').map(Number);
|
|
||||||
|
|
||||||
const startTotalMinutes = startHour * 60 + startMinute;
|
|
||||||
const endTotalMinutes = endHour * 60 + endMinute;
|
|
||||||
|
|
||||||
// Filter data berdasarkan kriteria
|
|
||||||
filteredReports = allReports.filter(report => {
|
|
||||||
const reportDate = new Date(report.timestamp);
|
const reportDate = new Date(report.timestamp);
|
||||||
|
|
||||||
// Filter berdasarkan tanggal
|
// Filter tanggal
|
||||||
if (filterDate) {
|
if (filterDate) {
|
||||||
const dateStr = reportDate.toISOString().split('T')[0];
|
const dateStr = reportDate.toISOString().split('T')[0];
|
||||||
if (dateStr !== filterDate) return false;
|
if (dateStr !== filterDate) return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter berdasarkan rentang waktu
|
// Filter waktu
|
||||||
if (timeFilterEnabled) {
|
if (timeFilterEnabled) {
|
||||||
|
const startTime = document.getElementById('startTime').value;
|
||||||
|
const endTime = document.getElementById('endTime').value;
|
||||||
|
const [startHour, startMinute] = startTime.split(':').map(Number);
|
||||||
|
const [endHour, endMinute] = endTime.split(':').map(Number);
|
||||||
|
|
||||||
|
const startTotalMinutes = startHour * 60 + startMinute;
|
||||||
|
const endTotalMinutes = endHour * 60 + endMinute;
|
||||||
|
|
||||||
const hour = reportDate.getHours();
|
const hour = reportDate.getHours();
|
||||||
const minute = reportDate.getMinutes();
|
const minute = reportDate.getMinutes();
|
||||||
const totalMinutes = hour * 60 + minute;
|
const totalMinutes = hour * 60 + minute;
|
||||||
|
|
||||||
// Handle case where time range crosses midnight
|
|
||||||
if (startTotalMinutes <= endTotalMinutes) {
|
if (startTotalMinutes <= endTotalMinutes) {
|
||||||
// Normal case (e.g., 08:00 to 17:00)
|
|
||||||
if (totalMinutes < startTotalMinutes || totalMinutes > endTotalMinutes) return false;
|
if (totalMinutes < startTotalMinutes || totalMinutes > endTotalMinutes) return false;
|
||||||
} else {
|
} else {
|
||||||
// Overnight case (e.g., 22:00 to 06:00)
|
|
||||||
if (totalMinutes < startTotalMinutes && totalMinutes > endTotalMinutes) return false;
|
if (totalMinutes < startTotalMinutes && totalMinutes > endTotalMinutes) return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
} else {
|
}
|
||||||
// Jika filter waktu tidak aktif, hanya filter berdasarkan tanggal
|
|
||||||
filteredReports = allReports.filter(report => {
|
|
||||||
const reportDate = new Date(report.timestamp);
|
|
||||||
|
|
||||||
// Filter berdasarkan tanggal
|
// Filter berdasarkan kategori
|
||||||
if (filterDate) {
|
if (categories.length > 0) {
|
||||||
const dateStr = reportDate.toISOString().split('T')[0];
|
filteredData = filteredData.filter(report => {
|
||||||
if (dateStr !== filterDate) return false;
|
// Deteksi perubahan untuk report ini
|
||||||
}
|
const changes = detectChanges(report, allReports, allReports.indexOf(report));
|
||||||
|
// Cek apakah ada perubahan yang masuk dalam kategori yang dipilih
|
||||||
return true;
|
return changes.some(change => categories.includes(change.badge));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset halaman ke 1 HANYA ketika menerapkan filter baru (karena ini eksplisit pengguna memfilter)
|
// Update data terfilter
|
||||||
|
filteredReports = filteredData;
|
||||||
|
|
||||||
|
// Reset ke halaman pertama
|
||||||
currentPage = 1;
|
currentPage = 1;
|
||||||
|
|
||||||
// PENTING: Update chart dengan data yang sudah difilter
|
// Update chart
|
||||||
renderSystemOverviewChart(filteredReports);
|
renderSystemOverviewChart(filteredReports);
|
||||||
|
|
||||||
// Render data yang telah difilter
|
// Render data
|
||||||
renderPaginatedReports(true);
|
renderPaginatedReports(true);
|
||||||
|
|
||||||
// Tampilkan notifikasi hasil filter
|
// Tampilkan notifikasi hasil filter
|
||||||
const filterCount = filteredReports.length;
|
const filterCount = filteredReports.length;
|
||||||
const totalCount = allReports.length;
|
const totalCount = allReports.length;
|
||||||
if (filterCount === 0) {
|
|
||||||
showFilterNotification(`Tidak ada data yang cocok dengan filter`);
|
let filterMessage = `Ditemukan ${filterCount} dari ${totalCount} data`;
|
||||||
} else {
|
if (categories.length > 0) {
|
||||||
showFilterNotification(`Ditemukan ${filterCount} dari ${totalCount} data`);
|
filterMessage += ` dengan kategori: ${categories.join(', ')}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tambahkan badge untuk menampilkan status filter pada chart
|
showFilterNotification(filterCount === 0 ? 'Tidak ada data yang cocok dengan filter' : filterMessage);
|
||||||
|
|
||||||
|
// Update status filter pada chart
|
||||||
updateChartFilterStatus();
|
updateChartFilterStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -481,6 +546,8 @@ function resetFilters() {
|
||||||
document.getElementById('filterTimeToggle').checked = false;
|
document.getElementById('filterTimeToggle').checked = false;
|
||||||
document.getElementById('timeRangePicker').classList.add('hidden');
|
document.getElementById('timeRangePicker').classList.add('hidden');
|
||||||
document.getElementById('timeToggleStatus').textContent = 'Semua Waktu';
|
document.getElementById('timeToggleStatus').textContent = 'Semua Waktu';
|
||||||
|
document.getElementById('categories').selectedIndex = -1; // Reset multiple select
|
||||||
|
selectedCategories = [];
|
||||||
|
|
||||||
// Reset flag filter
|
// Reset flag filter
|
||||||
isFilterActive = false;
|
isFilterActive = false;
|
||||||
|
@ -489,7 +556,7 @@ function resetFilters() {
|
||||||
filteredReports = allReports;
|
filteredReports = allReports;
|
||||||
currentPage = 1;
|
currentPage = 1;
|
||||||
|
|
||||||
// PENTING: Update chart dengan semua data
|
// Update chart dengan semua data
|
||||||
renderSystemOverviewChart(allReports);
|
renderSystemOverviewChart(allReports);
|
||||||
|
|
||||||
// Update chart filter status
|
// Update chart filter status
|
||||||
|
@ -745,15 +812,25 @@ function renderReports(reports) {
|
||||||
const changes = detectChanges(report, allReports, allReports.indexOf(report));
|
const changes = detectChanges(report, allReports, allReports.indexOf(report));
|
||||||
changes.forEach(change => {
|
changes.forEach(change => {
|
||||||
const badge = document.createElement('span');
|
const badge = document.createElement('span');
|
||||||
// Flowbite badge styles
|
|
||||||
|
// Flowbite badge styles dengan warna khusus untuk setiap jenis
|
||||||
const badgeClasses = {
|
const badgeClasses = {
|
||||||
'primary': 'bg-blue-100 text-blue-800',
|
'primary': 'bg-blue-100 text-blue-800',
|
||||||
'secondary': 'bg-gray-100 text-gray-800',
|
'motion': 'bg-yellow-100 text-yellow-800',
|
||||||
'danger': 'bg-red-100 text-red-800',
|
'status': 'bg-red-100 text-red-800',
|
||||||
'warning': 'bg-yellow-100 text-yellow-800',
|
'fan': 'bg-green-100 text-green-800',
|
||||||
'info': 'bg-indigo-100 text-indigo-800',
|
'last-access': 'bg-violet-100 text-violet-800',
|
||||||
'light': 'bg-gray-100 text-gray-800',
|
'servo-status': 'bg-cyan-100 text-cyan-800',
|
||||||
'dark': 'bg-gray-700 text-gray-300'
|
'restart-esp': 'bg-orange-100 text-orange-800',
|
||||||
|
'restart-wemos': 'bg-amber-100 text-amber-800',
|
||||||
|
'rfid': 'bg-indigo-100 text-indigo-800',
|
||||||
|
'dht': 'bg-lime-100 text-lime-800',
|
||||||
|
'mpu': 'bg-teal-100 text-teal-800',
|
||||||
|
'servo-log': 'bg-sky-100 text-sky-800',
|
||||||
|
'system-esp': 'bg-rose-100 text-rose-800',
|
||||||
|
'system-wemos': 'bg-pink-100 text-pink-800',
|
||||||
|
'device': 'bg-gray-100 text-gray-800',
|
||||||
|
'light': 'bg-gray-100 text-gray-400'
|
||||||
};
|
};
|
||||||
|
|
||||||
badge.className = `px-2 py-0.5 rounded text-xs font-medium me-2 ${badgeClasses[change.badge] || badgeClasses['light']}`;
|
badge.className = `px-2 py-0.5 rounded text-xs font-medium me-2 ${badgeClasses[change.badge] || badgeClasses['light']}`;
|
||||||
|
@ -810,41 +887,81 @@ function detectChanges(report, reports, index) {
|
||||||
// Security changes
|
// Security changes
|
||||||
if (report.security && prevReport.security) {
|
if (report.security && prevReport.security) {
|
||||||
if (report.security.motion !== prevReport.security.motion) {
|
if (report.security.motion !== prevReport.security.motion) {
|
||||||
changes.push({ type: 'Gerakan', badge: 'danger' });
|
changes.push({ type: 'Motion', badge: 'motion' });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (report.security.status !== prevReport.security.status) {
|
if (report.security.status !== prevReport.security.status) {
|
||||||
changes.push({ type: 'Status Keamanan', badge: 'danger' });
|
changes.push({ type: 'Status', badge: 'status' });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (report.security.fan !== prevReport.security.fan) {
|
||||||
|
changes.push({ type: 'Fan', badge: 'fan' });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Smartcab changes
|
// Smartcab changes
|
||||||
if (report.smartcab && prevReport.smartcab) {
|
if (report.smartcab && prevReport.smartcab) {
|
||||||
if (report.smartcab.last_access !== prevReport.smartcab.last_access) {
|
if (report.smartcab.last_access !== prevReport.smartcab.last_access) {
|
||||||
changes.push({ type: 'Akses Terakhir', badge: 'info' });
|
changes.push({ type: 'Last Access', badge: 'last-access' });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (report.smartcab.servo_status !== prevReport.smartcab.servo_status) {
|
if (report.smartcab.servo_status !== prevReport.smartcab.servo_status) {
|
||||||
changes.push({ type: 'Status Servo', badge: 'warning' });
|
changes.push({ type: 'Servo Status', badge: 'servo-status' });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Control changes
|
// Control changes
|
||||||
if (report.control && prevReport.control &&
|
if (report.control && prevReport.control) {
|
||||||
JSON.stringify(report.control) !== JSON.stringify(prevReport.control)) {
|
if (report.control.restartESP !== prevReport.control.restartESP) {
|
||||||
changes.push({ type: 'Kontrol Perangkat', badge: 'secondary' });
|
changes.push({ type: 'Restart ESP', badge: 'restart-esp' });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Device changes
|
if (report.control.restartWemos !== prevReport.control.restartWemos) {
|
||||||
if (report.device && prevReport.device &&
|
changes.push({ type: 'Restart Wemos', badge: 'restart-wemos' });
|
||||||
JSON.stringify(report.device) !== JSON.stringify(prevReport.device)) {
|
}
|
||||||
changes.push({ type: 'Status Perangkat', badge: 'secondary' });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Logs changes
|
// Logs changes
|
||||||
if (report.logs && prevReport.logs &&
|
if (report.logs && prevReport.logs) {
|
||||||
JSON.stringify(report.logs) !== JSON.stringify(prevReport.logs)) {
|
// RFID logs
|
||||||
changes.push({ type: 'Log Sistem', badge: 'dark' });
|
if (report.logs.RFID && prevReport.logs.RFID &&
|
||||||
|
JSON.stringify(report.logs.RFID) !== JSON.stringify(prevReport.logs.RFID)) {
|
||||||
|
changes.push({ type: 'RFID', badge: 'rfid' });
|
||||||
|
}
|
||||||
|
|
||||||
|
// DHT logs
|
||||||
|
if (report.logs.dht && prevReport.logs.dht &&
|
||||||
|
JSON.stringify(report.logs.dht) !== JSON.stringify(prevReport.logs.dht)) {
|
||||||
|
changes.push({ type: 'DHT', badge: 'dht' });
|
||||||
|
}
|
||||||
|
|
||||||
|
// MPU logs
|
||||||
|
if (report.logs.mpu && prevReport.logs.mpu &&
|
||||||
|
JSON.stringify(report.logs.mpu) !== JSON.stringify(prevReport.logs.mpu)) {
|
||||||
|
changes.push({ type: 'MPU', badge: 'mpu' });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Servo logs
|
||||||
|
if (report.logs.servo && prevReport.logs.servo &&
|
||||||
|
JSON.stringify(report.logs.servo) !== JSON.stringify(prevReport.logs.servo)) {
|
||||||
|
changes.push({ type: 'Servo Log', badge: 'servo-log' });
|
||||||
|
}
|
||||||
|
|
||||||
|
// System ESP logs
|
||||||
|
if (report.logs.systemESP !== prevReport.logs.systemESP) {
|
||||||
|
changes.push({ type: 'System ESP', badge: 'system-esp' });
|
||||||
|
}
|
||||||
|
|
||||||
|
// System Wemos logs
|
||||||
|
if (report.logs.systemWemos !== prevReport.logs.systemWemos) {
|
||||||
|
changes.push({ type: 'System Wemos', badge: 'system-wemos' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Device changes - general fallback if needed
|
||||||
|
if (report.device && prevReport.device &&
|
||||||
|
JSON.stringify(report.device) !== JSON.stringify(prevReport.device)) {
|
||||||
|
changes.push({ type: 'Device Status', badge: 'device' });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (changes.length === 0) {
|
if (changes.length === 0) {
|
||||||
|
@ -1039,145 +1156,186 @@ function showDetail(report) {
|
||||||
function analyzeSystemOverview(reports) {
|
function analyzeSystemOverview(reports) {
|
||||||
// Siapkan variabel untuk menyimpan hitungan status
|
// Siapkan variabel untuk menyimpan hitungan status
|
||||||
const statusCounts = {
|
const statusCounts = {
|
||||||
'securityNormal': 0, // Status keamanan normal
|
'motion': 0, // Motion status
|
||||||
'securityDanger': 0, // Status keamanan bahaya
|
'status': 0, // Security status
|
||||||
'motionDetected': 0, // Gerakan terdeteksi
|
'fan': 0, // Fan status
|
||||||
'motionNotDetected': 0, // Gerakan tidak terdeteksi
|
'servoStatus': 0, // Servo status
|
||||||
'servoLocked': 0, // Servo terkunci
|
'lastAccess': 0, // Last access
|
||||||
'servoUnlocked': 0, // Servo terbuka
|
'restartEsp': 0, // Restart ESP
|
||||||
'accessChanged': 0, // Perubahan akses terakhir
|
'restartWemos': 0, // Restart Wemos
|
||||||
'controlChanged': 0, // Perubahan kontrol perangkat
|
'rfid': 0, // RFID
|
||||||
'deviceChanged': 0, // Perubahan status perangkat
|
'dht': 0, // DHT sensor
|
||||||
'logError': 0 // Log error
|
'mpu': 0, // MPU sensor
|
||||||
|
'servoLog': 0, // Servo log
|
||||||
|
'systemEsp': 0, // System ESP
|
||||||
|
'systemWemos': 0, // System Wemos
|
||||||
|
'deviceStatus': 0 // Device status
|
||||||
};
|
};
|
||||||
|
|
||||||
// Total untuk persentase dan statusCounts
|
// Total untuk persentase
|
||||||
let totalCounts = 0;
|
let totalCounts = 0;
|
||||||
|
|
||||||
|
// Fungsi untuk menghitung perubahan antar report
|
||||||
|
function countChanges(report, index) {
|
||||||
|
if (index === 0 || index >= reports.length - 1) return [];
|
||||||
|
|
||||||
|
const changes = [];
|
||||||
|
const prevReport = reports[index + 1]; // Data baris sebelumnya (karena data sorted terbaru dulu)
|
||||||
|
|
||||||
|
// Security changes
|
||||||
|
if (report.security && prevReport.security) {
|
||||||
|
if (report.security.motion !== prevReport.security.motion) {
|
||||||
|
changes.push('motion');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (report.security.status !== prevReport.security.status) {
|
||||||
|
changes.push('status');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (report.security.fan !== prevReport.security.fan) {
|
||||||
|
changes.push('fan');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Smartcab changes
|
||||||
|
if (report.smartcab && prevReport.smartcab) {
|
||||||
|
if (report.smartcab.last_access !== prevReport.smartcab.last_access) {
|
||||||
|
changes.push('lastAccess');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (report.smartcab.servo_status !== prevReport.smartcab.servo_status) {
|
||||||
|
changes.push('servoStatus');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Control changes
|
||||||
|
if (report.control && prevReport.control) {
|
||||||
|
if (report.control.restartESP !== prevReport.control.restartESP) {
|
||||||
|
changes.push('restartEsp');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (report.control.restartWemos !== prevReport.control.restartWemos) {
|
||||||
|
changes.push('restartWemos');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logs changes
|
||||||
|
if (report.logs && prevReport.logs) {
|
||||||
|
// RFID logs
|
||||||
|
if (report.logs.RFID && prevReport.logs.RFID &&
|
||||||
|
JSON.stringify(report.logs.RFID) !== JSON.stringify(prevReport.logs.RFID)) {
|
||||||
|
changes.push('rfid');
|
||||||
|
}
|
||||||
|
|
||||||
|
// DHT logs
|
||||||
|
if (report.logs.dht && prevReport.logs.dht &&
|
||||||
|
JSON.stringify(report.logs.dht) !== JSON.stringify(prevReport.logs.dht)) {
|
||||||
|
changes.push('dht');
|
||||||
|
}
|
||||||
|
|
||||||
|
// MPU logs
|
||||||
|
if (report.logs.mpu && prevReport.logs.mpu &&
|
||||||
|
JSON.stringify(report.logs.mpu) !== JSON.stringify(prevReport.logs.mpu)) {
|
||||||
|
changes.push('mpu');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Servo logs
|
||||||
|
if (report.logs.servo && prevReport.logs.servo &&
|
||||||
|
JSON.stringify(report.logs.servo) !== JSON.stringify(prevReport.logs.servo)) {
|
||||||
|
changes.push('servoLog');
|
||||||
|
}
|
||||||
|
|
||||||
|
// System ESP logs
|
||||||
|
if (report.logs.systemESP !== prevReport.logs.systemESP) {
|
||||||
|
changes.push('systemEsp');
|
||||||
|
}
|
||||||
|
|
||||||
|
// System Wemos logs
|
||||||
|
if (report.logs.systemWemos !== prevReport.logs.systemWemos) {
|
||||||
|
changes.push('systemWemos');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Device changes - general fallback if needed
|
||||||
|
if (report.device && prevReport.device &&
|
||||||
|
JSON.stringify(report.device) !== JSON.stringify(prevReport.device)) {
|
||||||
|
changes.push('deviceStatus');
|
||||||
|
}
|
||||||
|
|
||||||
|
return changes;
|
||||||
|
}
|
||||||
|
|
||||||
// Periksa setiap laporan untuk mengisi kategori
|
// Periksa setiap laporan untuk mengisi kategori
|
||||||
reports.forEach(report => {
|
reports.forEach((report, index) => {
|
||||||
let categoriesAdded = 0;
|
const changes = countChanges(report, index);
|
||||||
|
|
||||||
// Cek status keamanan
|
changes.forEach(change => {
|
||||||
if (report.security) {
|
statusCounts[change]++;
|
||||||
if (report.security.status) {
|
totalCounts++;
|
||||||
if (report.security.status.toLowerCase() === 'bahaya') {
|
});
|
||||||
statusCounts['securityDanger']++;
|
|
||||||
categoriesAdded++;
|
|
||||||
} else if (report.security.status.toLowerCase() === 'aman') {
|
|
||||||
statusCounts['securityNormal']++;
|
|
||||||
categoriesAdded++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (report.security.motion) {
|
// Jika tidak ada perubahan, tambahkan satu ke total untuk report ini
|
||||||
if (report.security.motion.toLowerCase() === 'detected') {
|
if (changes.length === 0 && index > 0 && index < reports.length - 1) {
|
||||||
statusCounts['motionDetected']++;
|
totalCounts++;
|
||||||
categoriesAdded++;
|
|
||||||
} else if (report.security.motion.toLowerCase() === 'not detected') {
|
|
||||||
statusCounts['motionNotDetected']++;
|
|
||||||
categoriesAdded++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cek status smartcab
|
|
||||||
if (report.smartcab) {
|
|
||||||
if (report.smartcab.servo_status) {
|
|
||||||
if (report.smartcab.servo_status.toLowerCase() === 'locked') {
|
|
||||||
statusCounts['servoLocked']++;
|
|
||||||
categoriesAdded++;
|
|
||||||
} else if (report.smartcab.servo_status.toLowerCase() === 'unlocked') {
|
|
||||||
statusCounts['servoUnlocked']++;
|
|
||||||
categoriesAdded++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (report.smartcab.last_access) {
|
|
||||||
statusCounts['accessChanged']++;
|
|
||||||
categoriesAdded++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cek perubahan kontrol
|
|
||||||
if (report.control && Object.keys(report.control).length > 0) {
|
|
||||||
statusCounts['controlChanged']++;
|
|
||||||
categoriesAdded++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cek perubahan device
|
|
||||||
if (report.device && Object.keys(report.device).length > 0) {
|
|
||||||
statusCounts['deviceChanged']++;
|
|
||||||
categoriesAdded++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cek logs untuk error
|
|
||||||
if (report.logs) {
|
|
||||||
let hasError = false;
|
|
||||||
for (const logKey in report.logs) {
|
|
||||||
if (report.logs[logKey] && report.logs[logKey].status) {
|
|
||||||
const status = report.logs[logKey].status.toLowerCase();
|
|
||||||
if (status.includes('error') || status.includes('warning')) {
|
|
||||||
hasError = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasError) {
|
|
||||||
statusCounts['logError']++;
|
|
||||||
categoriesAdded++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tambahkan jumlah kategori ke total
|
|
||||||
totalCounts += categoriesAdded > 0 ? categoriesAdded : 1;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Update counter
|
// Update counter
|
||||||
document.getElementById('system-chart-count').textContent =
|
document.getElementById('system-chart-count').textContent =
|
||||||
`Total: ${reports.length} laporan dengan ${totalCounts} status`;
|
`Total: ${reports.length} laporan dengan ${totalCounts} perubahan status`;
|
||||||
|
|
||||||
// Kembalikan array untuk chart dengan hanya nilai yang bukan nol
|
// Kembalikan array untuk chart dengan hanya nilai yang bukan nol
|
||||||
const resultData = [
|
const resultData = [
|
||||||
statusCounts['securityNormal'],
|
statusCounts['fan'],
|
||||||
statusCounts['securityDanger'],
|
statusCounts['status'],
|
||||||
statusCounts['motionDetected'],
|
statusCounts['motion'],
|
||||||
statusCounts['motionNotDetected'],
|
statusCounts['servoStatus'],
|
||||||
statusCounts['servoLocked'],
|
statusCounts['lastAccess'],
|
||||||
statusCounts['servoUnlocked'],
|
statusCounts['restartEsp'],
|
||||||
statusCounts['accessChanged'],
|
statusCounts['restartWemos'],
|
||||||
statusCounts['controlChanged'],
|
statusCounts['rfid'],
|
||||||
statusCounts['deviceChanged'],
|
statusCounts['dht'],
|
||||||
statusCounts['logError']
|
statusCounts['mpu'],
|
||||||
|
statusCounts['servoLog'],
|
||||||
|
statusCounts['systemEsp'],
|
||||||
|
statusCounts['systemWemos'],
|
||||||
|
statusCounts['deviceStatus']
|
||||||
];
|
];
|
||||||
|
|
||||||
// Label yang sesuai dengan resultData
|
// Label yang sesuai dengan resultData
|
||||||
const resultLabels = [
|
const resultLabels = [
|
||||||
'Keamanan Normal',
|
'Fan',
|
||||||
'Status Bahaya',
|
'Status Keamanan',
|
||||||
'Gerakan Terdeteksi',
|
'Motion',
|
||||||
'Tidak Ada Gerakan',
|
'Servo Status',
|
||||||
'Servo Terkunci',
|
'Last Access',
|
||||||
'Servo Terbuka',
|
'Restart ESP',
|
||||||
'Akses Terakhir',
|
'Restart Wemos',
|
||||||
'Kontrol Diubah',
|
'RFID',
|
||||||
'Status Perangkat',
|
'DHT',
|
||||||
'Error/Warning'
|
'MPU',
|
||||||
|
'Servo Log',
|
||||||
|
'System ESP',
|
||||||
|
'System Wemos',
|
||||||
|
'Device Status'
|
||||||
];
|
];
|
||||||
|
|
||||||
// Warna yang sesuai dengan resultData
|
// Warna yang sesuai dengan resultData (sesuai dengan badge colors)
|
||||||
const resultColors = [
|
const resultColors = [
|
||||||
'#22c55e', // Keamanan Normal (Hijau)
|
'#22c55e', // Fan (Hijau)
|
||||||
'#ef4444', // Status Bahaya (Merah)
|
'#ef4444', // Status (Merah)
|
||||||
'#eab308', // Gerakan Terdeteksi (Kuning)
|
'#eab308', // Motion (Kuning)
|
||||||
'#84cc16', // Tidak Ada Gerakan (Hijau Muda)
|
'#06b6d4', // Servo Status (Cyan)
|
||||||
'#3b82f6', // Servo Terkunci (Biru)
|
'#8b5cf6', // Last Access (Ungu Muda)
|
||||||
'#06b6d4', // Servo Terbuka (Cyan)
|
'#f97316', // Restart ESP (Oranye)
|
||||||
'#8b5cf6', // Akses Terakhir (Ungu Muda)
|
'#f59e0b', // Restart Wemos (Amber)
|
||||||
'#a855f7', // Kontrol Diubah (Ungu)
|
'#6366f1', // RFID (Indigo)
|
||||||
'#ec4899', // Status Perangkat (Pink)
|
'#84cc16', // DHT (Lime)
|
||||||
'#f97316' // Error/Warning (Oranye)
|
'#14b8a6', // MPU (Teal)
|
||||||
|
'#0ea5e9', // Servo Log (Sky)
|
||||||
|
'#e11d48', // System ESP (Rose)
|
||||||
|
'#ec4899', // System Wemos (Pink)
|
||||||
|
'#6b7280' // Device Status (Gray)
|
||||||
];
|
];
|
||||||
|
|
||||||
// Filter untuk menghilangkan kategori dengan nilai nol
|
// Filter untuk menghilangkan kategori dengan nilai nol
|
||||||
|
@ -1298,32 +1456,36 @@ function updateChartFilterStatus() {
|
||||||
const filterDate = document.getElementById('filterDate').value;
|
const filterDate = document.getElementById('filterDate').value;
|
||||||
const timeFilterEnabled = document.getElementById('filterTimeToggle').checked;
|
const timeFilterEnabled = document.getElementById('filterTimeToggle').checked;
|
||||||
|
|
||||||
let filterInfo = '';
|
let filterInfo = [];
|
||||||
|
|
||||||
if (filterDate) {
|
if (filterDate) {
|
||||||
const formattedDate = new Date(filterDate).toLocaleDateString('id-ID', {
|
const formattedDate = new Date(filterDate).toLocaleDateString('id-ID', {
|
||||||
day: '2-digit',
|
day: '2-digit',
|
||||||
month: '2-digit',
|
month: '2-digit',
|
||||||
year: 'numeric'
|
year: 'numeric'
|
||||||
});
|
});
|
||||||
filterInfo += `Tanggal: ${formattedDate}`;
|
filterInfo.push(`Tanggal: ${formattedDate}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (timeFilterEnabled) {
|
if (timeFilterEnabled) {
|
||||||
const startTime = document.getElementById('startTime').value;
|
const startTime = document.getElementById('startTime').value;
|
||||||
const endTime = document.getElementById('endTime').value;
|
const endTime = document.getElementById('endTime').value;
|
||||||
if (filterInfo) filterInfo += ' | ';
|
filterInfo.push(`Waktu: ${startTime} - ${endTime}`);
|
||||||
filterInfo += `Waktu: ${startTime} - ${endTime}`;
|
}
|
||||||
|
|
||||||
|
if (selectedCategories.length > 0) {
|
||||||
|
filterInfo.push(`Kategori: ${selectedCategories.join(', ')}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update text pada chart count
|
// Update text pada chart count
|
||||||
if (filterInfo) {
|
if (filterInfo.length > 0) {
|
||||||
chartCountElement.innerHTML = `
|
chartCountElement.innerHTML = `
|
||||||
<span class="font-medium">Data Terfilter:</span> ${filteredReports.length} dari ${allReports.length}
|
<span class="font-medium">Data Terfilter:</span> ${filteredReports.length} dari ${allReports.length}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
// Tampilkan badge filter
|
// Tampilkan badge filter
|
||||||
chartFilterBadge.classList.remove('hidden');
|
chartFilterBadge.classList.remove('hidden');
|
||||||
chartFilterText.textContent = filterInfo;
|
chartFilterText.textContent = filterInfo.join(' | ');
|
||||||
} else {
|
} else {
|
||||||
chartCountElement.textContent = `Total: ${allReports.length} laporan dengan ${analyzeSystemOverview(allReports).series.reduce((a, b) => a + b, 0)} status`;
|
chartCountElement.textContent = `Total: ${allReports.length} laporan dengan ${analyzeSystemOverview(allReports).series.reduce((a, b) => a + b, 0)} status`;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue