数据库导入向导
Darra PLC IDE 内置"数据库导入向导", 把一张 SQL 表的结构直接翻译成 PLC 的 DB (数据块定义) + 一套标准读写 FB, 省去手写绑定的重复劳动。
向导生成的是 PLC 的 DB 块定义, 让 PLC 程序可以直接通过 DB_Recipe.Param1 等变量名访问数据库字段。
不是生成业务 CRUD 界面, 也不是生成业务 API 后端。
典型场景: 把上位的配方/工单表结构在 PLC 工程里复刻成 DB 块, PLC 程序用标准读 FB 把数据拉进来。
适用场景
- 上位系统的配方表结构导入 PLC → 生成对应 DB 块 + 读 FB
- 工单只读视图导入 PLC → 生成工单 DB 块, PLC 读取当前任务
- 设备参数表导入 PLC → 用 DB 块承载静态工艺参数
- 生成少量写 FB 用于班次汇总 / 换型审计 这类低频写入
- 老系统迁移: 原本用 Excel 表维护配方, 改为关系型数据库
前置准备
开始之前确认:
| 项 | 要求 |
|---|---|
| 数据库连接 | 已配置到工程, 见 数据库 |
| 目标表 | 存在, 有 PRIMARY KEY |
| 账号权限 | 至少 SELECT 权限, 如需写入需 INSERT/UPDATE/DELETE |
| 网络连通 | IDE 能访问数据库服务器 (诊断: 连接管理器 → 测试连接) |
| 字段类型 | 使用 Darra 支持的类型 (见 字段映射) |
一步式流程总览
步骤 1: 启动向导
打开向导的两种方式:
- 从通讯节点: 项目树 → 通讯 → 已配数据库 → 右键 → 导入为 DB
- 从 Ribbon: Ribbon → PLC → 导入 → 数据库表
截图占位: TODO 向导入口截图
步骤 2: 选择数据源
向导首页列出工程里所有已配置的数据库连接:
| 列 | 说明 |
|---|---|
| 连接名 | 例如 db_recipe, db_workorder |
| 引擎 | SQLite / SQL Server / MySQL / PostgreSQL / Oracle |
| 主机 | 服务器 IP 或主机名 |
| 数据库 | 目标数据库/Schema |
| 状态 | 在线 / 离线 (连不上的禁用) |
选择目标连接, 点"下一步"。
如果还没配置连接, 点"新建连接"走完整配置:
| 字段 | 说明 |
|---|---|
| 连接名 | 工程内唯一 |
| 引擎 | 下拉选 |
| 服务器 | IP:端口 |
| 数据库 | 名称 |
| 用户 / 密码 | 或 Windows 集成认证 |
| SSL | 如需加密 |
点"测试连接", 绿色打勾后保存。
截图占位: TODO 连接选择页面截图
步骤 3: 选择表
向导查询 INFORMATION_SCHEMA 或类似系统视图列出所有表:
| 列 | 说明 |
|---|---|
| 表名 | schema.table 全限定 |
| 行数估计 | 近似值 |
| 列数 | 字段数 |
| 有主键 | 是 / 否 (无主键的需手工选择键列) |
| 说明 | 表注释 (如果数据库里配了) |
树形或列表视图, 支持:
- 搜索过滤
- 按 schema 分组
- 收藏常用表
勾选一张或多张表 (支持多选批量导入), 点"下一步"。
截图占位: TODO 表选择页面截图
步骤 4: 字段映射
这一步决定每个 SQL 字段如何对应到 PLC 变量。左侧是数据库列, 右侧是对应的 PLC 定义:
| SQL 字段 | SQL 类型 | PLC 名称 | PLC 类型 | 是否主键 | 读写权限 | 注释 |
|---|---|---|---|---|---|---|
| id | INT | RecipeId | DINT | 是 | 只读 | 主键 |
| code | VARCHAR(32) | RecipeCode | STRING[32] | 否 | 读写 | 配方编码 |
| speed | DECIMAL(6,2) | Speed | REAL | 否 | 读写 | 运行速度 |
| pressure | FLOAT | Pressure | REAL | 否 | 读写 | 气压 |
| active | BIT | IsActive | BOOL | 否 | 读写 | 启用 |
| updated_at | DATETIME | UpdatedAt | DT | 否 | 只读 | 更新时间 |
默认规则 (见 字段映射):
- 整数: TINYINT → USINT, SMALLINT → INT, INT → DINT, BIGINT → LINT
- 浮点: FLOAT/REAL → REAL, DOUBLE → LREAL, DECIMAL(p,s) → REAL (精度可能丢失)
- 字符串: VARCHAR(N) → STRING[N]
- 时间: DATETIME → DT, DATE → DATE, TIME → TIME
- 布尔: BIT / BOOLEAN → BOOL
向导自动填写, 用户可改。右侧显示每行的"警告":
- 精度丢失 (DECIMAL 转 REAL)
- 长度截断 (VARCHAR(1000) → STRING[255])
- 类型不安全 (BLOB → ARRAY OF BYTE, 需要手动处理长度)
截图占位: TODO 字段映射页面截图
自定义名称
默认 PLC 名称 = SQL 字段名转 PascalCase。可改为:
- 业务名 (例如
runSpeed→Speed_mpm) - 加前缀 (例如
Recipe_Speed) - 批量: 点 "批量改名", 用规则
{FieldName}_{Unit}
排除字段
不需要的字段点行首的 "排除" 按钮, 变灰后不出现在最终 DB 里。典型排除:
created_by/modified_by(操作员字段)- BLOB / LONG TEXT 字段
- 纯内部字段 (如
version,deleted_flag)
步骤 5: 生成选项
最后一步确认生成什么:
DB 块
每张表生成一个 DB, 名称 = DB_<TableName>, 包含表结构对应的所有字段:
DATA_BLOCK DB_Recipes
VAR
RecipeId : DINT;
RecipeCode : STRING[32];
Speed : REAL;
Pressure : REAL;
IsActive : BOOL;
UpdatedAt : DT;
(* 操作状态 *)
LastError : STRING[128];
LastRead : DT;
LastWrite : DT;
END_VAR
END_DATA_BLOCK
读取 FB
自动生成 FB_ReadRecipes:
FUNCTION_BLOCK FB_ReadRecipes
VAR_INPUT
Execute : BOOL; (* 上升沿触发 *)
FilterId : DINT := 0; (* 0 = 全部, >0 = 按 ID 查 *)
FilterCode : STRING[32] := ''; (* 非空 = 按 code 查 *)
END_VAR
VAR_OUTPUT
Done : BOOL;
Busy : BOOL;
Error : BOOL;
ErrorMessage : STRING[128];
RowCount : INT;
END_VAR
VAR
Select : DB_SELECT;
State : INT;
StartEdge : R_TRIG;
END_VAR
StartEdge(CLK := Execute);
CASE State OF
0: (* 空闲 *)
IF StartEdge.Q THEN
Busy := TRUE;
Error := FALSE;
Done := FALSE;
State := 10;
END_IF;
10: (* 执行查询 *)
Select(
ConnectionName := 'db_recipe',
SQL := 'SELECT id, code, speed, pressure, active, updated_at ' +
'FROM recipes WHERE (:id = 0 OR id = :id) ' +
'AND (:code = '''' OR code = :code)',
Execute := TRUE
);
Select.BindInt('id', FilterId);
Select.BindString('code', FilterCode);
State := 20;
20: (* 等待完成 *)
Select(Execute := FALSE);
IF Select.Done THEN
IF Select.RowCount > 0 THEN
DB_Recipes.RecipeId := Select.Row[0].Cell[0].AsDInt;
DB_Recipes.RecipeCode := Select.Row[0].Cell[1].AsString;
DB_Recipes.Speed := Select.Row[0].Cell[2].AsReal;
DB_Recipes.Pressure := Select.Row[0].Cell[3].AsReal;
DB_Recipes.IsActive := Select.Row[0].Cell[4].AsBool;
DB_Recipes.UpdatedAt := Select.Row[0].Cell[5].AsDT;
END_IF;
RowCount := Select.RowCount;
DB_Recipes.LastRead := NOW();
Done := TRUE;
Busy := FALSE;
State := 0;
ELSIF Select.Error THEN
Error := TRUE;
ErrorMessage := Select.ErrorMessage;
DB_Recipes.LastError := Select.ErrorMessage;
Busy := FALSE;
State := 0;
END_IF;
END_CASE;
END_FUNCTION_BLOCK
写入 FB
FB_WriteRecipes (如表在映射里设为读写):
FUNCTION_BLOCK FB_WriteRecipes
VAR_INPUT
Execute : BOOL;
WriteMode : INT := 0; (* 0=INSERT, 1=UPDATE, 2=UPSERT *)
END_VAR
VAR_OUTPUT
Done : BOOL;
Error : BOOL;
AffectedRows : INT;
NewInsertId : DINT;
END_VAR
VAR
Insert : DB_INSERT;
Update : DB_UPDATE;
Upsert : DB_EXECUTE;
(* ... *)
END_VAR
(* 略 *)
END_FUNCTION_BLOCK
删除 FB
如勾选"生成删除", 生成 FB_DeleteRecipes, 支持按主键或条件删除。
列表 FB
生成 FB_ListRecipes 返回 N 行数据, 用于配方列表显示:
FUNCTION_BLOCK FB_ListRecipes
VAR_INPUT
Execute : BOOL;
MaxRows : INT := 100;
WhereClause : STRING[256] := '';
END_VAR
VAR_OUTPUT
Done : BOOL;
Count : INT;
Rows : ARRAY[0..99] OF TYPE_Recipes; (* UDT, 字段结构 *)
END_VAR
(* ... *)
END_FUNCTION_BLOCK
步骤 6: 预览与确认
向导生成代码预览, 显示:
- DB 结构 (新增 / 修改 字段对比)
- 所有 FB 的源码 (只读预览)
- 变量表新增项
- SQL 语句
逐项检查无误后点 "生成"。
截图占位: TODO 预览页面截图
步骤 7: 生成完成
向导关闭, 项目树自动更新:
MyProject/
├─ 程序块/
│ ├─ DB_Recipes (新增)
│ └─ 函数块/
│ ├─ FB_ReadRecipes (新增)
│ ├─ FB_WriteRecipes (新增)
│ ├─ FB_ListRecipes (新增)
│ └─ FB_DeleteRecipes (新增)
├─ 数据类型/
│ └─ TYPE_Recipes (新增 UDT)
└─ 变量表/
└─ 全局变量 (更新)
立即可编译下载。
使用生成的代码
读取当前激活配方
VAR
ReadRecipe : FB_ReadRecipes;
Trig : BOOL;
END_VAR
ReadRecipe(
Execute := Trig,
FilterCode := 'RECIPE_A'
);
IF ReadRecipe.Done THEN
(* 读取完成, DB_Recipes 已填充 *)
(* 用 DB_Recipes.Speed 等字段 *)
END_IF;
写入生产记录
VAR
WriteLog : FB_WriteProduction;
END_VAR
WriteLog(
Execute := PartCompleted,
WriteMode := 0 (* INSERT *)
);
IF WriteLog.Done THEN
LastInsertedId := WriteLog.NewInsertId;
END_IF;
再次导入 (结构变更)
数据库表结构变了怎么办?
- 再次运行向导, 选同一张表
- 向导检测到已存在的 DB, 显示"结构对比"页面:
- 新增字段 (绿色)
- 删除字段 (红色删除线)
- 类型变化 (橙色)
- 重命名 (紫色)
- 选择处理策略:
- 智能合并 (推荐): 保留人工改过的 FB 代码, 只更新结构相关部分
- 完全覆盖: 重新生成所有文件 (人工改动丢失)
- 导出差异: 输出 diff 让人工合并
批量导入
一次导入多张表:
- 步骤 3 时勾选多张
- 字段映射页按 Tab 切换不同表
- 生成时一次性产出多组文件
适合批量导入产线所用的多张配方/工单/BOM 表结构。
导入模板
常改的配置 (连接 + 表集合 + 命名规则) 可以保存为模板:
向导底部 → 保存为模板
→ 起名: "产线 A 配方与工单"
→ 下次打开向导时选"从模板", 一键进入预览
诊断与排错
| 症状 | 原因 | 处理 |
|---|---|---|
| 表列表为空 | 账号无 INFORMATION_SCHEMA 权限 | 赋予 SELECT 权限或联系 DBA |
| 字段映射有警告 "精度丢失" | DECIMAL(18,10) → REAL (7位有效) | 改用 LREAL, 或在 SQL 端用 CAST |
| 生成后编译报错 "DB 已存在" | 同名 DB 冲突 | 覆盖或改名 |
| FB_Read 超时 | SQL 复杂或表大 | 加索引, 或加 LIMIT |
| 中文字段名被截断 | SQL 用 UTF-8, PLC 字符串默认 | 配置连接为 CharSet=utf8mb4 |
| 主键为复合键 | 向导只支持单主键 | 手工修改生成的 FB 的 WHERE |
最佳实践
- 先在数据库端规范化: 设好主键、外键、NOT NULL, 向导才能生成好代码
- 字段名英文: 中文字段名在多环境兼容性差, 英文更稳
- 有版本列: 每张配方/工单表加
version INT或updated_at, 方便乐观锁 - 分读写角色: 读 FB 用只读账号, 写 FB 用读写账号, 部署时 IDE 侧单独配连接
- 避免大字段: TEXT / BLOB 字段不要导入 PLC, 用另一张元数据表索引
- 批量操作: 批量 FB 用于班次结束的汇总写入 (一次写 N 行), 不是扫描周期内反复调用; 高频变量归档请走 异步数据管道