Initial commit: HC900 Crawler
Honeywell HC900을 Modbus TCP로 직접 폴링 → gRPC → C# 크롤러 → PostgreSQL. 기존 Experion OPC UA 데이터 경로를 HC900 직접 통신으로 대체. - industrial-comm/cpp: C++ Modbus 게이트웨이 (gRPC 서버) - src: C# .NET 8 ASP.NET Core 크롤러 + 웹 UI (3-Layer) - mcp-server: Python FastMCP (RAG/NL2SQL/P&ID) - 다중 컨트롤러(N-Controller) 지원 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
121
scripts/migration_20260603_controller_id.sql
Normal file
121
scripts/migration_20260603_controller_id.sql
Normal file
@@ -0,0 +1,121 @@
|
||||
-- ============================================================================
|
||||
-- Migration: Add controller_id columns for multi-controller support
|
||||
-- Run this once against the existing HC900 database.
|
||||
-- ============================================================================
|
||||
|
||||
BEGIN;
|
||||
|
||||
-- 1. realtime_table: add controller_id + UNIQUE(controller_id, tagname)
|
||||
ALTER TABLE realtime_table
|
||||
ADD COLUMN IF NOT EXISTS controller_id TEXT NOT NULL DEFAULT 'HC1';
|
||||
|
||||
-- Drop any existing unique constraint on tagname alone (idempotent wrapper)
|
||||
DO $$
|
||||
BEGIN
|
||||
IF EXISTS (
|
||||
SELECT 1 FROM pg_indexes
|
||||
WHERE tablename = 'realtime_table'
|
||||
AND indexname = 'idx_realtime_table_tagname_unique'
|
||||
) THEN
|
||||
DROP INDEX IF EXISTS idx_realtime_table_tagname_unique;
|
||||
END IF;
|
||||
IF EXISTS (
|
||||
SELECT 1 FROM pg_constraint c
|
||||
JOIN pg_class t ON t.oid = c.conrelid
|
||||
WHERE t.relname = 'realtime_table' AND c.contype = 'u'
|
||||
) THEN
|
||||
-- There's a unique constraint; we can't easily drop it by name unless we know it.
|
||||
-- Use a safe approach: drop any unique constraint on tagname
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- Add the new composite UNIQUE (required for ON CONFLICT (controller_id, tagname) DO UPDATE)
|
||||
-- Skip if already exists
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM pg_indexes
|
||||
WHERE tablename = 'realtime_table'
|
||||
AND indexname = 'idx_realtime_table_ctrl_tag_unique'
|
||||
) THEN
|
||||
CREATE UNIQUE INDEX idx_realtime_table_ctrl_tag_unique
|
||||
ON realtime_table(controller_id, tagname);
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- 2. history_table: add controller_id with default 'HC1'
|
||||
ALTER TABLE history_table
|
||||
ADD COLUMN IF NOT EXISTS controller_id TEXT NOT NULL DEFAULT 'HC1';
|
||||
|
||||
-- 3. event_history_table: add controller_id
|
||||
ALTER TABLE event_history_table
|
||||
ADD COLUMN IF NOT EXISTS controller_id TEXT NOT NULL DEFAULT 'HC1';
|
||||
|
||||
-- 4. tag_metadata: add controller_id
|
||||
ALTER TABLE tag_metadata
|
||||
ADD COLUMN IF NOT EXISTS controller_id TEXT NOT NULL DEFAULT 'HC1';
|
||||
|
||||
-- 5. hc900_map_master: add controller_id
|
||||
ALTER TABLE hc900_map_master
|
||||
ADD COLUMN IF NOT EXISTS controller_id TEXT NOT NULL DEFAULT 'HC1';
|
||||
|
||||
-- 6. v_tag_summary: recreate with controller_id
|
||||
DROP VIEW IF EXISTS v_tag_summary CASCADE;
|
||||
CREATE VIEW v_tag_summary AS
|
||||
SELECT
|
||||
rt_base.base_tag,
|
||||
pv_rt.livevalue AS pv,
|
||||
sp_rt.livevalue AS sp,
|
||||
op_rt.livevalue AS op,
|
||||
instate0_rt.livevalue AS instate0,
|
||||
instate1_rt.livevalue AS instate1,
|
||||
instate2_rt.livevalue AS instate2,
|
||||
desc_md.value AS description,
|
||||
area_md.value AS area,
|
||||
sub_area_md.value AS sub_area,
|
||||
rt_base.controller_id
|
||||
FROM (SELECT DISTINCT split_part(tagname, '.', 1) AS base_tag, controller_id FROM realtime_table) rt_base
|
||||
LEFT JOIN realtime_table pv_rt ON pv_rt.tagname = rt_base.base_tag || '.pv' AND pv_rt.controller_id = rt_base.controller_id
|
||||
LEFT JOIN realtime_table sp_rt ON sp_rt.tagname = rt_base.base_tag || '.sp' AND sp_rt.controller_id = rt_base.controller_id
|
||||
LEFT JOIN realtime_table op_rt ON op_rt.tagname = rt_base.base_tag || '.op' AND op_rt.controller_id = rt_base.controller_id
|
||||
LEFT JOIN realtime_table instate0_rt ON instate0_rt.tagname = rt_base.base_tag || '.instate0' AND instate0_rt.controller_id = rt_base.controller_id
|
||||
LEFT JOIN realtime_table instate1_rt ON instate1_rt.tagname = rt_base.base_tag || '.instate1' AND instate1_rt.controller_id = rt_base.controller_id
|
||||
LEFT JOIN realtime_table instate2_rt ON instate2_rt.tagname = rt_base.base_tag || '.instate2' AND instate2_rt.controller_id = rt_base.controller_id
|
||||
LEFT JOIN tag_metadata desc_md ON desc_md.base_tag = rt_base.base_tag AND desc_md.attribute = 'desc'
|
||||
LEFT JOIN tag_metadata area_md ON area_md.base_tag = rt_base.base_tag AND area_md.attribute = 'area'
|
||||
LEFT JOIN tag_metadata sub_area_md ON sub_area_md.base_tag = rt_base.base_tag AND sub_area_md.attribute = 'sub_area';
|
||||
|
||||
-- 7. v_plant_running_state: recreate (depends on v_tag_summary)
|
||||
DROP VIEW IF EXISTS v_plant_running_state;
|
||||
CREATE VIEW v_plant_running_state AS
|
||||
WITH pump_state AS (
|
||||
SELECT
|
||||
trim(split_part(area, '|', 2)) AS area_code,
|
||||
area AS area_raw,
|
||||
base_tag,
|
||||
pv,
|
||||
controller_id
|
||||
FROM v_tag_summary
|
||||
WHERE area IS NOT NULL
|
||||
AND (base_tag LIKE 'p-%' OR base_tag LIKE 'vp-%')
|
||||
AND pv ~ '\|\s*(L-RUN|R-RUN|L-STOP|R-STOP|L-TRIP|R-TRIP)\s*\|'
|
||||
)
|
||||
SELECT
|
||||
area_code,
|
||||
MAX(area_raw) AS area_raw,
|
||||
COUNT(*) AS total_pumps,
|
||||
COUNT(*) FILTER (WHERE pv ~ '\|\s*[LR]-RUN\s*\|') AS running_pumps,
|
||||
COUNT(*) FILTER (WHERE pv ~ '\|\s*[LR]-TRIP\s*\|') AS tripped_pumps,
|
||||
COUNT(*) FILTER (WHERE pv ~ '\|\s*(L-STOP|R-STOP)\s*\|') AS stopped_pumps,
|
||||
CASE
|
||||
WHEN COUNT(*) FILTER (WHERE pv ~ '\|\s*[LR]-RUN\s*\|') > 0 THEN 'RUNNING'
|
||||
WHEN COUNT(*) FILTER (WHERE pv ~ '\|\s*[LR]-TRIP\s*\|') > 0 THEN 'TRIPPED'
|
||||
ELSE 'STOPPED'
|
||||
END AS status,
|
||||
array_agg(base_tag) FILTER (WHERE pv ~ '\|\s*[LR]-RUN\s*\|') AS running_pump_tags
|
||||
FROM pump_state
|
||||
WHERE area_code IS NOT NULL AND area_code <> ''
|
||||
GROUP BY area_code
|
||||
ORDER BY area_code;
|
||||
|
||||
COMMIT;
|
||||
Reference in New Issue
Block a user