CREATE TABLE IF NOT EXISTS machines ( id SERIAL PRIMARY KEY, code VARCHAR(50) NOT NULL UNIQUE, name VARCHAR(120) NOT NULL, brand VARCHAR(120), model VARCHAR(120), status VARCHAR(50) NOT NULL DEFAULT 'hors_planning', is_active BOOLEAN NOT NULL DEFAULT TRUE, powered_on BOOLEAN NOT NULL DEFAULT TRUE, cycle_signal_edge VARCHAR(20) NOT NULL DEFAULT 'rising', debounce_ms INTEGER NOT NULL DEFAULT 300, min_cycle_time_sec INTEGER NOT NULL DEFAULT 5, max_cycle_time_sec INTEGER NOT NULL DEFAULT 300, stop_detection_delay_sec INTEGER NOT NULL DEFAULT 180, last_cycle_at TIMESTAMPTZ, status_since TIMESTAMPTZ, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE TABLE IF NOT EXISTS production_orders ( id SERIAL PRIMARY KEY, order_number VARCHAR(80) NOT NULL UNIQUE, machine_id INTEGER NOT NULL REFERENCES machines(id), article_ref VARCHAR(80) NOT NULL, article_name VARCHAR(160) NOT NULL, mold_ref VARCHAR(80), material_ref VARCHAR(80), planned_qty INTEGER NOT NULL, cavities INTEGER NOT NULL DEFAULT 1, theoretical_cycle_time_sec DOUBLE PRECISION NOT NULL, status VARCHAR(40) NOT NULL DEFAULT 'planned', started_at TIMESTAMPTZ, ended_at TIMESTAMPTZ, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE TABLE IF NOT EXISTS downtime_reasons ( code VARCHAR(80) PRIMARY KEY, label VARCHAR(160) NOT NULL ); CREATE TABLE IF NOT EXISTS scrap_reasons ( code VARCHAR(80) PRIMARY KEY, label VARCHAR(160) NOT NULL ); CREATE TABLE IF NOT EXISTS operators ( id SERIAL PRIMARY KEY, name VARCHAR(120) NOT NULL UNIQUE, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE TABLE IF NOT EXISTS machine_events ( id SERIAL PRIMARY KEY, machine_id INTEGER NOT NULL REFERENCES machines(id), event_type VARCHAR(80) NOT NULL, timestamp TIMESTAMPTZ NOT NULL, payload JSONB NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE TABLE IF NOT EXISTS cycles ( id SERIAL PRIMARY KEY, machine_id INTEGER NOT NULL REFERENCES machines(id), production_order_id INTEGER REFERENCES production_orders(id), timestamp TIMESTAMPTZ NOT NULL, cycle_time_sec DOUBLE PRECISION NOT NULL, cavities INTEGER NOT NULL DEFAULT 1, produced_qty INTEGER NOT NULL DEFAULT 1, is_valid BOOLEAN NOT NULL DEFAULT TRUE, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE TABLE IF NOT EXISTS downtimes ( id SERIAL PRIMARY KEY, machine_id INTEGER NOT NULL REFERENCES machines(id), production_order_id INTEGER REFERENCES production_orders(id), start_time TIMESTAMPTZ NOT NULL, end_time TIMESTAMPTZ, duration_sec INTEGER, reason_code VARCHAR(80) REFERENCES downtime_reasons(code), comment TEXT, auto_detected BOOLEAN NOT NULL DEFAULT TRUE, qualified_by VARCHAR(120), created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE TABLE IF NOT EXISTS scraps ( id SERIAL PRIMARY KEY, machine_id INTEGER NOT NULL REFERENCES machines(id), production_order_id INTEGER REFERENCES production_orders(id), quantity INTEGER NOT NULL, reason_code VARCHAR(80) NOT NULL REFERENCES scrap_reasons(code), comment TEXT, operator_name VARCHAR(120) NOT NULL, timestamp TIMESTAMPTZ NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_machine_events_machine_time ON machine_events(machine_id, timestamp DESC); CREATE INDEX IF NOT EXISTS idx_cycles_machine_time ON cycles(machine_id, timestamp DESC); CREATE INDEX IF NOT EXISTS idx_downtimes_machine_time ON downtimes(machine_id, start_time DESC); CREATE INDEX IF NOT EXISTS idx_scraps_machine_time ON scraps(machine_id, timestamp DESC);