TKK_E32231477/create_mysql_tables.sql

652 lines
27 KiB
SQL

-- =====================================================
-- SMART RACK SECURITY SYSTEM - MySQL Database Schema
-- =====================================================
-- Generated: 2026-04-29
-- Compatible with: MySQL 8.0+, MariaDB 10.3+
-- =====================================================
-- Create database
CREATE DATABASE IF NOT EXISTS smart_rack_security
CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE smart_rack_security;
-- =====================================================
-- 1. USERS TABLE
-- =====================================================
CREATE TABLE users (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
email_verified_at TIMESTAMP NULL,
password VARCHAR(255) NOT NULL,
role ENUM('admin', 'operator', 'viewer') DEFAULT 'viewer',
is_active BOOLEAN DEFAULT TRUE,
last_login_at TIMESTAMP NULL,
remember_token VARCHAR(100) NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_email (email),
INDEX idx_role_active (role, is_active)
);
-- =====================================================
-- 2. DEVICES TABLE (Arduino/LoRa Nodes)
-- =====================================================
CREATE TABLE devices (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
device_id VARCHAR(50) UNIQUE NOT NULL COMMENT 'Node ID seperti LORA_001',
name VARCHAR(255) NOT NULL,
type ENUM('lora_node', 'gateway', 'sensor_hub') DEFAULT 'lora_node',
location VARCHAR(255) NULL COMMENT 'Lokasi fisik device',
ip_address VARCHAR(45) NULL,
mac_address VARCHAR(17) NULL,
firmware_version VARCHAR(50) NULL,
battery_level DECIMAL(5,2) DEFAULT 100.00 COMMENT 'Battery percentage',
signal_strength INT DEFAULT 0 COMMENT 'Signal strength in dBm',
is_online BOOLEAN DEFAULT FALSE,
last_seen_at TIMESTAMP NULL,
configuration JSON NULL COMMENT 'Device specific config',
metadata JSON NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_device_id (device_id),
INDEX idx_type_online (type, is_online),
INDEX idx_last_seen (last_seen_at)
);
-- =====================================================
-- 3. SENSORS TABLE
-- =====================================================
CREATE TABLE sensors (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
device_id BIGINT UNSIGNED NOT NULL,
sensor_type ENUM('pir', 'reed_switch', 'vibration', 'temperature', 'humidity', 'door_access') NOT NULL,
sensor_name VARCHAR(255) NOT NULL,
pin_number INT NULL COMMENT 'GPIO pin number',
unit VARCHAR(20) NULL COMMENT 'Measurement unit',
min_value DECIMAL(10,4) NULL,
max_value DECIMAL(10,4) NULL,
threshold DECIMAL(10,4) NULL COMMENT 'Alert threshold',
calibration_offset DECIMAL(10,4) DEFAULT 0,
is_active BOOLEAN DEFAULT TRUE,
metadata JSON NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (device_id) REFERENCES devices(id) ON DELETE CASCADE,
INDEX idx_device_sensor (device_id, sensor_type),
INDEX idx_sensor_type_active (sensor_type, is_active)
);
-- =====================================================
-- 4. PIR READINGS TABLE (Motion Detection)
-- =====================================================
CREATE TABLE pir_readings (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
device_id BIGINT UNSIGNED NOT NULL,
motion_detected BOOLEAN DEFAULT FALSE COMMENT 'Gerakan terdeteksi',
motion_intensity INT DEFAULT 0 COMMENT 'Intensitas gerakan (0-100)',
duration_seconds INT DEFAULT 0 COMMENT 'Durasi gerakan dalam detik',
is_authorized_time BOOLEAN DEFAULT TRUE COMMENT 'Apakah dalam jam kerja',
is_suspicious BOOLEAN DEFAULT FALSE COMMENT 'Gerakan mencurigakan',
motion_type ENUM('normal', 'suspicious', 'unauthorized') DEFAULT 'normal',
detection_zone VARCHAR(50) NULL COMMENT 'Area deteksi (front, back, side, center)',
metadata JSON NULL COMMENT 'Data tambahan sensor',
motion_start TIMESTAMP NULL COMMENT 'Waktu mulai gerakan',
motion_end TIMESTAMP NULL COMMENT 'Waktu selesai gerakan',
recorded_at TIMESTAMP NOT NULL COMMENT 'Waktu pembacaan sensor',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (device_id) REFERENCES devices(id) ON DELETE CASCADE,
INDEX idx_device_recorded (device_id, recorded_at),
INDEX idx_motion_suspicious (motion_detected, is_suspicious),
INDEX idx_authorized_type (is_authorized_time, motion_type),
INDEX idx_recorded_at (recorded_at)
);
-- =====================================================
-- 5. DOOR READINGS TABLE (Reed Switch)
-- =====================================================
CREATE TABLE door_readings (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
device_id BIGINT UNSIGNED NOT NULL,
door_open BOOLEAN DEFAULT FALSE COMMENT 'Status pintu terbuka',
is_authorized_access BOOLEAN DEFAULT TRUE COMMENT 'Akses yang sah',
is_forced_entry BOOLEAN DEFAULT FALSE COMMENT 'Pembukaan paksa',
access_type ENUM('normal', 'unauthorized', 'forced', 'maintenance') DEFAULT 'normal',
door_location VARCHAR(100) NULL COMMENT 'front_panel, back_panel, side_door, main_door',
open_duration_seconds INT DEFAULT 0 COMMENT 'Durasi terbuka dalam detik',
proper_closure BOOLEAN DEFAULT TRUE COMMENT 'Apakah ditutup dengan benar',
access_card_data JSON NULL COMMENT 'Data kartu akses jika ada',
metadata JSON NULL COMMENT 'Data tambahan sensor',
door_opened_at TIMESTAMP NULL COMMENT 'Waktu pintu dibuka',
door_closed_at TIMESTAMP NULL COMMENT 'Waktu pintu ditutup',
recorded_at TIMESTAMP NOT NULL COMMENT 'Waktu pembacaan sensor',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (device_id) REFERENCES devices(id) ON DELETE CASCADE,
INDEX idx_device_recorded (device_id, recorded_at),
INDEX idx_door_authorized (door_open, is_authorized_access),
INDEX idx_access_location (access_type, door_location),
INDEX idx_forced_entry (is_forced_entry, proper_closure),
INDEX idx_recorded_at (recorded_at)
);
-- =====================================================
-- 6. VIBRATION READINGS TABLE (SW-420 Sensor)
-- =====================================================
CREATE TABLE vibration_readings (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
device_id BIGINT UNSIGNED NOT NULL,
x_axis DECIMAL(8,4) NOT NULL COMMENT 'Getaran sumbu X',
y_axis DECIMAL(8,4) NOT NULL COMMENT 'Getaran sumbu Y',
z_axis DECIMAL(8,4) NOT NULL COMMENT 'Getaran sumbu Z',
magnitude DECIMAL(8,4) GENERATED ALWAYS AS (SQRT(POW(x_axis,2) + POW(y_axis,2) + POW(z_axis,2))) STORED COMMENT 'Total magnitude getaran',
is_abnormal BOOLEAN DEFAULT FALSE COMMENT 'Status getaran abnormal',
threshold DECIMAL(8,4) DEFAULT 2.0000 COMMENT 'Batas normal getaran',
status ENUM('normal', 'warning', 'critical') DEFAULT 'normal',
metadata JSON NULL COMMENT 'Data tambahan sensor',
recorded_at TIMESTAMP NOT NULL COMMENT 'Waktu pembacaan sensor',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (device_id) REFERENCES devices(id) ON DELETE CASCADE,
INDEX idx_device_recorded (device_id, recorded_at),
INDEX idx_abnormal_status (is_abnormal, status),
INDEX idx_magnitude (magnitude),
INDEX idx_recorded_at (recorded_at)
);
-- =====================================================
-- 7. DOOR ACCESS READINGS TABLE (Detailed Access Log)
-- =====================================================
CREATE TABLE door_access_readings (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
device_id BIGINT UNSIGNED NOT NULL,
door_opened BOOLEAN DEFAULT FALSE,
user_id_card VARCHAR(100) NULL COMMENT 'ID Card atau badge number',
duration_seconds INT DEFAULT 0,
access_method ENUM('keycard', 'manual', 'forced', 'maintenance') DEFAULT 'manual',
door_location VARCHAR(100) NULL,
is_authorized BOOLEAN DEFAULT TRUE,
access_granted_by VARCHAR(100) NULL COMMENT 'Who granted access',
metadata JSON NULL,
recorded_at TIMESTAMP NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (device_id) REFERENCES devices(id) ON DELETE CASCADE,
INDEX idx_device_recorded (device_id, recorded_at),
INDEX idx_user_card (user_id_card),
INDEX idx_access_method (access_method, is_authorized),
INDEX idx_recorded_at (recorded_at)
);
-- =====================================================
-- 8. LORA MESSAGES TABLE (Communication Log)
-- =====================================================
CREATE TABLE lora_messages (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
node_id VARCHAR(50) NOT NULL COMMENT 'Source node ID',
gateway_id VARCHAR(50) NULL COMMENT 'Gateway yang menerima',
message_type ENUM('sensor_data', 'heartbeat', 'command', 'ack', 'error') NOT NULL,
raw_payload TEXT NOT NULL COMMENT 'Raw LoRa message',
parsed_data JSON NULL COMMENT 'Parsed message data',
rssi INT NULL COMMENT 'Signal strength',
snr DECIMAL(5,2) NULL COMMENT 'Signal to noise ratio',
frequency DECIMAL(10,2) NULL COMMENT 'Frequency in Hz',
is_processed BOOLEAN DEFAULT FALSE,
processing_error TEXT NULL,
received_at TIMESTAMP NOT NULL,
processed_at TIMESTAMP NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_node_received (node_id, received_at),
INDEX idx_message_type (message_type, is_processed),
INDEX idx_received_at (received_at)
);
-- =====================================================
-- 9. SENSOR READINGS TABLE (General Sensor Data)
-- =====================================================
CREATE TABLE sensor_readings (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
sensor_id BIGINT UNSIGNED NOT NULL,
device_id BIGINT UNSIGNED NOT NULL,
value DECIMAL(15,4) NOT NULL COMMENT 'Processed sensor value',
raw_value VARCHAR(255) NULL COMMENT 'Raw sensor reading',
status ENUM('normal', 'warning', 'critical', 'error') DEFAULT 'normal',
battery_level DECIMAL(5,2) NULL COMMENT 'Device battery level',
signal_strength INT NULL COMMENT 'Signal strength in dBm',
metadata JSON NULL,
reading_time TIMESTAMP NOT NULL,
is_processed BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (sensor_id) REFERENCES sensors(id) ON DELETE CASCADE,
FOREIGN KEY (device_id) REFERENCES devices(id) ON DELETE CASCADE,
INDEX idx_sensor_reading_time (sensor_id, reading_time),
INDEX idx_device_reading_time (device_id, reading_time),
INDEX idx_status (status),
INDEX idx_reading_time (reading_time)
);
-- =====================================================
-- 10. ALERTS TABLE (System Alerts & Notifications)
-- =====================================================
CREATE TABLE alerts (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
device_id BIGINT UNSIGNED NULL,
alert_type ENUM('motion', 'door_access', 'vibration', 'system', 'security') NOT NULL,
severity ENUM('low', 'medium', 'high', 'critical') DEFAULT 'medium',
title VARCHAR(255) NOT NULL,
message TEXT NOT NULL,
source_table VARCHAR(50) NULL COMMENT 'Table yang memicu alert',
source_id BIGINT UNSIGNED NULL COMMENT 'ID record yang memicu alert',
is_acknowledged BOOLEAN DEFAULT FALSE,
acknowledged_by BIGINT UNSIGNED NULL,
acknowledged_at TIMESTAMP NULL,
metadata JSON NULL,
triggered_at TIMESTAMP NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (device_id) REFERENCES devices(id) ON DELETE SET NULL,
FOREIGN KEY (acknowledged_by) REFERENCES users(id) ON DELETE SET NULL,
INDEX idx_device_triggered (device_id, triggered_at),
INDEX idx_type_severity (alert_type, severity),
INDEX idx_acknowledged (is_acknowledged, acknowledged_at),
INDEX idx_triggered_at (triggered_at)
);
-- =====================================================
-- 11. ACTIVITY LOGS TABLE (System Activity)
-- =====================================================
CREATE TABLE activity_logs (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT UNSIGNED NULL,
device_id BIGINT UNSIGNED NULL,
action VARCHAR(100) NOT NULL COMMENT 'Action performed',
description TEXT NULL,
ip_address VARCHAR(45) NULL,
user_agent TEXT NULL,
metadata JSON NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL,
FOREIGN KEY (device_id) REFERENCES devices(id) ON DELETE SET NULL,
INDEX idx_user_created (user_id, created_at),
INDEX idx_device_created (device_id, created_at),
INDEX idx_action (action),
INDEX idx_created_at (created_at)
);
-- =====================================================
-- 12. SYSTEM SETTINGS TABLE
-- =====================================================
CREATE TABLE system_settings (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
setting_key VARCHAR(100) UNIQUE NOT NULL,
setting_value TEXT NULL,
setting_type ENUM('string', 'integer', 'float', 'boolean', 'json') DEFAULT 'string',
description TEXT NULL,
is_public BOOLEAN DEFAULT FALSE COMMENT 'Can be accessed by non-admin users',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_key_public (setting_key, is_public)
);
-- =====
================================================
-- SAMPLE DATA INSERTION
-- =====================================================
-- Insert default admin user
INSERT INTO users (name, email, password, role, is_active) VALUES
('System Admin', 'admin@smartrack.com', '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', 'admin', 1),
('Operator', 'operator@smartrack.com', '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', 'operator', 1);
-- Insert sample devices
INSERT INTO devices (device_id, name, type, location, battery_level, is_online) VALUES
('LORA_001', 'Smart Rack Node 1', 'lora_node', 'Server Room A - Rack 1', 85.50, 1),
('LORA_002', 'Smart Rack Node 2', 'lora_node', 'Server Room A - Rack 2', 92.30, 1),
('GATEWAY_001', 'LoRa Gateway Main', 'gateway', 'Server Room A - Central', 100.00, 1);
-- Insert sensors for each device
INSERT INTO sensors (device_id, sensor_type, sensor_name, pin_number, unit, threshold, is_active) VALUES
-- Device LORA_001 sensors
(1, 'pir', 'PIR Motion Sensor', 33, 'boolean', 1, 1),
(1, 'reed_switch', 'Door Reed Switch', 32, 'boolean', 1, 1),
(1, 'vibration', 'Vibration Sensor SW-420', 34, 'g-force', 2.0, 1),
-- Device LORA_002 sensors
(2, 'pir', 'PIR Motion Sensor', 33, 'boolean', 1, 1),
(2, 'reed_switch', 'Door Reed Switch', 32, 'boolean', 1, 1),
(2, 'vibration', 'Vibration Sensor SW-420', 34, 'g-force', 2.5, 1);
-- Insert system settings
INSERT INTO system_settings (setting_key, setting_value, setting_type, description, is_public) VALUES
('system_name', 'Smart Rack Security System', 'string', 'System display name', 1),
('alert_email', 'alerts@smartrack.com', 'string', 'Email for system alerts', 0),
('work_hours_start', '08:00', 'string', 'Work hours start time', 1),
('work_hours_end', '17:00', 'string', 'Work hours end time', 1),
('vibration_threshold_default', '2.0', 'float', 'Default vibration threshold', 1),
('heartbeat_interval', '300', 'integer', 'Heartbeat interval in seconds', 1),
('lora_frequency', '433000000', 'integer', 'LoRa frequency in Hz', 0),
('max_door_open_duration', '600', 'integer', 'Max door open duration in seconds', 1);
-- =====================================================
-- TRIGGERS FOR AUTOMATIC CALCULATIONS
-- =====================================================
-- Trigger untuk update magnitude pada vibration_readings
DELIMITER $$
CREATE TRIGGER tr_vibration_magnitude_update
BEFORE UPDATE ON vibration_readings
FOR EACH ROW
BEGIN
-- Magnitude sudah dihitung otomatis dengan GENERATED ALWAYS AS
-- Update status berdasarkan magnitude vs threshold
IF (SQRT(POW(NEW.x_axis,2) + POW(NEW.y_axis,2) + POW(NEW.z_axis,2))) > NEW.threshold THEN
SET NEW.is_abnormal = TRUE;
IF (SQRT(POW(NEW.x_axis,2) + POW(NEW.y_axis,2) + POW(NEW.z_axis,2))) > (NEW.threshold * 1.5) THEN
SET NEW.status = 'critical';
ELSE
SET NEW.status = 'warning';
END IF;
ELSE
SET NEW.is_abnormal = FALSE;
SET NEW.status = 'normal';
END IF;
END$$
-- Trigger untuk insert vibration_readings
CREATE TRIGGER tr_vibration_magnitude_insert
BEFORE INSERT ON vibration_readings
FOR EACH ROW
BEGIN
-- Update status berdasarkan magnitude vs threshold
IF (SQRT(POW(NEW.x_axis,2) + POW(NEW.y_axis,2) + POW(NEW.z_axis,2))) > NEW.threshold THEN
SET NEW.is_abnormal = TRUE;
IF (SQRT(POW(NEW.x_axis,2) + POW(NEW.y_axis,2) + POW(NEW.z_axis,2))) > (NEW.threshold * 1.5) THEN
SET NEW.status = 'critical';
ELSE
SET NEW.status = 'warning';
END IF;
ELSE
SET NEW.is_abnormal = FALSE;
SET NEW.status = 'normal';
END IF;
END$$
-- Trigger untuk auto-create alerts pada data abnormal
CREATE TRIGGER tr_create_vibration_alert
AFTER INSERT ON vibration_readings
FOR EACH ROW
BEGIN
IF NEW.is_abnormal = TRUE THEN
INSERT INTO alerts (
device_id,
alert_type,
severity,
title,
message,
source_table,
source_id,
triggered_at
) VALUES (
NEW.device_id,
'vibration',
CASE
WHEN NEW.status = 'critical' THEN 'critical'
WHEN NEW.status = 'warning' THEN 'high'
ELSE 'medium'
END,
CONCAT('Abnormal Vibration Detected - ', NEW.status),
CONCAT('Vibration magnitude ', NEW.magnitude, ' exceeds threshold ', NEW.threshold, ' on device ', (SELECT device_id FROM devices WHERE id = NEW.device_id)),
'vibration_readings',
NEW.id,
NEW.recorded_at
);
END IF;
END$$
-- Trigger untuk PIR suspicious motion alerts
CREATE TRIGGER tr_create_pir_alert
AFTER INSERT ON pir_readings
FOR EACH ROW
BEGIN
IF NEW.is_suspicious = TRUE OR NEW.is_authorized_time = FALSE THEN
INSERT INTO alerts (
device_id,
alert_type,
severity,
title,
message,
source_table,
source_id,
triggered_at
) VALUES (
NEW.device_id,
'motion',
CASE
WHEN NEW.is_suspicious = TRUE THEN 'high'
WHEN NEW.is_authorized_time = FALSE THEN 'medium'
ELSE 'low'
END,
CONCAT('Motion Alert - ', NEW.motion_type),
CONCAT('Motion detected with intensity ', NEW.motion_intensity, ' for ', NEW.duration_seconds, ' seconds in zone ', COALESCE(NEW.detection_zone, 'unknown')),
'pir_readings',
NEW.id,
NEW.recorded_at
);
END IF;
END$$
-- Trigger untuk door access alerts
CREATE TRIGGER tr_create_door_alert
AFTER INSERT ON door_readings
FOR EACH ROW
BEGIN
IF NEW.is_forced_entry = TRUE OR NEW.is_authorized_access = FALSE THEN
INSERT INTO alerts (
device_id,
alert_type,
severity,
title,
message,
source_table,
source_id,
triggered_at
) VALUES (
NEW.device_id,
'door_access',
CASE
WHEN NEW.is_forced_entry = TRUE THEN 'critical'
WHEN NEW.is_authorized_access = FALSE THEN 'high'
ELSE 'medium'
END,
CONCAT('Door Access Alert - ', NEW.access_type),
CONCAT('Door ', NEW.door_location, ' accessed via ', NEW.access_type, ' for ', NEW.open_duration_seconds, ' seconds'),
'door_readings',
NEW.id,
NEW.recorded_at
);
END IF;
END$$
DELIMITER ;
-- =====================================================
-- VIEWS FOR EASY DATA ACCESS
-- =====================================================
-- View untuk dashboard summary
CREATE VIEW dashboard_summary AS
SELECT
(SELECT COUNT(*) FROM devices WHERE is_online = TRUE) as online_devices,
(SELECT COUNT(*) FROM devices WHERE is_online = FALSE) as offline_devices,
(SELECT COUNT(*) FROM alerts WHERE is_acknowledged = FALSE AND triggered_at >= DATE_SUB(NOW(), INTERVAL 24 HOUR)) as unacknowledged_alerts_24h,
(SELECT COUNT(*) FROM pir_readings WHERE motion_detected = TRUE AND recorded_at >= DATE_SUB(NOW(), INTERVAL 24 HOUR)) as motions_24h,
(SELECT COUNT(*) FROM door_readings WHERE door_open = TRUE AND recorded_at >= DATE_SUB(NOW(), INTERVAL 24 HOUR)) as door_accesses_24h,
(SELECT COUNT(*) FROM vibration_readings WHERE is_abnormal = TRUE AND recorded_at >= DATE_SUB(NOW(), INTERVAL 24 HOUR)) as abnormal_vibrations_24h;
-- View untuk device status
CREATE VIEW device_status AS
SELECT
d.id,
d.device_id,
d.name,
d.type,
d.location,
d.battery_level,
d.signal_strength,
d.is_online,
d.last_seen_at,
COUNT(s.id) as sensor_count,
COUNT(CASE WHEN s.is_active = TRUE THEN 1 END) as active_sensors,
(SELECT COUNT(*) FROM alerts WHERE device_id = d.id AND is_acknowledged = FALSE) as unacknowledged_alerts
FROM devices d
LEFT JOIN sensors s ON d.id = s.device_id
GROUP BY d.id;
-- View untuk recent activities
CREATE VIEW recent_activities AS
SELECT
'motion' as activity_type,
d.device_id,
d.name as device_name,
d.location,
CONCAT('Motion detected - Intensity: ', p.motion_intensity, ', Duration: ', p.duration_seconds, 's') as description,
p.recorded_at as activity_time
FROM pir_readings p
JOIN devices d ON p.device_id = d.id
WHERE p.motion_detected = TRUE AND p.recorded_at >= DATE_SUB(NOW(), INTERVAL 24 HOUR)
UNION ALL
SELECT
'door_access' as activity_type,
d.device_id,
d.name as device_name,
d.location,
CONCAT('Door ', dr.door_location, ' - ', dr.access_type, ' access for ', dr.open_duration_seconds, 's') as description,
dr.recorded_at as activity_time
FROM door_readings dr
JOIN devices d ON dr.device_id = d.id
WHERE dr.door_open = TRUE AND dr.recorded_at >= DATE_SUB(NOW(), INTERVAL 24 HOUR)
UNION ALL
SELECT
'vibration' as activity_type,
d.device_id,
d.name as device_name,
d.location,
CONCAT('Vibration - Magnitude: ', ROUND(v.magnitude, 2), ' (', v.status, ')') as description,
v.recorded_at as activity_time
FROM vibration_readings v
JOIN devices d ON v.device_id = d.id
WHERE v.is_abnormal = TRUE AND v.recorded_at >= DATE_SUB(NOW(), INTERVAL 24 HOUR)
ORDER BY activity_time DESC
LIMIT 50;
-- =====================================================
-- STORED PROCEDURES
-- =====================================================
-- Procedure untuk cleanup old data
DELIMITER $$
CREATE PROCEDURE CleanupOldData(IN days_to_keep INT)
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
RESIGNAL;
END;
START TRANSACTION;
-- Delete old sensor readings (keep last X days)
DELETE FROM sensor_readings WHERE reading_time < DATE_SUB(NOW(), INTERVAL days_to_keep DAY);
DELETE FROM pir_readings WHERE recorded_at < DATE_SUB(NOW(), INTERVAL days_to_keep DAY);
DELETE FROM door_readings WHERE recorded_at < DATE_SUB(NOW(), INTERVAL days_to_keep DAY);
DELETE FROM vibration_readings WHERE recorded_at < DATE_SUB(NOW(), INTERVAL days_to_keep DAY);
DELETE FROM door_access_readings WHERE recorded_at < DATE_SUB(NOW(), INTERVAL days_to_keep DAY);
-- Delete old LoRa messages (keep last 30 days max)
DELETE FROM lora_messages WHERE received_at < DATE_SUB(NOW(), INTERVAL LEAST(days_to_keep, 30) DAY);
-- Delete old acknowledged alerts (keep last 90 days max)
DELETE FROM alerts WHERE is_acknowledged = TRUE AND acknowledged_at < DATE_SUB(NOW(), INTERVAL LEAST(days_to_keep, 90) DAY);
-- Delete old activity logs (keep last 180 days max)
DELETE FROM activity_logs WHERE created_at < DATE_SUB(NOW(), INTERVAL LEAST(days_to_keep, 180) DAY);
COMMIT;
SELECT CONCAT('Cleanup completed. Removed data older than ', days_to_keep, ' days.') as result;
END$$
-- Procedure untuk device health check
CREATE PROCEDURE DeviceHealthCheck()
BEGIN
SELECT
d.device_id,
d.name,
d.battery_level,
d.signal_strength,
d.is_online,
d.last_seen_at,
CASE
WHEN d.last_seen_at < DATE_SUB(NOW(), INTERVAL 10 MINUTE) THEN 'OFFLINE'
WHEN d.battery_level < 20 THEN 'LOW_BATTERY'
WHEN d.signal_strength < -100 THEN 'WEAK_SIGNAL'
ELSE 'HEALTHY'
END as health_status,
TIMESTAMPDIFF(MINUTE, d.last_seen_at, NOW()) as minutes_since_last_seen
FROM devices d
ORDER BY
CASE
WHEN d.last_seen_at < DATE_SUB(NOW(), INTERVAL 10 MINUTE) THEN 1
WHEN d.battery_level < 20 THEN 2
WHEN d.signal_strength < -100 THEN 3
ELSE 4
END,
d.device_id;
END$$
DELIMITER ;
-- =====================================================
-- INDEXES FOR PERFORMANCE OPTIMIZATION
-- =====================================================
-- Additional composite indexes for common queries
CREATE INDEX idx_pir_device_time_motion ON pir_readings(device_id, recorded_at, motion_detected);
CREATE INDEX idx_door_device_time_open ON door_readings(device_id, recorded_at, door_open);
CREATE INDEX idx_vibration_device_time_abnormal ON vibration_readings(device_id, recorded_at, is_abnormal);
CREATE INDEX idx_alerts_device_time_ack ON alerts(device_id, triggered_at, is_acknowledged);
-- Indexes for time-based queries
CREATE INDEX idx_pir_recorded_at_desc ON pir_readings(recorded_at DESC);
CREATE INDEX idx_door_recorded_at_desc ON door_readings(recorded_at DESC);
CREATE INDEX idx_vibration_recorded_at_desc ON vibration_readings(recorded_at DESC);
CREATE INDEX idx_alerts_triggered_at_desc ON alerts(triggered_at DESC);
-- =====================================================
-- COMPLETION MESSAGE
-- =====================================================
SELECT '✅ Smart Rack Security Database Schema Created Successfully!' as status,
'Database: smart_rack_security' as database_name,
'12 Tables Created' as tables_count,
'3 Views Created' as views_count,
'2 Stored Procedures Created' as procedures_count,
'6 Triggers Created' as triggers_count;