Files
bj_power/bj_power_mes/doc/engine_schema.sql
T

282 lines
17 KiB
SQL
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
-- ============================================================================
-- 新架构全配置化流程引擎 - 数据库表结构
-- 版本: v2.0
-- 说明: 新表与旧表共存,不修改现有表结构
-- 引擎通过 recipe_step 子表配置驱动,无硬编码 step_type
-- ============================================================================
-- ========== 1. recipe / recipe_step 表 ==========
-- 已由 ent schema 自动创建,无需重复建表
-- recipe 表: id, name, code, version, desc, isActive
-- recipe_step.priority(调度优先级,数值越小越优先,默认 2)由 ent schema 生成。
-- 存量数据库升级时执行(幂等):
-- ALTER TABLE recipe_step ADD COLUMN IF NOT EXISTS priority INT NOT NULL DEFAULT 2;
-- ALTER TABLE recipe_step ADD COLUMN IF NOT EXISTS hold_until_handover BOOLEAN NOT NULL DEFAULT FALSE;
-- ALTER TABLE recipe_step ADD COLUMN IF NOT EXISTS fallback_next_step VARCHAR(20) NOT NULL DEFAULT '';
-- fallback_next_step 双语义(两种场景互斥,不冲突):
-- ① holding 步骤(如 OP20_WAIT 磨床等待加工)=挂起时无后继工件交接的收尾取料步骤;
-- ② 取料步骤(如 OP50_UNLOAD 缓存取料)=被代做跳过(handover_proxy)时直接推进到的目标步骤。
-- ========== 2. recipe_step_resource(资源需求,引擎 ACQUIRE 阶段读取) ==========
CREATE TABLE IF NOT EXISTS recipe_step_resource (
id SERIAL PRIMARY KEY,
recipe_step_id INT NOT NULL REFERENCES recipe_step(id),
resource_type VARCHAR(30) NOT NULL,
-- 'robot' 机器人(互斥)
-- 'temp_slot' 缓存槽(中间工件暂存)
-- 'equipment_slot' 设备槽位(磨床/全检台/缓存台等)
action VARCHAR(10) NOT NULL,
-- 'acquire' 独占获取(SELECT FOR UPDATE),如获取机器人、空槽位
-- 'check' 非独占检查(SELECT FOR UPDATE 轻量锁),如检查至少 1 个磨床空槽位
equipment_type VARCHAR(50) DEFAULT '',
-- 'GRINDER' | 'DOCK' | 'FULL_CHECK' | 'CACHE' | 'TURN_TABLE' | 'AIR_BLOW'
resource_key VARCHAR(100) DEFAULT '',
-- 指定槽位资源键:'{dock_slot}' 由引擎解析为 job 绑定的接驳台槽位(equipmentId:slotNo
-- 空 = 任意符合 expected_status 的槽位;仅 acquire 时生效,check 忽略
expected_status VARCHAR(20) NOT NULL,
-- 'empty' | 'occupied' | 'done' | 'idle' | 'free'
min_count INT DEFAULT 1, -- check 时最少数量
acquire_mode VARCHAR(20) NOT NULL DEFAULT 'self',
-- 'self' 仅本 job 自持槽位(current_job_id IS NULL OR = 本 job)——常规取放料
-- 'any' 可跨 job 抢占任意槽位(换料:取前任工件磨好的 done 槽位,记录前任 job 交接)
description TEXT DEFAULT '',
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
is_del BOOLEAN NOT NULL DEFAULT FALSE
);
CREATE INDEX IF NOT EXISTS idx_recipe_step_resource_step
ON recipe_step_resource(recipe_step_id) WHERE is_del = FALSE;
-- ========== 4. recipe_step_signal(信号序列,引擎 EXECUTE/WAIT 阶段读取) ==========
CREATE TABLE IF NOT EXISTS recipe_step_signal (
id SERIAL PRIMARY KEY,
recipe_step_id INT NOT NULL REFERENCES recipe_step(id),
signal_name VARCHAR(50) NOT NULL, -- 对应 signal 表的 name
signal_role VARCHAR(10) NOT NULL,
-- 'param' 参数信号:在 req 之前写入,如位置号、工件类型
-- 'req' 请求信号:发送给 PLC,引擎发送后立即返回
-- 'done' 完成信号:SignalRouter 异步监控,到达后触发 COMPLETE
param_value_expr VARCHAR(100) DEFAULT '', -- 参数表达式,如 '{product_type}'
sort_order INT DEFAULT 0, -- 发送顺序
wait_after_ms INT DEFAULT 0, -- 发送后等待毫秒数(0=不等待),用于 param 最后一条→req 之间
broadcast BOOLEAN NOT NULL DEFAULT FALSE, -- done 信号广播模式:到达时一次性唤醒全部等待步骤(批次信号,如 M2098 全检台批量检测完成)
description TEXT DEFAULT '',
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
is_del BOOLEAN NOT NULL DEFAULT FALSE
);
CREATE INDEX IF NOT EXISTS idx_recipe_step_signal_step
ON recipe_step_signal(recipe_step_id) WHERE is_del = FALSE;
-- ========== 5. recipe_step_action(后置操作,引擎 COMPLETE 阶段读取) ==========
CREATE TABLE IF NOT EXISTS recipe_step_action (
id SERIAL PRIMARY KEY,
recipe_step_id INT NOT NULL REFERENCES recipe_step(id),
action_type VARCHAR(20) NOT NULL,
-- 'release_resource' 释放资源(robot→idle, temp_slot→free
-- 'set_resource' 设置资源状态(equipment_slot→empty/occupied/done
-- 'send_signal' 发送额外触发信号(COMPLETE 阶段发送)
-- 'jump_step' 跳转到指定步骤(用于 DECISION 步骤的分支跳转)
resource_type VARCHAR(30) DEFAULT '',
resource_key VARCHAR(100) DEFAULT '',
-- '{allocated}' 引擎在 acquire 阶段解析为实际分配的资源标识
-- 'robot' 机器人
-- 'temp_slot' 缓存槽
target_status VARCHAR(20) DEFAULT '',
-- 'idle' | 'free' | 'empty' | 'occupied' | 'done'
signal_name VARCHAR(50) DEFAULT '', -- send_signal 时的信号名
jump_to_step_id VARCHAR(20) DEFAULT '', -- jump_step 时的目标 step_id
sort_order INT DEFAULT 0,
description TEXT DEFAULT '',
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
is_del BOOLEAN NOT NULL DEFAULT FALSE
);
CREATE INDEX IF NOT EXISTS idx_recipe_step_action_step
ON recipe_step_action(recipe_step_id) WHERE is_del = FALSE;
-- ========== 6. recipe_step_decision(决策分支表) ==========
CREATE TABLE IF NOT EXISTS recipe_step_decision (
id SERIAL PRIMARY KEY,
recipe_step_id INT NOT NULL REFERENCES recipe_step(id),
priority INT NOT NULL DEFAULT 0, -- 匹配优先级
condition_equipment_type VARCHAR(50) NOT NULL, -- 'FULL_CHECK' | 'CACHE' | 'GRINDER' | 'JOB'(虚拟:同工单剩余活跃件数)
condition_status VARCHAR(20) NOT NULL, -- 'empty' | 'occupied' | 'done' | 'remaining'JOB 专用)
condition_operator VARCHAR(5) NOT NULL, -- '>=' | '==' | '<=' | '>'
condition_value INT NOT NULL,
target_next_step_id VARCHAR(20) NOT NULL, -- 匹配时的目标 step_id
description TEXT DEFAULT '',
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
is_del BOOLEAN NOT NULL DEFAULT FALSE
);
CREATE INDEX IF NOT EXISTS idx_recipe_step_decision_step
ON recipe_step_decision(recipe_step_id) WHERE is_del = FALSE;
-- ========== 7. resource_lock(资源锁表,引擎运行时状态) ==========
CREATE TABLE IF NOT EXISTS resource_lock (
id SERIAL PRIMARY KEY,
resource_type VARCHAR(30) NOT NULL, -- 'robot' | 'temp_slot'
resource_key VARCHAR(100) NOT NULL, -- 'robot' | 'temp_slot'
status VARCHAR(20) NOT NULL, -- 'idle' | 'busy' | 'free' | 'occupied'
held_by_job_id INT DEFAULT 0, -- 持有锁的 job ID0=空闲)
held_at TIMESTAMP DEFAULT NULL,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
is_del BOOLEAN NOT NULL DEFAULT FALSE
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_resource_lock_key
ON resource_lock(resource_type, resource_key) WHERE is_del = FALSE;
-- ========== 8. equipment_signal(设备信号映射表,逻辑名→物理地址) ==========
CREATE TABLE IF NOT EXISTS equipment_signal (
id SERIAL PRIMARY KEY,
equipment_id INT NOT NULL REFERENCES equipment(id),
logical_name VARCHAR(50) NOT NULL, -- 'PlaceReq' | 'PlaceDone' | 'FetchReq' | 'FetchDone' | 'MachiningDone'
signal_name VARCHAR(50) NOT NULL, -- 对应 signal 表的 name
address VARCHAR(20) NOT NULL, -- PLC 地址
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
is_del BOOLEAN NOT NULL DEFAULT FALSE
);
CREATE INDEX IF NOT EXISTS idx_equipment_signal_eq
ON equipment_signal(equipment_id) WHERE is_del = FALSE;
-- ========== 9. job_stepjob 运行时步骤快照) ==========
CREATE TABLE IF NOT EXISTS job_step (
id SERIAL PRIMARY KEY,
job_id INT NOT NULL,
step_id VARCHAR(20) NOT NULL, -- 对应 recipe_step.step_id
step_name VARCHAR(100) NOT NULL,
step_index INT NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'pending',
-- 'pending' 等待资源获取
-- 'executing' 正在发送信号
-- 'waiting_done' 等待 PLC Done 信号
-- 'completed' 步骤完成
-- 'holding' 完成但挂起等待交接(换料:磨完等下一件来交接)
next_step_id VARCHAR(20) DEFAULT '', -- 快照时的下一步骤 ID
allocated_equipment_id INT DEFAULT 0,
allocated_slot_no INT DEFAULT 0,
attempt_count INT DEFAULT 0,
done_signal_received BOOLEAN NOT NULL DEFAULT FALSE,
hold_until_handover BOOLEAN NOT NULL DEFAULT FALSE, -- 步骤完成后挂起等待交接(复制自 recipe_step
fallback_next_step VARCHAR(20) NOT NULL DEFAULT '', -- 双语义(复制自 recipe_step):①holding 步骤=挂起无后继交接的收尾步骤;②取料步骤=被代做跳过(handover_proxy)后的推进目标
prev_job_id INT NOT NULL DEFAULT 0, -- acquire_mode='any' 取前任工件槽位时记录的前任 job
proxy_job_id INT NOT NULL DEFAULT 0, -- acquire_mode='proxy' 代取其他 job 工件时记录的被代做 jobhandover_proxy 用)
started_at TIMESTAMPTZ DEFAULT NULL, -- 进入 waiting_done 的时间(超时检测用,带时区避免 NOW()-started_at 时区偏差)
timeout_seconds INT DEFAULT -1, -- 超时秒数(-1=一致监视+轮询兜底不超时,0=不超时,>0=超时秒数)
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
is_del BOOLEAN NOT NULL DEFAULT FALSE
);
-- 存量数据库升级(幂等):表已存在时补齐缺失列(首次建表时 CREATE 已含该列,此语句自动跳过)
ALTER TABLE job_step ADD COLUMN IF NOT EXISTS proxy_job_id INT NOT NULL DEFAULT 0;
CREATE INDEX IF NOT EXISTS idx_job_step_job
ON job_step(job_id) WHERE is_del = FALSE;
-- ========== 9a. job_step_resourcejob 运行时资源快照) ==========
CREATE TABLE IF NOT EXISTS job_step_resource (
id SERIAL PRIMARY KEY,
job_step_id INT NOT NULL REFERENCES job_step(id),
resource_type VARCHAR(30) NOT NULL,
action VARCHAR(10) NOT NULL,
equipment_type VARCHAR(50) DEFAULT '',
resource_key VARCHAR(100) DEFAULT '',
expected_status VARCHAR(20) NOT NULL,
min_count INT DEFAULT 1,
acquire_mode VARCHAR(20) NOT NULL DEFAULT 'self',
description TEXT DEFAULT '',
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
is_del BOOLEAN NOT NULL DEFAULT FALSE
);
CREATE INDEX IF NOT EXISTS idx_job_step_resource_step
ON job_step_resource(job_step_id) WHERE is_del = FALSE;
-- ========== 9b. job_step_signaljob 运行时信号快照) ==========
CREATE TABLE IF NOT EXISTS job_step_signal (
id SERIAL PRIMARY KEY,
job_step_id INT NOT NULL REFERENCES job_step(id),
signal_name VARCHAR(50) NOT NULL,
signal_role VARCHAR(10) NOT NULL,
param_value_expr VARCHAR(100) DEFAULT '',
sort_order INT DEFAULT 0,
wait_after_ms INT DEFAULT 0, -- 发送后等待毫秒数
broadcast BOOLEAN NOT NULL DEFAULT FALSE, -- 复制自 recipe_step_signaldone 广播模式)
description TEXT DEFAULT '',
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
is_del BOOLEAN NOT NULL DEFAULT FALSE
);
CREATE INDEX IF NOT EXISTS idx_job_step_signal_step
ON job_step_signal(job_step_id) WHERE is_del = FALSE;
-- ========== 9c. job_step_actionjob 运行时后置操作快照) ==========
CREATE TABLE IF NOT EXISTS job_step_action (
id SERIAL PRIMARY KEY,
job_step_id INT NOT NULL REFERENCES job_step(id),
action_type VARCHAR(20) NOT NULL,
resource_type VARCHAR(30) DEFAULT '',
resource_key VARCHAR(100) DEFAULT '',
target_status VARCHAR(20) DEFAULT '',
signal_name VARCHAR(50) DEFAULT '',
jump_to_step_id VARCHAR(20) DEFAULT '',
sort_order INT DEFAULT 0,
description TEXT DEFAULT '',
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
is_del BOOLEAN NOT NULL DEFAULT FALSE
);
CREATE INDEX IF NOT EXISTS idx_job_step_action_step
ON job_step_action(job_step_id) WHERE is_del = FALSE;
-- ========== 9d. job_step_decisionjob 运行时决策快照) ==========
CREATE TABLE IF NOT EXISTS job_step_decision (
id SERIAL PRIMARY KEY,
job_step_id INT NOT NULL REFERENCES job_step(id),
priority INT NOT NULL DEFAULT 0,
condition_equipment_type VARCHAR(50) NOT NULL,
condition_status VARCHAR(20) NOT NULL,
condition_operator VARCHAR(5) NOT NULL,
condition_value INT NOT NULL,
target_next_step_id VARCHAR(20) NOT NULL,
description TEXT DEFAULT '',
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
is_del BOOLEAN NOT NULL DEFAULT FALSE
);
CREATE INDEX IF NOT EXISTS idx_job_step_decision_step
ON job_step_decision(job_step_id) WHERE is_del = FALSE;
-- ========== 10/11. 引擎运行时种子数据(resource_lock / equipment_signal ==========
-- 已迁移至 doc/new_recipe.sql(末尾"引擎运行时种子数据"节):
-- resource_lock/equipment_signal 的 INSERT 依赖 equipment 表数据存在(equipment_signal 外键),
-- 而 equipment 由 doc/data.sql 在 seed 阶段导入;reset-all 执行顺序为
-- ent 建表 → 本文件(纯结构) → data.sql → new_recipe.sql(含种子数据)INSERT 必须放在 seed 之后。
-- ========== 12. robot_task_priority(机器人任务优先级配置表) ==========
-- 组合优先级:task_kind + from_type + to_type → priority(数值越小越优先),
-- 产线调度策略的唯一配置源,与配方解耦。from_type/to_type 为搬运源/目标设备类型码,
-- NULL 表示通配;查找顺序(最具体优先):精确 (kind,from,to) → (kind,from) → (kind,to) → kind-only → 内置。
-- 组合语义:一条搬运路径(如换向台→磨床链的 UNLOAD+TURN_TABLE=5)是否优先由三要素
-- 共同决定,任何设备组合可单独配置,无需发明新任务类型。
-- 缺省值与代码内置默认一致(EXCHANGE=0 / UNLOAD+TURN_TABLE=5 / LOAD=UNLOAD=WASH=10 /
-- RELOAD=20 / TRANSFER=30),种子 INSERT 见 doc/new_recipe.sql 末尾;line 装配层启动时
-- 亦会幂等自举建表+种子(含存量库 ALTER 补列与旧索引替换)。
CREATE TABLE IF NOT EXISTS robot_task_priority (
id SERIAL PRIMARY KEY,
task_kind VARCHAR(30) NOT NULL, -- EXCHANGE/LOAD/UNLOAD/WASH/RELOAD/TRANSFER
from_type VARCHAR(30), -- 来源设备类型码,NULL=通配
to_type VARCHAR(30), -- 目标设备类型码,NULL=通配
priority INT NOT NULL, -- 数值越小越优先
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
is_del BOOLEAN NOT NULL DEFAULT FALSE
);
ALTER TABLE robot_task_priority ADD COLUMN IF NOT EXISTS from_type VARCHAR(30);
ALTER TABLE robot_task_priority ADD COLUMN IF NOT EXISTS to_type VARCHAR(30);
DROP INDEX IF EXISTS idx_robot_task_priority_kind;
CREATE UNIQUE INDEX IF NOT EXISTS idx_robot_task_priority_combo
ON robot_task_priority(task_kind, COALESCE(from_type, ''), COALESCE(to_type, ''))
WHERE is_del = FALSE;