Files
bj_power/bj_power_mes/doc/engine_schema.sql
T

282 lines
17 KiB
SQL
Raw Normal View History

-- ============================================================================
-- 新架构全配置化流程引擎 - 数据库表结构
-- 版本: 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;