KingFusion驾驶舱开发实战:转运信息模块(AGV状态+任务列表+完成率+转运次数)

2026-08-07 | 雷工笔记 | MES系列合集


一、背景

驾驶舱是MES系统的"仪表盘",也是工厂管理层最关注的功能模块。之前我分享了产能信息和设备信息模块,今天继续完善转运信息区域。

转运信息模块的核心是AGV小车的运行状态和任务数据展示。现场有5台AGV小车,由AGV调度系统统一管理,通过API接口将AGV状态实时上报给MES系统,MES收到后更新到数据库,驾驶舱再从数据库读取数据展示给用户。

整体数据链路:

复制代码
AGV调度系统 → API上报 → MES接口 → PM_AGVStatusDetail表 → 计算工程 → 驾驶舱前端
                                    PM_AGVTaskDetail表   → (case 8~11) → 图表/变量
复制代码

本文聚焦计算工程后端脚本(SQL查询)和前端客户端页面变量绑定的具体实现,不带任何概念包装,全是代码和踩坑记录。


二、涉及的表结构

2.1 PM_AGVStatusDetail(AGV状态详情)

这个表由AGV调度系统通过API实时更新写入,5台AGV各有一条记录,字段如下:

javascript 复制代码
CREATE TABLE "public"."PM_AGVStatusDetail" (
  "id" int4 NOT NULL,
  "c_AGVCode" int4 NOT NULL,       -- AGV设备编码(1~5)
  "c_AGVStatus" int4 NOT NULL,     -- 状态:0未定义,1停止,2运行,3空闲,4读卡停止,5管制
  "c_AGVBattery" int4,             -- 电池电量
  "c_AGVAlarm" int4,               -- 报警状态
  "c_AGVPosition" varchar(50),     -- 当前位置
  "c_FinchTime" timestamp           -- 更新时间
);
ALTER TABLE "PM_AGVStatusDetail" ADD CONSTRAINT "AGV" UNIQUE ("c_AGVCode");

2.2 PM_AGVTaskDetail(AGV任务详情)

每一条记录代表一次AGV转运任务,字段如下:

javascript 复制代码
CREATE TABLE "public"."PM_AGVTaskDetail" (
  "ID" int4 NOT NULL,
  "c_TaskID" varchar(100),         -- 调度AGV任务编码
  "c_AGVCode" int4,                -- AGV设备编码
  "c_LocationCode" varchar(50),    -- 当前位置
  "c_AGVTaskStatus" varchar(20),   -- 任务状态:创建/执行中/已完成
  "c_CreateTime" timestamp,        -- 任务创建时间
  "c_FinchTime" timestamp,         -- 完成时间
  "c_UpdateTime" timestamp         -- 更新时间
);

三、功能实现的四个步骤

3.1 AGV状态更新(case 8)

需求:从PM_AGVStatusDetail表读取5台AGV的状态码,赋值给5个工程变量 System.状态_AGV01 \~ System.状态_AGV05,图形模型根据变量值显示不同颜色的AGV图标。

后端计算工程脚本(PM_cockpit请求式计算):

javascript 复制代码
case 8://驾驶舱-转运信息-AGV状态
    var sql = `SELECT "c_AGVCode", "c_AGVStatus"
               FROM "PM_AGVStatusDetail"
               ORDER BY "c_AGVCode"`;
    var res=[];
    var ret = await SQLQuery('RFM_PM', sql, res);
    OutParam.records=res;
    OutParam.errorCode=ret;
    break;

前端客户端(驾驶舱页面,变量触发事件):

javascript 复制代码
var M = { 'type': 8 };
 
requestCalculationPost($System.RFM_PM, 'PM_cockpit', M, function (res) {
    if (res.Code === 0 && res.errorCode === 0) {
        var resdata = res.records;
        for (var i = 0; i < resdata.length; i++) {
            var code = resdata[i].c_AGVCode;
            var status = resdata[i].c_AGVStatus;
            if (code === 1) $System.状态_AGV01 = status;
            else if (code === 2) $System.状态_AGV02 = status;
            else if (code === 3) $System.状态_AGV03 = status;
            else if (code === 4) $System.状态_AGV04 = status;
            else if (code === 5) $System.状态_AGV05 = status;
        }
    }
});

状态码 → 颜色映射:

状态码 含义 图标颜色
0 未定义 灰色
1 停止 红色
2 运行 绿色
3 空闲 黄色
4 读卡停止 橙色
5 管制 紫色

