652 lines
27 KiB
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; |