条件:数据变化、阈值与时间

第 2 课 · 共 7 课 约 9 分钟

三类条件怎么写、怎么防止重复触发,以及为什么看门狗只能靠时间。

本课目标

读完这一课,你将能够

  • 区分对象数据变化、阈值和时间三类条件,各举一个示例工厂的例子
  • 写出带 when 与 cooldown 的 trigger.change,并说出它只看变化之后的对象
  • 说出看门狗为什么只能用时间条件,并写出它的触发

对象数据条件:盯着一组对象

对象数据条件盯着一个对象集:对象加入集合、离开集合、在集合里被修改,或者定期对集合里的每个对象跑一遍。条件触发时,会把触发它的对象交给效果去用。

「加入集合」最容易误解。给对象集加一个筛选(状态 = 待处理),新建的对象算加入,状态刚改成「待处理」的旧对象也算;已经在集合里的对象再改别的属性,不算。所以它只在「进入」那一刻触发一次。

组织级自动化支持 objectsAdded、objectsModified、objectsRemoved 和 runOnAllObjects。应用工作流用 trigger.change:指定 Object Type,再写 when。对象新建或修改后,平台用变化后的对象求值;为真就开一次运行。每条变化最多触发一次,工作流自己的写入不会触发自己。

「加入集合」when 为真
触发时机对象进入集合的那一次每次新建或修改后,条件都为真
看什么变化前后的状态只看变化之后的对象 object,另有 change(操作、来源、执行人、序号)
防重复进入才触发,天然一次cooldown,加上效果里的检查

阈值:跨过那条线,还是一直在线下

阈值条件拿一个指标和一个数比较。指标可以是一个属性,也可以是一组对象的汇总,比如一条产线上所有设备的最高温度。标准模型在指标越线时触发,回到线内时再记一次「恢复」。

在 AIDC 里,单个对象的阈值直接写进 when。示例工厂的④「设备振动超阈值」这样写;lastVibration 是本课给 machine 加的一个小属性,设备物联网每次同步写入最新读数,7.1 是示例阈值:

"trigger": {
  "change": {
    "type": "machine",
    "when": "=object.lastVibration > 7.1",
    "cooldown": "1d",
    "input": { "machine_id": "=object.machineId" }
  }
}

设备一直振动超标时,每次同步都会修改对象。工作流的 when 每次都为真,它不判断是否刚越线。因此要两道保险:

  • cooldown:同一个对象两次运行至少隔这么久,冷却期内的变化只推进游标,不开运行。
  • 效果里的检查:先查是否已有未完成的维保工单,有就什么都不做。

一组对象的汇总阈值,目前没有对应的条件类型:用定时触发加聚合查询算出汇总,在计算步骤里比较,再通知。

时间条件与看门狗

时间条件按时间表触发:每天、每周、每月,标准模型还支持 cron 表达式。它的下一次评估,要等上一次评估完成、效果排上队之后才排定,所以两次评估不会互相重叠。

应用工作流的 trigger.schedule 用 every 和 at。every 是 1–9,999 的整数加 m、h 或 d,最短 5 分钟。按天间隔才能配 at,时间按 UTC 的 HH:MM 写。组织级时间条件的 at 和 weekdays 按北京时间算。部署工作流时按频率分级检查:

REJECT

小于 5 分钟

部署被拒绝。要实时,就用 change。

WARN

5 分钟到 1 小时

高频,一定告警。每 5 分钟一次,一个月是 8,640 次运行。

OK

超过 1 小时

中频只提示次数;一天一次起是正常。

只有时间本身就是条件的事才用它:日报(⑤)、月底对账,还有看门狗(⑥)。看门狗盯的是「该来的数据没来」:数据不来就没有变化,change 永远不会响,只有时间能发现。

feedStatus 是本课给示例工厂加的小对象,ERP 发布端每一轮把当前时间写进它的 syncedAt。⑥ 每小时查它一次,超过 2 小时就发邮件。下面是清单的关键部分:

{
  "slug": "erp-watchdog",
  "limits": { "notificationsPerDay": 10 },
  "workflow": {
    "trigger": { "schedule": { "every": "1h" } },
    "steps": [
      { "id": "feed", "kind": "use", "title": "ERP heartbeat",
        "use": "factory-ops/erp_heartbeat", "with": {} },
      { "id": "check", "kind": "compute", "title": "How old is it",
        "fields": {
          "hours": "=hoursSince(steps.feed.row.syncedAt)",
          "stale": "=hours == null || hours > 2"
        } },
      { "id": "mail", "kind": "notify", "title": "Alert the planner",
        "when": "=steps.check.stale",
        "to": ["planner@example.com"],
        "subject": "ERP data is {{ steps.check.hours }} hours old",
        "body": "No ERP sync has arrived for over two hours. Check the publisher before trusting stock and order figures.",
        "dedupe": "=today()", "cooldown": "6h" }
    ],
    "output": { "hours": "=steps.check.hours" }
  }
}

hoursSince 算数据有多久没更新;读不到心跳时得到 null,同样算过期。dedupe 与 cooldown 让同一天里最多每 6 小时提醒一次。每小时属于高频,部署会有告警,这里时间本身就是条件,可以接受;要放宽就把 every 改成 2h。

逐个对象,还是整批

change 触发时,input 决定一次运行处理什么。

input 映射到对象input 留空
一次运行处理一个对象同一批变化只开一次运行
适合对每个对象各做一件事:为一个物料起草一张订单对全体做一次检查或汇总
标准模型里的说法逐个对象执行一次给全部对象执行

一次数据快照改了很多对象,留空的写法同一批只开一次运行,而不是同样的检查跑很多遍。频繁更新、又只需要汇总的检查,用留空的写法,或者干脆改成定时。

要点

  • AIDC 的 trigger.change 只看变化之后的对象,when 每次为真就触发一次,没有「刚刚跌破」的概念。
  • 防重复用两道保险:cooldown,和效果里的「已经处理过了吗」的检查。
  • 定时只留给时间本身就是条件的事;小于 5 分钟拒收,5 分钟到 1 小时一定告警。
  • 看门狗盯「该来没来」:没有数据就没有变化,只有定时触发能发现。
  • input 映射到对象是一个对象一次运行,留空是一批变化一次运行。

练一练

为示例工厂写条件

三个练习,都只用本课讲到的写法。

为①「销售订单确认且物料齐套」写 trigger.change。when 只能看变化的那个对象:「物料齐套」应该写在条件里,还是放到效果的查询与计算步骤里?为什么?

小测

选一个答案,马上看解析。

Q1「ERP 两小时没来数据就告警」,为什么不能用 trigger.change?

Q2物料一直低于安全库存,ERP 每次同步都会修改它。只写 when: onHandQty < safetyStockQty 会怎样?

Q3下面哪个 schedule 会被部署直接拒收?

延伸阅读