# 报警

> 报警编辑器：标识与地址、条件、带优先级、通知方式、延时、消息和确认的“行为”卡片、“状态”竖栏、状态机、宣示方式，以及确认和全部确认。

报警盯着一个布尔条件，并驱动一台确认状态机，这样一个要紧的条件就绝不会来了又走而没人看见。在 Logic 那一栏 Alarms 根下面选中一个报警，或者在它所在文件夹或所属那一节的根的表格里按它那一行上的**打开**，就能打开它的编辑器（用“新建…”>“新建报警”造出来的报警不会被打开，而是在那张表格里突出显示，见[新建与复制](logic#creating-and-copying)）；读要**查看 Logic**，编辑要**配置 Logic**，确认要**操作 Logic**。

标题栏显示报警标记（报警活动期间它在响）、名称、那一行“本工作站监视的一个条件：条件成立期间，报警会宣示自己，并等待被确认。”、一枚写着当前状态的胶囊（正常；活动，未确认；活动，已确认；已复归，未确认），以及报警关着的时候那枚**已禁用**胶囊。表单那一列里放着**标识**、**条件**和**行为**；右边那条竖栏放着**状态**。

## 标识 {#identity}

| 字段 | 它是什么 | 取值 / 默认值 | 影响 |
| --- | --- | --- | --- |
| 名称 | 显示名称；它规整之后那一段就是 `Alarms` 下面的地址。 | 自由文本；新报警叫 `AlarmN`。 | 和[变量的名称](logic-variables#name-and-address)一样通过安全重命名确认：先预告会跟着改的那些引用，按回车或者离开字段确认，然后是“报警已重命名，更新了 N 处引用。”重命名会保住实时状态和一次还没做的确认。撞名的名字会当场被拒绝。 |
| 地址行 | `Alarms.<folders>.<segment>`，回显在名称下面。 | 只读。 | 表达式和脚本通过它读这个报警的状态（`Alarms.HighLevel.IsActive`）。 |
| 说明（可选） | 自由文本。 | 空。 | 没有影响。 |

## 条件 {#condition}

**条件**卡片就是那个[脚本编辑器](logic-expressions#the-script-editor)，带一句提示“一条在地址空间之上书写、结果为布尔值的 JavaScript 表达式，例如 Connector.Simulated.Pump.Temp.ValueToReal() > Variables.SetpointHigh.ValueToReal()。条件不能引用其他报警。”这个条件遵守[表达式](logic-expressions)的每一条规则：一条只读表达式、静态地址、质量关卡、250 毫秒上限。它可以引用点位、两种种类的变量、Process 的点位、Assets 和那些诊断值，绝不能引用 `Alarms.*`，而且它必须产出一个布尔量（“条件的计算结果必须是布尔值，但它得到的是‘…’。”）。一个在某次扫描周期里求不出来的条件（被引用的点位不可用、缺失或为空）算作不确定：状态机保持它上一个状态。

## 行为 {#behavior}

| 字段 | 它是什么 | 取值 / 默认值 | 影响 |
| --- | --- | --- | --- |
| 优先级 | 严重程度。 | 严重、高、中（默认）、低；每一个选项都按它的严重程度着色画出来。 | 显示在树的圆点上、每一处概要里、弹出消息卡片上、诊断日志里和状态栏指示上；在那些共用的标记上，处境最差的待处理优先级胜出。 |
| 通知方式 | 一次激活除了那个常开的状态栏指示之外，还怎样宣示。 | 常驻（默认）、定时、状态栏。字段的提示文字是：“一次激活怎样宣示：常驻 = 弹出消息一直留到被确认或恢复正常；定时 = 弹出消息显示几秒；状态栏 = 只在状态栏上给一个指示。” | 见“宣示方式”。 |
| 上升延时（ms） | 条件要连续成立多久，报警才升起。 | 一个非负整数；填 0 立刻升起。 | 按 100 毫秒扫描周期的分辨率算的连续观察时间。 |
| 下降延时（ms） | 条件要连续不成立多久，报警才复归。 | 一个非负整数；填 0 立刻复归。 | 同样的分辨率。 |
| 消息（活动期间显示，可选） | 宣示时带着的文字。 | 自由文本，空。 | 卡片和系统通知读作“`<Message>`（优先级）”；没写消息时是“`<Priority>`优先级报警处于活动状态。” |
| 需要确认：已复归的报警在操作员确认之前一直保持锁定 | 一次激活要不要一个响应。 | 默认打开。 | 关掉：报警直接升到“活动，已确认”，也直接复归到“正常”；它照样宣示，但绝不锁定，也绝不等待。 |

一个负的或者解析不了的延时会保留已存的值，并显示“延时必须是非负整数（ms；分辨率是 100 ms 的扫描周期）。”延时算的是连续**观察到**的时间：条件求不出来期间，两个延时时钟都重新起算，所以一段看不见的窗口绝不会被算进升起或复归里。

保存对条件、对某个延时、对“需要确认”或者对“已启用”的一次改动，会把状态机重置成“正常”（换了规则之后留着的旧状态会撒谎）；这次重置以来源“配置”记进日志，并把这条报警的弹出消息卡片关掉。重命名称、说明、优先级、消息或通知方式，或者把这条报警重新归到另一个文件夹里，都保住状态。

## “状态”竖栏 {#the-state-rail}

**状态**用文字显示当前状态、“优先级：`<priority>`”、**活动开始**（条件不成立时是一道短横）和**上次状态变化**（还没有变过状态时是一道短横），以及那句提示“每一次状态变化都会出现在事件页面和常驻的报警历史里。报警状态就在地址空间里，例如在表达式或脚本中写 Alarms.X.IsActive。”Alarms 根的面板在“数值”那一列里列出每一个报警，写作“`<state>` · `<priority>` · 自 `<when>` 起”，每一行还配一个**确认**按钮。

## 状态机 {#the-state-machine}

| 状态 | 含义 |
| --- | --- |
| 正常 | 条件不成立，没有待处理的东西。 |
| 活动，未确认 | 条件成立，还没有被确认。 |
| 活动，已确认 | 条件成立，已经被确认（或者这条报警根本不要求确认）。 |
| 已复归，未确认 | 条件在还没被确认时就回到了正常：保持锁定，直到操作员确认。 |

条件连续成立熬过上升延时，就把报警从“正常”或者从“已复归，未确认”升到“活动，未确认”（“需要确认”关掉时直接到“活动，已确认”）。条件连续不成立熬过下降延时，就把它复归：已经确认过的到“正常”，没确认过的到“已复归，未确认”。确认把“活动，未确认”移到“活动，已确认”，并把那个锁定放开回到“正常”。扫描周期在逻辑队列上每 100 毫秒先给变量求值、再给报警求值。报警状态只在内存里：重启之后每一台状态机都从“正常”开始，第一次扫描周期从实时条件把现实重新推一遍，所以一个锁定或者一次还没做的确认挺不过一次重启。每一次状态变化，不管它从哪里来（扫描周期、一次确认、一次保存/禁用/删除引起的重置），都会被记进日志，并记进[事件](events-alarms)页面那份常驻的报警历史。

## 宣示方式 {#annunciation}

窗口状态栏始终带着数量和处境最差的那个严重程度（见[窗口](app-window)）。除此之外，由通知方式决定：

| 通知方式 | 行为 |
| --- | --- |
| 常驻 | 升起时给一张弹出消息卡片，标题是这条报警的名字，颜色按优先级来（严重画成错误，高和中画成警告，低画成提示），还没确认时带着**确认**，另外还带**打开报警**。它一直留着，直到报警被确认或者回到“正常”；“已复归，未确认”那个锁定让它留在屏幕上，而再一次升起会把它替换掉。 |
| 定时 | 同一张卡片，8 秒之后自己消失。 |
| 状态栏 | 不给卡片，也不给系统通知；只有状态栏上那个指示。 |

手工把一张卡片关掉不算一次确认。窗口收进托盘或者最小化期间，一次“常驻”或“定时”的激活还会额外弹一条文字相同的系统通知。

## 确认 {#acknowledging}

确认是一条操作类命令，报警在哪里遇见你，你就在哪里确认：一个打开着的报警上的**确认**命令、Alarms 根上的**全部确认**、Alarms 根面板里每一行的**确认**按钮、一行报警右键菜单里的**确认**、弹出消息卡片上的**确认**、[事件](events-alarms)页面、仪表画面上的[报警提示条](component-alarm-banner)，以及代理（记成 MCP）。每一次确认都搭逻辑队列，连同它的来源记进日志，并把这条报警的卡片关掉。

不管在哪里出现，只有真的有答复可给的地方才提供答复。一条处于“正常”或者已经确认过的报警，它的命令仍然摆在那里、置灰，写着“这条报警不在等待响应。”；对一份没有待处理项的注册表按**全部确认**，写的是“没有报警在等待响应。”而记录条只确认真正动过的事：按下一个什么都不改变的答复，报告的是同一句话，绝不会是“已确认。”，因为一份记下了根本没人做过的操作员动作的工作站日志，比一条一时够不着的命令还要糟。

| 命令 | 作用 | 何时置灰 | 何时不画出来 |
| --- | --- | --- | --- |
| 确认 | 确认打开着的这条报警；记录条写“已确认。” | 它处于“正常”或者已经确认过时写“这条报警不在等待响应。”；只读期间写“运行时已停止”。 | 角色没有“操作 Logic”。 |
| 全部确认 | 在一个队列项里把每一条有待处理项的报警都确认掉；记录条写“全部报警已确认。” | 没有待处理项时写“没有报警在等待响应。”；只读期间写“运行时已停止”。 | 角色没有“操作 Logic”。 |
| 启用 / 禁用 | 翻转“已启用”；禁用会把状态机重置成“正常”并停止求值。 | 工作站被锁住或只读。 | 角色没有“配置 Logic”。 |
| 复制 | 把这条报警复制到 Alarms 根下面一个空闲的名字上，副本不会被打开，而是在这个根的表格里突出显示（见[新建与复制](logic#creating-and-copying)）；副本从“正常”开始。 | 工作站被锁住或只读。 | 角色没有“配置 Logic”。 |
| 删除报警 | 先问“要删除‘X’吗？这会把这条报警拿掉。 此操作无法撤销。”，先把状态机重置（这样一次宣示绝不会活得比它的报警还久）再把它移除；指向 `Alarms.X` 的引用会变成悬空。 | 工作站被锁住或只读。 | 角色没有“配置 Logic”。 |

## 使用者 {#used-by}

表单那一列底部的**使用者**卡片列出工作站上读取这个报警的一切，这样在重命名或删除它之前，你就能看到什么依赖着它。选中这个报警时会读取一次，重命名之后再读取一次。

| 种类 | 位置 | 名称 |
| --- | --- | --- |
| Modbus 服务器 | 空 | [Modbus 服务器](connector-modbus-server)寄存器映射里提供这个报警某一个状态的那一行，以寄存器类别和寄存器表示（`Coil 0`） |
| OPC UA 服务器 | 空 | OPC UA 服务器[提供的节点](connector-opcua-served-nodes)里提供这个报警的那一行：始终只读 |
| MQTT 发布 | Broker | Broker 的[发布卡片](driver-mqtt#the-publish-card)里发布这个报警的那一行的主题 |
| 变量、动作 | 空 | 文本里读取这个报警某个成员（`Alarms.HighLevel.IsActive`）的表达式变量或动作，并带上它所在的文件夹 |
| 仪表画面 | 那个仪表画面 | 地址属性点名这个报警的组件 |

条件从不读取别的报警，所以不会列出报警。服务器的一行凭稳定的身份抓住这个报警：重命名之后它仍然提供同一个报警，删除之后这一行仍然留着，并说出它失去了什么。Broker 的一行也一样，并保持它的主题。查找进行时，卡片上写“正在读取引用…”；没有任何东西使用这个报警时，写“工作站上没有任何东西使用这个报警。”；查找失败时，写“无法读取这些引用”并带有**重试**，绝不会是一个空列表。卡片只读：每一个能打开编辑器的角色都看得到它，工作站锁定期间也看得到，而且它不提供任何命令。

## 报警不做什么 {#what-alarms-do-not-do}

除了条件本身写出来的之外没有回差或死区，没有屏蔽，没有声音，不能从脚本里确认，重启之后不保留状态，报警被禁用或者运行时停止期间也不求值。报警不是 Process 的事件条目：那些是在 [Process](process-occurrences) 页面上配置和判定的。