3.2 AGV任务列表(case 9)

需求:在Datagrid中展示最近10条AGV任务记录,5个字段:任务编码、AGV编号、当前位置、任务状态、创建时间。

后端:

javascript 复制代码
case 9://驾驶舱-转运信息-AGV任务列表
    var sql = `SELECT "c_TaskID", "c_AGVCode", "c_LocationCode",
                      "c_AGVTaskStatus", "c_CreateTime"
               FROM "PM_AGVTaskDetail"
               ORDER BY "c_UpdateTime" DESC NULLS LAST
               LIMIT 10`;
    var res=[];
    var ret = await SQLQuery('RFM_PM', sql, res);
    OutParam.records=res;
    OutParam.errorCode=ret;
    break;

关键细节:ORDER BY "c_UpdateTime" DESC NULLS LAST。为什么加 NULLS LAST?因为未完成的任务 c_UpdateTime 为NULL,不处理的话NULL值会排在最前面,导致取到的10条记录全是NULL。加上 NULLS LAST 确保真正有更新时间的记录排前面。

前端绑定Datagrid:

javascript 复制代码
var M = { 'type': 9 };
 
requestCalculationPost($System.RFM_PM, 'PM_cockpit', M, function (res) {
    if (res.Code === 0 && res.errorCode === 0) {
        var resdata = res.records;
        if (resdata.length === 0) {
            $Function.tip('info', '无AGV任务数据!');
        } else {
            var datagrid1 = [];
            for (var i = 0; i < resdata.length; i++) {
                var data2 = {};
                data2.number = i + 1;
                data2.c_TaskID = resdata[i].c_TaskID;
                data2.c_AGVCode = resdata[i].c_AGVCode;
                data2.c_LocationCode = resdata[i].c_LocationCode || '';
                data2.c_AGVTaskStatus = resdata[i].c_AGVTaskStatus;
                data2.c_CreateTime = resdata[i].c_CreateTime;
                datagrid1.push(data2);
            }
            $(DatagridX.id).datagrid({
                data: datagrid1
            });
        }
    }
});

3.3 近7日转运完成率(case 10)

需求:折线图展示近7天每天AGV转运任务的完成率(已完成任务数 / 总任务数 × 100%)。

后端:

javascript 复制代码
case 10://驾驶舱-转运信息-近七日转运完成率
    var sql = `SELECT
        "c_CreateTime"::date AS "date",
        ROUND(COUNT(*) FILTER (WHERE "c_AGVTaskStatus" = '已完成')
              * 100.0 / NULLIF(COUNT(*), 0), 1) AS "rate"
    FROM "PM_AGVTaskDetail"
    WHERE "c_CreateTime" >= CURRENT_DATE - 7
    GROUP BY "c_CreateTime"::date
    ORDER BY "c_CreateTime"::date`;
    var res=[];
    var ret = await SQLQuery('RFM_PM', sql, res);
    OutParam.records=res;
    OutParam.errorCode=ret;
    break;

技术要点:

  • NULLIF(COUNT(*), 0)

    防止当天没有任务时除零报错

  • FILTER (WHERE ...)

    是PostgreSQL的聚合过滤语法,比 CASE WHEN 更简洁

  • ::date

    是PostgreSQL的类型转换语法,将timestamp转为date以按天分组

前端(ECharts折线图,CustomCharts52):

javascript 复制代码
var dates = getLastSevenDays();  // 生成最近7天日期数组
var M = { 'type': 10 };
 
requestCalculationPost($System.RFM_PM, 'PM_cockpit', M, function (res) {
    if (res.Code === 0 && res.errorCode === 0) {
        var resdata = res.records;
        // 构建日期→完成率映射
        var rateMap = {};
        for (var i = 0; i < resdata.length; i++) {
            var d = resdata[i].date;
            var parts = d.split('-');
            rateMap[parts[1] + '-' + parts[2]] = Number(resdata[i].rate);
        }
        // 按7天顺序填充数据,无数据的天填0
        var chartData = [];
        for (var j = 0; j < dates.length; j++) {
            chartData.push(rateMap[dates[j]] || 0);
        }
 
        option = {
            title: { text: "近七日转运完成率", ... },
            xAxis: { data: dates, ... },
            yAxis: { name: '单位:(100%)', ... },
            series: [{
                data: chartData,
                type: 'line',
                lineStyle: { color: 'DeepSkyBlue', width: 5 },
                ...
            }]
        };
        CustomCharts52.$Children.echart.setOption(option, true);
    }
});

