# 单元地址空间

> 一个单元在 Process.模型.单元 下面发布的每一个点位、它的类型和含义、哪些点位是可写的命令，以及一个 Logic 脚本可以从中读到什么。

每个单元都把自己的运行数据当作普通的 [Logic](logic) 地址发布在 `Process.<Model>.<Unit>` 下面，因此仪表画面、Logic 表达式、内置的 OPC UA 服务器和代理都不用额外接线就够得着它。这一支在 Logic 页面上“来自其他页面”下面浏览（见[来自其他页面](logic-from-other-pages)），在每一个地址选择器里也能浏览。重命名一个模型、单元、通道、元数据字段、判定或判据，会替换掉相应的地址，并改写活着的文字引用；已记录的文件保留写下去时冻结的那些名称。各段是净化过的名称：模型“Climatic chamber”里一个名叫“单元 1”的单元，寻址写作 `Process.Climatic_chamber.单元_1`。

| 分支 | 暴露什么 | 可写 |
| --- | --- | --- |
| `Channels.<Channel>` | 每条通道当前绑定到的实时值，按通道声明的类型定型。 | 可写的通道（只写或读写角色）是经出口可写的点位；被某次运行记录占着的信号源会拒绝其他每一个写入者。 |
| `Procedure.<point>` | 运行状态，每个点位一个标量（见下表）。 | 否。 |
| `Commands.<command>` | `Start`、`Hold`、`Resume`、`Stop`、`Abort` 和 `Comment`。 | 是：写入即派发（见下表）。 |
| `Next.<point>` | 下一次启动要消耗的那个预选区。 | 只有手工填写的元数据字段可写。 |
| `LastRun.<point>` | 这个单元最近一次完成的运行记录的存档（见 [LastRun](#lastrun)）。 | 否。 |
| `Productivity.Today.<branch>` 和 `Productivity.Rolling.<branch>` | 这个单元在各个周期上的产量统计。 | 否。 |

## 通道 {#channels}

一个通道点位读的是这个单元绑定背后的那个值，投影成通道的数据类型；通道没有绑定、或者它的信号源不可用时，它读作一个空档。在 Logic 脚本里，这个点位是所绑定点位或变量的一个只读定型投影，绝不是那个点位本身：`Value`、`ValueObject`、`ValueRaw`、`ValueScaled`、`ValueCalibrated`、`ValueLimited`、`ValueRounded`，转换函数 `ValueToBool()`、`ValueToInteger()`、`ValueToReal()`、`ValueToString()` 和 `ValueToDateTime()`，质量 `State()`，时间戳 `SourceTimeStamp()` 和 `ApplicationTimeStamp()`，以及 `Refresh()`、`Name` 和 `EngineeringUnit`。数值转换链上的那些成员是为点位绑定作答的；变量绑定作答的是值和应用时间戳。

## Procedure {#procedure}

每一个点位都从活着的那次运行记录算出来，读起来质量良好；没什么可说的点位读作一个空档。

| 点位 | 类型 | 含义 |
| --- | --- | --- |
| `Status` | 文本 | 单元已被预留而运行记录还不存在时是 `Preparing`，没有东西占着它时是 `Idle`，其余时候就是运行状态：`Running`、`Held`、`Saving`、`Completed`、`Aborted`、`Failed`。 |
| `Running` | 布尔值 | 严格对应运行中这个状态；准备、已暂停、保存中和收尾期间它绝不为真。 |
| `Busy` | 布尔值 | 整段被占用的时间：预留、运行中、已暂停、保存中，以及运行记录文件最后关掉。 |
| `Verdict` | 文本 | `Ok`、`Nok`、`Indeterminate` 或 `None`。不管界面或打印出来的标签用什么字样显示，这个值都严格按上面的写法，大小写也一样，所以比较要写成 `Process.<Model>.<Unit>.Procedure.Verdict == "Nok"`。判过的证据在运行记录结束之后仍然读得到，直到下一次运行记录开始。 |
| `Mode` | 文本 | 活着那次运行记录的模式，`Instant`、`Temporal` 或 `Controlled`；一条单条命令（快照、立即判定）也会报。空闲时是空档。 |
| `ActiveProcedure` | 文本 | 活着那次运行记录的名称（“流程 · 单元”）。空闲时是空档。 |
| `SegmentIndex` | 整数 | 时序曲线当前所在的曲线段，从 0 开始。没有曲线时是空档。 |
| `CycleIndex` | 整数 | 当前的循环，从 0 开始。空闲时是空档。 |
| `CycleCount` | 整数 | 这次运行记录的总循环数（单趟为 1）。空闲时是空档。 |
| `Cycle` | 文本 | 循环运行记录写作“2 / 10”；运行记录不循环时是一个空档，这样绑定上去的数值显示写的是空档标记，而不是误导人的一趟。 |
| `FailedCycles` | 整数 | 到目前为止判为不合格的循环数。运行记录不循环时是空档。 |
| `SamplesEvaluated`、`SamplesOut`、`SamplesUnavailable`、`SamplesInvalid` | 整数 | 判定的证据计数器，由判定引擎原子地更新。没有判过的证据时是空档。 |
| `Elapsed` | 小数 | 从运行记录开始到现在的秒数。空闲时是空档。 |
| `StartedAt` | 日期和时间 | 活着那次运行记录是什么时候开始的。空闲时是空档。 |
| `Metadata.<Field>` | 文本 | 模型的每个元数据字段一个点位：正在进行的运行记录在开始时冻结的值。空闲时，以及这次运行记录没有提示填写的字段，是空档；留空的字段读作空文本。开始时触发的动作读的就是它，因为 `Next.<Field>` 显示的已经是下一次。 |

## Commands {#commands}

读一个命令点位，回答的是“现在能不能执行”，因此绑定上去的按钮会自己禁用；写它则通过运行控制台派发这条命令，按写入界面的来源记进日志，并连同按下的那个人的身份一起记录下来。命令是一次服务调用，绝不是一次排队的点位写入。

| 点位 | 什么时候读作真 | 写入 |
| --- | --- | --- |
| `Start` | 单元不忙、运行时接受操作、联锁正常，而且已经预选了一个流程。 | 用预选的元数据启动预选的流程。 |
| `Hold` | 有一次运行记录在运行中，而且它的流程是时序的。 | 冻结这个窗口。 |
| `Resume` | 有一次运行记录处于已暂停，而且联锁正常。 | 解除暂停。 |
| `Stop` | 有一次运行记录在运行中。 | 以已完成结束这次运行记录。 |
| `Abort` | 有一次运行记录在运行中或者已暂停。 | 以已中止结束这次运行记录。 |
| `Comment` | 永不：这个点位没有读数。 | 把写进去的文字作为一条备注追加到活着的那次运行记录上；单元空闲时，则作为一条事后补记加到这个单元最近一次已结束的运行记录上。一个一次运行记录都没有的单元会拒绝（“这个单元没有可以写备注的运行记录。”），空文字也一样被拒绝。 |

工作站拒绝写入期间（运行时已停止），每一条命令都读作假。

## Next {#next}

| 点位 | 类型 | 含义 |
| --- | --- | --- |
| `Next.Procedure` | 文本 | 预选流程的名称。在这里只读：预选是在单元面板的运行记录卡片上做的。 |
| `Next.<Field>` | 文本 | 预选的流程提示填写的每一个元数据字段一个点位；单元重新预选时这一支会重新成形，而属于另一个流程的字段读作空档。手工填写的字段（文本或数字）是一个可写的预选点位，绑定到它的输入组件写的就是它。流水号字段是对计数器此刻会拼出的那个标识的只读预测；读它绝不会把它占下来。 |

预选是全工作站范围的、放在内存里的、临时的：应用启动时它归零，而启动是把它消耗掉，不是把它清空。

## Productivity {#productivity}

两个周期，`Today`（从本地零点起）和 `Rolling`（模型或单元上声明的那个窗口），各带三个分支（见[产量](process-productivity)）：

| 分支 | 点位 | 类型 |
| --- | --- | --- |
| `Summary` | `ProductivityPercent`、`YieldPercent`、`TypicalElapsedSeconds` | 小数；分母还不存在时是空档。 |
| `Summary` | `Finalized`、`OK`、`NOK`、`Indeterminate`、`Unclassified`、`Aborted`、`Failed` | 整数计数。 |
| `Outputs.<key>` | `Quantity`、`RatePerHour` | 小数；每个命名的产量项一个键，模型一个产量项都没声明时，则每个计入产出的流程一个键。 |
| `Procedures.<key>` | `Quantity`、`RatePerHour`，以及上面那些汇总点位 | 同上，按计入产出的流程分。 |

这些键是从稳定的 id 和冻结下来的产出单位推出来的，不是从显示名称推出来的，因此重命名绝不会让一个表达式挪窝。每一个点位都是只读的，而这份统计是从历史索引读出来的，绝不从运行记录文件读。

## LastRun {#lastrun}

这个单元最近一次完成的运行记录的存档：是哪一次、怎样结束的、操作员在开始时填了什么，以及每条判据的结果。在完成时触发的 Logic 动作读的就是它（见[动作](process-actions#logic-action-commands)），其他任何消费者也用同样的方式读它：显示最后一件的画面、报警、工作站的服务器。这个单元每一次记录下来的运行记录都算数，失败的、快照和立即判定也算；导入的运行记录从来不算。

| 点位 | 类型 | 含义 |
| --- | --- | --- |
| `RunId` | 文本 | 运行记录的标识。 |
| `Procedure` | 文本 | 运行记录当时记下的流程名称。 |
| `Status` | 文本 | `Completed`、`Aborted` 或 `Failed`，就这样拼写。 |
| `Verdict` | 文本 | `Ok`、`Nok`、`Indeterminate` 或 `None`，拼写同 `Procedure.Verdict`；失败的运行记录读作 `None`。 |
| `StartedAt`、`FinishedAt` | 日期和时间 | 运行记录开始和结束的时刻。 |
| `Elapsed` | 小数 | 从开始到结束的秒数。 |
| `CycleCount` | 整数 | 运行记录的循环数（单次通过为 1）。 |
| `FailedCycles` | 整数 | 判为不合格的循环数。 |
| `Metadata.<Field>` | 文本 | 模型的每个元数据字段一个点位：运行记录在开始时冻结的值。这次运行记录没有提示填写的字段是空档；留空的字段读作空文本。 |
| `Evaluations.<Evaluation>.<Criterion>.<point>` | 见下表 | 模型的每个判定一个文件夹，下面每条判据一个文件夹，用判据的名称命名，判据没有名称时用它所看通道的名称。 |

每条判据带一个结果，把它被判过的所有循环折叠起来，所以单次通过和一百个循环的运行记录，地址都一样：

| 点位 | 类型 | 含义 |
| --- | --- | --- |
| `Outcome` | 文本 | `Ok`、`Nok` 或 `Indeterminate`，就这样拼写：有一个不合格的循环就是 `Nok`；否则有一个无法判定的循环就是 `Indeterminate`；否则是 `Ok`。 |
| `Passed` | 布尔值 | 只有 `Outcome` 为 `Ok` 时才为真。 |
| `Value` | 小数 | 所看通道的测量值：窗口里最后一个被评估的采样，单个采样点方式下就是被判的那个，取自最近一个测到值的循环。 |
| `MinValue`、`MaxValue` | 小数 | 所有循环里被评估的最小值和最大值。 |
| `Limits` | 文本 | 记录下来的限值，例如 `5 .. 45`。 |
| `WorstExcursion` | 小数 | 所有循环里越过某个界限的最大距离。 |
| `FailedCycles` | 整数 | 把这条判据判为不合格的循环数。 |

这一支的形状来自模型，从不来自最近一次运行记录：目录里的每个元数据字段、每个判定的每条判据都是一个点位，所以写到其中某一个的脚本，在任何运行记录之前就能通过校验，在一次按另一个判定来判的流程运行之后也能通过。

| 情况 | 这些点位读到什么 |
| --- | --- |
| 这个单元还没有任何运行记录结束 | 每个点位都是空档，质量不可用。 |
| 一次运行记录结束 | 新的存档，在单元的 `Procedure.Status` 说运行记录结束之前就已经到位。 |
| 这个单元的另一次运行记录正在进行 | 之前那份存档，直到那次运行记录结束。 |
| 应用程序重新启动 | 这个单元最近一次完成的运行记录，启动后不久从它的文件里重新读出；读出之前是空档。 |
| 最近一次运行记录没有判过的判据（属于另一个判定，或之后才添加的） | 每个点位都是空档。 |
| 两个判定、或两条判据的名称读起来一样 | 什么都没有：这个地址哪一个都不解析，判定的页面会说明（见[判定](process-evaluations#names-are-addresses)）。 |

重命名元数据字段、判定、判据，或没有名称的判据所看的通道，会像本页其他任何重命名一样移动这些地址。

## 一个脚本可以做什么 {#what-a-script-may-do}

Logic 的变量、报警和动作脚本可以**读**这里的一切：通道数值、运行状态、命令就绪状态、预选的值、最近一次运行记录和产量点位。它们不能派发命令、不能写预选字段，也不能追加备注；那些写入属于明确的命令消费者：那些操作面板、仪表画面上的按钮和输入框、单元的自动化，以及那些代理工具。

通道点位读作[通道](#channels)里那种定了型的投影；这里的其他每个点位都读作它的普通值，后面不跟 `.Value`，这一点和变量不同：

```js
if (Process.Oven_soak.Oven_1.LastRun.Verdict === "Ok") {
  Variables.Last_serial = Process.Oven_soak.Oven_1.LastRun.Metadata.Serial;
}
```
