DLMS/COSEM 蓝皮书解读十八Register monitor 类class_id 21—— 数据驱动的越限触发值一越线就跑脚本系列说明本系列基于 DLMS UA《Blue Book蓝皮书第 16 版 · 第 2 部分》一个接口类一篇。第 17 篇讲了Activity calendarclass_id 20它解决的是时间驱动的触发——到了某个时刻去调Script table切费率、切季节。本篇的Register monitorclass_id 21解决的是另一类触发数据驱动的触发——当某个被监视的寄存器值越过阈值时自动去执行脚本。过压、欠压、过流、反向潮流、需量越限告警背后站着的都是它。和到点就动的日历分工明确一个管何时一个管值到哪了。上篇回顾Activity calendar让表计自己知道今天该用哪套费率并且到点自己换整条链路是Clock给时间 →Activity calendar判时段 → 调Script table→Script table去SETRegister activation的active_mask。这条链路里所有动作都是由时间发起的。但现场还有一大类需求不是到点而是量到了电压过了 253 V、电流过了 100 A、功率反向了——这些时刻不是预先写在日历里的而是随负荷实时发生的。这一类量到即触发的需求就是Register monitor的领地。0. 为什么需要 Register monitor先想一个没有它的场景。你要做过压告警电压 Register 里实时存着当前电压你想在电压超过 253 V 时让表计主动上报一条告警事件。方案 A主站轮询主站每隔几秒去GET这个电压 Register自己判断有没有超。问题很明显——主站得 7×24 不间断地拉浪费信道而且电压尖峰可能只持续 1 秒轮询间隔稍大就漏掉了更别说电表掉线、主站挂掉时这段期间完全无告警。方案 B表内自判断把判断 动作下沉到表计里——表计自己一直在看这个 Register值一越线自己就跑一段脚本记事件、置状态字、甚至通过Push setup主动上报。主站不用一直盯着只等表计主动喊一声。Register monitor就是方案 B 在 COSEM 对象模型里的标准落地。它不存业务数据它只做一件事盯着一个寄存器的值越线就触发脚本。蓝皮书原文Register monitor, Overview“This IC allows modelling the function of monitoring of values modelled by ‘Data’, ‘Register’, ‘Extended register’ or ‘Demand register’ objects. It allows specifying thresholds, the value monitored, and a set of scripts … that are executed when the value monitored crosses a threshold.”注意原文把能监视谁说得很死只有Data、Register、Extended register、Demand register这四个类的值能被监视。换句话说你不能拿它去监视Profile generic的一行、也不能监视Clock的 time。后面讲坑时会展开为什么。另外还有一句硬约束写代码配对象列表时千万别忘蓝皮书原文Register monitor, Overview“The IC ‘Register monitor’ requires an instantiation of the IC ‘Script table’ in the same logical device.”它自己不执行任何东西它只是指定越线了去叫哪个Script table里的哪条脚本。所以Register monitor和Script table是配对出现的——没有Script table实例这个 monitor 配了也白配。1. 类蓝图Register monitor 0...n class_id 21, version 0属性静态/动态数据类型MinMaxDefShort namelogical_namestaticoctet-string———xthresholdsstaticarray———x 0x08monitored_valuestaticvalue_definition———x 0x10actionsstaticarray———x 0x18该类无方法Specific methods 一栏为空。这点很关键触发是表计内部自动完成的不需要客户端去调一个 method。客户端只负责SET这几个属性来配置监视规则之后就交给表计自己跑。Register monitor只有version 0没有版本分裂的痛苦。2. 属性逐条解读2.1 logical_name第一属性老规矩octet-string6 字节 OBIS。例如过压监视对象可以挂0-0:16.0.0.255仅为示例实际 OBIS 以设备对象列表为准。其余没啥好说。2.2 thresholds阈值集合蓝皮书原文Register monitor, thresholds“Provides the threshold values to which the attribute of the referenced register is compared.”数据类型是array数组的每个元素叫threshold。这里有个极易踩坑的类型约束蓝皮书原文Register monitor, thresholds“The threshold is of the same type as the monitored attribute of the referenced object.”阈值必须和被监视的那个 attribute 是同一个数据类型。被监视的是Register的value假如是long-unsigned那thresholds这个 array 里的每个元素也得是long-unsigned被监视的是boolean阈值也得是boolean。你不能指望表计帮你做隐式类型转换。工程上通常把thresholds按升序排比如[198, 253]下限、上限对应电压的正常区间 [198, 253] V。原文没强制升序但升序最直觉、也最不容易配反。2.3 monitored_value被监视值软引用蓝皮书原文Register monitor, monitored_value“Defines which attribute of an object is to be monitored. Only values with simple data types are allowed.”它不是一个值而是指向哪个对象的哪个属性的软引用。结构是value_definitionvalue_definition :: structure { class_id: long-unsigned, logical_name: octet-string, attribute_index: integer }三个字段正好定位一个属性class_id指明是哪一类3 Registerlogical_name是那个对象的 6 字节 OBISattribute_index是属性序号Register 的value是 2。“Only values with simple data types are allowed”——这是第二个硬约束。也就是说被监视的属性必须是简单类型long-unsigned、integer、double-long、boolean、float32这种不能是structure/array/compact-array。所以你想监视一整条负荷曲线里的最大值是不行的只能监视某个具体的标量属性。2.4 actions越线动作集合蓝皮书原文Register monitor, actions“Defines the scripts to be executed when the monitored attribute of the referenced object crosses the corresponding threshold. The attribute actions has exactly the same number of elements as the attribute thresholds. The ordering of the action_items correspond to the ordering of the thresholds.”这条原文信息量很大拆成三句话记actions也是array元素个数必须和thresholds完全相同。第 i 个 action 对应第 i 个 threshold——一一对应、顺序对齐。每个 action 不是单个脚本而是一个action_set上穿 / 下穿两个动作array action_set action_set :: structure { action_up: action_item, action_down: action_item }action_up与action_down又各自是一个action_itemaction_item :: structure { script_logical_name: octet-string, -- 指向某个 Script table 实例的 OBIS script_selector: long-unsigned -- 该 Script table 里的第几条脚本 }蓝皮书原文Register monitor, actions“action_up defines the action when the attribute value of the monitored register crosses the threshold in the upwards direction;”“action_down defines the action when the attribute value of the monitored register crosses the threshold in the downwards direction.”也就是说对每一个阈值表计在值向上穿过它时跑action_up向下穿过它时跑action_down。比如阈值 253电压从 240 蹿到 255是向上穿跑action_up过压告警从 255 回落到 250是向下穿跑action_down过压恢复 / 清告警。2.5 一个完整的对象结构逻辑视图把四个属性拼起来Register monitor实例在内存里大致长这样逻辑结构具体字节按 A-XDR 编码下节给示意Register monitor (logical_name 0-0:16.0.0.255) ├─ thresholds: array[ long-unsigned(198), long-unsigned(253) ] ├─ monitored_value: { class_id3, logical_name01 00 20 07 00 FF, attribute_index2 } └─ actions: array[ action_set { up {0-0:10.0.155.255, 3}, down {0-0:100.0.155.255, 4} }, -- 对应 threshold 198 action_set { up {0-0:10.0.155.255, 1}, down {0-0:100.0.155.255, 2} } -- 对应 threshold 253 ]注意thresholds与actions等长都是 2且顺序一一对应索引 0 的阈值 198 配actions[0]索引 1 的阈值 253 配actions[1]。3. 方法没有方法。如前所述Register monitor的 Specific methods 一览为空。触发完全由表计固件在检测到越线时自动发起客户端只能SET属性来配置规则含把actions/thresholds配成空数组来临时停用这个监视。这点和Script table的execute、Activity calendar的activate_passive_calendar那种由客户端主动调用的 method 形成对照——Register monitor是纯被动、纯表内自治的。4. 【实战举例】下面所有 OBIS、scaler、配置值均为帮助理解而构造的示例非蓝皮书原文实际以设备对象列表为准。示例 1三相电压 L1 的过压 / 欠压告警被监视对象电压 RegisterOBIS 1-0:32.7.0.255L1 相电压瞬时值scaler_unit {0, 35}即真实电压 value × 10^0V单位是 Venum 35。允许范围按国标经验取 [198 V, 253 V]。我们配两个阈值thresholds [ long-unsigned(198), long-unsigned(253) ]升序下限、上限monitored_value { class_id 3, logical_name 01 00 20 07 00 FF, attribute_index 2 }指向那个电压 Register 的valueactions必须与thresholds等长2 个action_set并假设Script table实例 OBIS 0-0:10.0.155.255里面预先编好了 4 条脚本阈值方向动作含义198下限action_upscript_selector 3电压从欠压区回升到 ≥198 → 清除欠压告警198下限action_downscript_selector 4电压跌到 198 → 置欠压告警253上限action_upscript_selector 1电压升到 253 → 置过压告警253上限action_downscript_selector 2电压回落到 ≤253 → 清除过压告警时序正常稳态 231 V231V ─── 升到 255V ─┐ ├─ 向上穿过 253 → actions[1].action_up → 置过压告警 255V ─── 回落到 240V ┘ 240V ─── 跌到 190V ─┐ ├─ 向下穿过 198 → actions[0].action_down → 置欠压告警 190V ─── 升回 230V ─┘ └─ 向上穿过 198 → actions[0].action_up → 清欠压告警每一条脚本selector 1~4里面通常就是SET某个告警状态字比如一个Data对象里的bit-string或者SET一个事件寄存器、再配合Push setup主动上报。具体脚本体由第 9 篇Script table定义。示例 2电流越限告警阈值类型必须一致被监视对象电流 RegisterOBIS 1-0:31.7.0.255scaler_unit {0, 33}单位 A。假设配thresholds [ long-unsigned(100) ]。如果手滑把thresholds写成了[ integer(100) ]带符号整型而value是long-unsigned——类型不一致。原文明确threshold类型必须等于被监视属性的类型这种不一致要么SET直接被拒要么表计行为未定义不同厂商实现不同但都要么拒绝要么不触发。配之前先确认被监视 attribute 的>示例 3电流反向告警结合电流方向被监视对象有功功率 RegisterOBIS 1-0:11.7.0.255当前计量电流单位 Aenum 33scaler_unit {0, 33}。注意电流可能有正负正向 / 反向电流数据类型若是long那阈值也得用带符号long。thresholds [ long(0) ]actions[0].action_down值向下穿过 0即从正变负执行反向电流告警脚本。这样一个阈值就能区分送 / 受电方向。示例 4一个 monitor 配多个阈值 与 Activity calendar 的分工假设一台光伏表要同时监控过压(253)、欠压(198)、过流(100A)。可以用同一个 monitor 堆 3 个阈值每个阈值一个action_set上下穿两个动作也可以拆成 3 个 monitor 各管一个量。工程取舍同一 monitor 多阈值省对象、逻辑集中但所有动作脚本都得在同一个Script table里且阈值必须同类型电压是 V、电流是 A类型不同就不能塞进同一个 monitor——必须拆。拆多个 monitor每个量一类、类型自由调试清晰。至于时间驱动 vs 数据驱动同样是切费率Activity calendar是按日历时刻触发每月 1 号 0 点切月费率Register monitor是按量触发比如反向功率越限时立即告警。两者不是替代关系是互补关系。示例 5从 LN 视角看一次 GETA-XDR 结构示意客户端想读取上面那个 monitor 的全部属性发一个get-requestLN 方式get-request { class_id 21, logical_name 00 00 10 00 00 FF (示例: 0-0:16.0.0.255), attribute_index 0 -- 0 表示所有 public 属性 }表计返回的get-response里attribute_index 0的 data 是一个structure元素顺序严格按属性表structure { (1) logical_name: octet-string(6) 01 00 10 00 00 FF (2) thresholds: array { long-unsigned(198), long-unsigned(253) } (3) monitored_value: structure { long-unsigned(3), -- class_id Register octet-string(6)01 00 20 07 00 FF, -- 电压 Register 的 OBIS integer(2) } -- attribute_index value (4) actions: array { structure { -- action_set[0] structure { octet-string(6)00 00 0A 00 9B FF, long-unsigned(3) }, -- up structure { octet-string(6)00 00 0A 00 9B FF, long-unsigned(4) } -- down }, structure { -- action_set[1] structure { octet-string(6)00 00 0A 00 9B FF, long-unsigned(1) }, -- up structure { octet-string(6)00 00 0A 00 9B FF, long-unsigned(2) } -- down } } }上面是逻辑结构真实报文每个字段前还有 A-XDR 的 type tag 和长度如structure0x02、long-unsigned0x12、octet-string0x09 等字节级编码此处略按第 1、2 篇讲的 A-XDR 规则展开即可。示例 6Register monitor 与 Limiterclass_id 71的定位差异前瞻蓝皮书里另有一个专门做越限的接口类Limiterclass_id 71仅在 Ed.16 中提供 version 0。它比Register monitor能力更强可同时监视多个monitored_value、分别设threshold_active/threshold_normal上下限分离、配actions越限动作、并带emergency_profile紧急动作档案越限后按时间档案执行一连串动作。可以把Register monitor理解为早期、较精简的越限模型而Limiter是功能更完整的后续模型。本篇严格按原文只讲Register monitorclass_id 214 属性、无方法。Limiter属于另一接口类细节留待后续对应篇目展开此处不臆测其字段。示例 7越限没触发现场排查清单配完Register monitor却发现值明明超了却没告警按这个顺序查基本能定位同 LD 内有Script table吗原文硬约束monitor 要求同逻辑设备内有Script table实例。没有它越线无事发生。thresholds与actions等长吗不等长要么SET被拒要么越线跑错/不跑脚本。先数元素个数。阈值类型 被监视属性类型吗long-unsignedvsinteger一字之差就可能导致整类不触发。先GET被监视属性的类型。monitored_value指向的是真实存在的标量属性吗class_id/logical_name/attribute_index任一错监视对象就瞄歪了。阈值升序、up/down 没配反吗升序最稳配反会让过压跑去清告警、“欠压跑去置告警”。值是穿过了阈值还是停在区间内触发条件是穿越瞬间不是持续在阈值之上。值一直高着不会反复告警。脚本本身执行成功了吗看事件日志/状态字脚本里引用的对象若不存在或ACTION失败越线事件就哑火但阈值状态通常不回滚。测试时真的把值推过阈值了吗别只改thresholds本身要改被监视 Register 的值或用测试工装让固件真正检测到 crossing。示例 8越限即上报 —— 与 Push setup 联动Register monitor自己只负责跑脚本若想让越限主动上报到主站而非只记本地事件就把上报动作编进脚本越线 →action_up指向的脚本里除了SET本地告警状态字再调一次Push setupclass_id 40后续篇目会讲的pushmethod把告警事件主动推给主站。整条链路Register值越过阈值 →Register monitor内部检测 crossing → 执行actions[i].action_up→ 该action_item指向的脚本 → 脚本内SET状态字 ACTIONPush setup.push→ 主站收到告警。这样就把数据驱动触发和主动上报串成一条全自动链路主站无需轮询。注Push setup的具体 method/属性以原文对应篇目为准此处仅说明协作关系不臆测其字段。5. 工程上容易踩的坑thresholds 与 actions 必须等长、顺序对齐。原文写得很死“exactly the same number of elements”、“ordering … correspond to the ordering of the thresholds”。多配或少配一个SET可能拒绝或越线时跑错脚本。先数清楚再下发。阈值类型 被监视属性类型。这是Register monitor第一大坑。threshold是long-unsigned还是integer还是float32必须和被监视attribute的>6. 小结 下期预告本篇要点Register monitorclass_id 21做数据驱动的越限触发盯着一个Register或Data/Extended register/Demand register的值越线就跑脚本。四个属性logical_name、thresholds阈值数组类型必须与被监视属性一致、monitored_value指向目标属性的value_definition软引用仅限简单类型、actions动作数组与阈值一一对应、顺序对齐。每个阈值配action_up/action_down两个方向动作各指向Script table里的一条脚本。无 method触发纯表内自治必须和同 LD 内的Script table配对使用。与Activity calendar时间驱动互补一个何时动一个量到哪了动。更完整的越限能力见后续Limiter(71) 篇。下一篇第 19 篇Single action scheduleclass_id 22—— 另一种到点执行的类但它不一定和费率相关。它比Schedule10更轻单个脚本 一组execution_time时间点配合type枚举的 5 种模式含通配符日期用来做每天固定时刻抄表 / 每月 1 号自检这类与计费解耦的周期动作。我们会讲清type的五种取值分别意味着什么、execution_time里 time/date 两个 octet-string 怎么按 date-time 规范编码、以及它和Schedule、Activity calendar三者到底该怎么选。参考资料DLMS UA《Blue Book Ed.16 Part 2 – COSEM interface classes》Register monitor (class_id 21, version 0) 章节。文中属性、数据类型、Short name 偏移x / x0x08 / x0x10 / x0x18、value_definition与action_item结构、以及引文均与原文一致示例中的 OBIS、scaler、配置值与脚本 selector 为帮助理解而构造实际以设备对象列表为准。