3.4 近7日转运次数(case 11)

需求:柱状图展示近7天每天AGV转运任务的总数量。

后端:

javascript 复制代码
case 11://驾驶舱-转运信息-近七日转运次数
    var sql = `SELECT
        "c_CreateTime"::date AS "date",
        COUNT(*) AS "cnt"
    FROM "PM_AGVTaskDetail"
    WHERE "c_CreateTime" >= CURRENT_DATE - 7
    GROUP BY "c_CreateTime"::date
    ORDER BY "c_CreateTime"::date`;
    var res=[];
    var ret = await SQLQuery('RFM_PM', sql, res);
    OutParam.records=res;
    OutParam.errorCode=ret;
    break;

前端的绑定逻辑与完成率类似,只是后端返回的是 cnt(数量)而非 rate(百分比),图表改为柱状图(CustomCharts53),使用渐变色柱状条。


四、遇到的坑和解决

坑1:PostgreSQL 的列名必须加双引号

KingFusion 连接的是 PostgreSQL 数据库,PG 对大小写敏感,KingFusion 建立表时字段名带了双引号(如 "c_AGVCode"),后续查询必须原样加上双引号,否则报错"字段不存在"。

错误写法:

复制代码
var sql = `SELECT c_AGVCode, c_AGVStatus FROM PM_AGVStatusDetail`;

正确写法:

复制代码
var sql = `SELECT "c_AGVCode", "c_AGVStatus" FROM "PM_AGVStatusDetail"`;

坑2:NULL值排序问题

ORDER BY "c_UpdateTime" DESC 时,NULL值在PostgreSQL中默认排在最前面(DESC排序时),导致 LIMIT 10 取到的都是没有更新时间的记录,真正需要的最新记录反而取不到。

解决:加 NULLS LAST,让NULL值排到最后。

坑3:静态数据到动态数据的过渡

之前驾驶舱的图表数据是静态写死的,比如完成率 95, 96, 95, 97, 98, 95, 97,转运次数 50, 45, 42, 36, 38, 31, 50。改动态数据后,需要前端构建 日期→数据 映射,然后按7天固定顺序填充,某天无数据时填0,避免图表数据错位。

坑4:刷新机制设计

AGV状态需要实时更新,但又不希望频繁调用后端。最终采用"页面变量取反触发"的方式:

  • 页面创建时,取反变量 → 触发一次查询

  • 如需定时刷新,在定时器位置取反同一变量即可

  • 这样刷新的频率和触发时机完全可配置,不用修改查询逻辑


五、总结

转运信息模块的开发,核心就两件事:后端写SQL、前端绑定变量。但细节决定成败:

  1. 数据库设计决定了能做什么功能,PM_AGVStatusDetail 和 PM_AGVTaskDetail 两张表分别支撑了状态展示和任务统计

  2. SQL查询要考虑PG的特殊语法和NULL值处理

  3. 前端架构用页面变量触发取代直接调用,刷新机制更灵活

  4. 代码管理:每次测试通过后,代码备份到知识库,并在驾驶舱页面创建Excalidraw逻辑流程图,避免时间久了忘记


相关文章:

代码备份:本文所有代码已归档到个人知识库,持续更新。

相关推荐
LHX sir5 天前
HubPort 和 SCADA / MES 是什么关系?是替代还是增强
网络·工业互联网·scada·mes
优德普ERP数字化11 天前
壳体承压曲线实时判异实施:时序数据、规则边界与MES状态闭环
mes·优德普·承压曲线·实时判异·质量状态·复检任务·制造追溯
山海鲸可视化18 天前
数字孪生技术:创新性破解城市高温难题
大数据·运维·数字孪生·数据可视化·可视化大屏
IT古董19 天前
【MES学习笔记系列】05 - MES 数据库设计
笔记·学习·mes
源码宝1 个月前
制造工厂如何挑选MES?有哪些注意事项?
源码·mes
招风的黑耳1 个月前
Axure 可视化原型设计效果全景展示
axure·可视化大屏·大屏端
力旷智能2 个月前
制药设备智能化中的数据采集架构设计与MES集成方案
数据采集·plc·工业物联网·mes·数智化转型·制药装备
Digiwin_CN2 个月前
MES品牌有哪些?附选型核心指标与鼎捷MES功能详解
mes
摩尔元数2 个月前
数字化破局!电感制造过程管理转型实践
制造·mes