# 变量

> 变量编辑器逐张卡片：标识与地址、表达式和内部存储值两种种类、输出类型、实时的“数值”卡片、设置值、质量与掉电保持、那些命令，以及编辑器拒绝什么。

变量是工作站算出来或者保存着的一个带类型的值，在 `Variables` 下面按它的文件夹路径和规整过的名字寻址。在 Logic 那一栏里选中一行变量，或者在它所在文件夹或所属那一节的根的表格里按它那一行上的**打开**，就能打开它的编辑器。用“新建…”>“新建变量”，或者在一个文件夹上用“新建变量”造出来的变量不会被打开：它的那一行在那张表格里突出显示（见[新建与复制](logic#creating-and-copying)）。这个编辑器要**查看 Logic** 才读得了，要**配置 Logic** 才改得了任何字段，要**操作 Logic** 才写得了一个内部存储值。

面板标题栏显示这一行的标记（表达式标记或内部存储值标记）、名称，以及随种类而异的一行说明：“本工作站在地址空间之上用表达式算出的一个值，每个扫描周期重算一次。”或“本工作站保存的一个值：由脚本、仪表画面或你写入，任何地方都可以按地址读取。”表单那一列里放着“标识”卡片，然后是**表达式**编辑器或**内部存储值**卡片；右边那条竖栏放着**数值**，表达式变量还多一张**引用的点位**。

## 标识 {#identity}

| 字段 | 它是什么 | 取值 / 默认值 | 影响 |
| --- | --- | --- | --- |
| 名称 | 显示名称；它规整之后那一段就是地址的最后一级。 | 自由文本；新变量叫 `VariableN`。 | 通过安全重命名确认（见下）。名字留空会被拒绝（“必须填写名称。”）。 |
| 地址行 | 这个变量所对应的地址，回显在名称下面：这一节的根、那些文件夹层级、那一段。 | 只读。 | 随着你打名字跟着变；文件夹那一部分只能通过树里的**移动到**来改。 |
| 种类 | 这个值是怎么产生的。 | 表达式（默认）或内部存储值。 | 切到内部存储值会按初始值重新取值；切到表达式会把它清成一个空档，直到下一次扫描周期。 |
| 输出类型 | 每一个结果或每一次写入都被强制转换成的那个类别。 | 布尔值、整数、小数（默认）、文本、日期和时间、图片、图标、颜色。 | 改它会让一个内部存储值变量重新取值（掉电保持时把这次取值也存下来），或者把一个表达式变量清成空档；预览会在新类型下重新解析。 |
| 单位（可选） | 画在数值旁边的工程单位。 | 自由文本，默认为空。 | 显示在“数值”卡片里，脚本里可以按 `.EngineeringUnit` 读到。 |
| 说明（可选） | 自由文本。 | 默认为空。 | 对求值没有影响。 |

### 名称与地址 {#name-and-address}

“名称”字段是一个重命名字段：打字只留在本地，停顿一会儿之后字段下面会有一行预告影响（“正在检查引用…”，然后是“重命名会更新读取这个地址的 3 处位置。”“没有任何引用使用当前地址。”，或者在新名字规整成同一段时是“有效地址不会改变，没有引用需要更新。”），按回车或离开字段就确认。确认会给这个变量重命名，改写用过旧地址的每一处表达式、脚本、条件和仪表画面绑定，保住实时数值和通道绑定（通道绑的是变量稳定的键，绝不是它的名字），并报告“变量已重命名，更新了 N 处引用。”在它那一层上撞名的名字会当场被拒绝：“‘X’已经对应‘Variables.X’。归在同一个文件夹里的名称，净化之后必须得到互不相同的地址段。”书写和撞名的规则在[地址空间](logic-address-space#how-an-address-is-spelled)里。

## 表达式 {#expression}

种类是“表达式”时显示：**表达式**编辑器，带它的“浏览…”按钮和那句提示“一条在地址空间之上书写的 JavaScript 表达式（例如 Connector.Simulated.Pump.Flow.ValueToReal() * 0.1、Variables.Setpoint.Value）。如果输出类型是图片、图标或颜色，请返回一个 Assets.Images、Assets.Icons 或 Assets.Colors 地址（例如 Variables.Level.Value > 50 ? Assets.Colors.Red : Assets.Colors.Green）。输入时会自动补全，也可以用“浏览…”挑一个。”这段文字每一次按键都自动保存，每半秒校验一次；有问题会显示在“数值”卡片里。语言、可读面和每一条规则都在[表达式](logic-expressions)里。种类是“内部存储值”时，表达式的文字仍然留着，但不生效。

## 内部存储值 {#memory}

种类是“内部存储值”时显示。

| 字段 | 它是什么 | 取值 / 默认值 | 影响 |
| --- | --- | --- | --- |
| 初始值 | 以文本形式给的那个初值。下面写着：“启动时按输出类型解析。留空表示没有值。” | 任何文本，默认为空。 | 在变量被创建时、在种类或输出类型改变时，以及在一个非掉电保持变量每一次启动时，按输出类型解析。留空表示没有值：变量以一个空档开始。解析不了的文字会在“数值”卡片里被标出来（“初始值无法按小数解析。”），并以一个空档起步。 |
| 掉电保持：最后写入的值在重启后仍然保留 | 写入是否被持久化。 | 默认关。 | 掉电保持的变量在写入停下大约一秒之后被存下来，而且绝不会晚于第一次未持久化写入之后的五秒；把运行时停掉会把还没落盘的都刷出去。下一次启动时，保留下来的文字按当前的输出类型解析；解析不了时，改用初始值。非掉电保持的变量在开机时回到初始值。 |

### 设置值 {#set-value}

**设置值**字段和它的**写入**按钮只对拥有**操作 Logic** 的角色画出来，工作站只读时带着“运行时已停止”置灰；工程锁定不碰它们。打一个值再按“写入”：这段文字按输出类型解析；解析不了的文字被拒绝（“无法把‘abc’解析为整数。”），什么都不写；字段留空则写入“没有值”，把这个变量清回一个空档。这次写入搭的是那唯一的[出口](logic#writing-values)：一个正在跑的配方或者一个联锁占着这个变量时它被拒绝（“‘Variables.X’被‘`<holder>`’写保护。”），以“从旧值到新值”按来源“操作员”记下来，在逻辑队列上被应用，而且在变量是掉电保持时被标记为需要保存。字段下面的一行，在值写入时写“值已写入。”，被拒绝时写出原因（上面的任一种拒绝），和 Connector 里标签的写入回答方式一样。这个字段不是读数显示；“数值”卡片才是。

内部存储值变量也可以从工作站外面写入：只要[Modbus 服务器](connector-modbus-server)映射里有一行允许写入的行提供它，它就会经过同一个出口接收 Modbus 客户端的写入，并以来源 `Modbus client <address>:<port>` 记下来。表达式变量也可以被提供，但只读。

## 输出类型 {#output-types}

| 输出类型 | 装着什么 | 说明 |
| --- | --- | --- |
| 布尔值 | true 或 false | 可以通过通道记录，也可以画成曲线 |
| 整数 | 一个 64 位整数 | 可以记录，也可以画成曲线 |
| 小数 | 一个双精度实数 | 可以记录，也可以画成曲线；读数按工作站的小数位数显示 |
| 文本 | 自由文本 | |
| 日期和时间 | 一个时刻 | 按工作站的日期和时间格式显示 |
| 图片 | 一个素材 id，以文本形式 | 呈现类输出：表达式返回一个 `Assets.Images.*` 地址 |
| 图标 | 一个字形或一段 SVG 标记，以文本形式 | 呈现类输出：返回一个 `Assets.Icons.*` 地址 |
| 颜色 | 一个十六进制颜色，以文本形式 | 呈现类输出：返回一个 `Assets.Colors.*` 地址 |

强制转换和应用其余部分用的是同一套宽容转换：文本被解析，数字被舍入或转换，转换不了的就变成没有值。呈现类型的内部存储值变量保存的是你写进去的那个字面量（一个素材 id、一个字形、一串十六进制）。这三种呈现类输出是为仪表画面准备的：绑上去的[图片](component-image)、[图标](component-icon)或[图元](component-symbol)组件会画出这个变量此刻解析出来的东西；它们返回的那些地址列在[资源](logic-assets#what-a-logic-address-can-name)里。

## “数值”卡片 {#the-value-card}

**数值**那条竖栏显示当前的值，每秒刷新两次：标量类型显示读数并在旁边配单位，图片显示那张图片，图标按尺寸显示字形，颜色显示色块并配它的十六进制值。空档画成一道短横。对表达式变量，这张卡片会预览你正在打的那份草稿，用当前的值求出来，早于扫描周期把它接过去；表达式无效期间读数保持短横，校验提示出现在卡片里（撞名也报在这里，不过一个坏名字绝不会让预览停下来）。标量读数下面卡片写着“要记录这个变量，请在 Process > 定义 > 通道下给它绑定一个通道。”；绑定是在一个 Process 模型的[通道](process-channels)界面上做的。

对表达式变量，**引用的点位**卡片列出这个表达式点名的每一个地址以及它当前的读数，地址不再解析得出来时写“?”，这样一个不可用的输入一眼就能看见。

## 质量 {#quality}

一个变量的值要么是一个带类型的值，要么根本没有值（一个空档）。一个点位只有在它自己的值存在时才是良好：内部存储值变量一旦保存着一个值就是良好，而一个空的初值在第一次真正写入之前都是空档；表达式变量带的是它最近一次求值的质量，所以只要有任何被引用的点位缺失、不可用或者没有值，这个表达式就不求值，变量成为一个质量不可用的空档。一次空的读数绝不会悄悄变成零。脚本用 `.State()` 读质量；绑在这个变量上的通道把空档就记成空档。

## 使用者 {#used-by}

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

| 种类 | 位置 | 名称 |
| --- | --- | --- |
| Modbus 服务器 | 空 | [Modbus 服务器](connector-modbus-server)寄存器映射里提供这个变量的那一行，以寄存器类别和手册中的地址表示（`保持寄存器（4xxxx） 40101`） |
| OPC UA 服务器 | 空 | 通过 OPC UA 服务器[提供的节点](connector-opcua-served-nodes)里提供这个变量的那一行，客户端可以做什么：只读或读写 |
| MQTT 发布 | Broker | Broker 的[发布卡片](driver-mqtt#the-publish-card)里发布这个变量的那一行的主题 |
| 变量、动作、报警 | 空 | 文本里写着这个变量地址的表达式变量、动作或报警，并带上它所在的文件夹 |
| 仪表画面 | 那个仪表画面 | 地址属性点名这个变量的组件 |

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

## 命令 {#commands}

| 命令 | 作用 | 何时置灰 | 何时不画出来 |
| --- | --- | --- | --- |
| 复制 | 把这个变量复制到 Variables 根下面一个空闲的名字上（`Name (2)`……），副本不会被打开，而是在这个根的表格里突出显示（见[新建与复制](logic#creating-and-copying)）；副本有一个全新的键，所以还没有通道绑在它上面。 | 工作站被锁住或只读。 | 角色没有“配置 Logic”。 |
| 删除变量 | 先问“要删除‘X’吗？这会把这个变量拿掉。指向它地址的引用会变成悬空。 此操作无法撤销。”然后连同它保留下来的值一起删掉。 | 工作站被锁住或只读。 | 角色没有“配置 Logic”。 |
| 移动到（右键） | 把这个变量归到另一个文件夹里或者根上；地址和每一处引用都跟着走。 | 工作站被锁住或只读。 | 角色没有“配置 Logic”。 |

变量没有启用或禁用：它要么一直被求值，要么一直被保存着。

## 编辑器拒绝什么 {#what-the-editor-refuses}

- 空名字，或者在它那一层上那一段已经被占用的名字。
- 不是一条只读 JavaScript 表达式的表达式、引用了不存在的点位或者另一个表达式变量的表达式，以及试算不通过的表达式（确切的提示在[表达式](logic-expressions#what-validation-refuses)里）。
- 在输出类型下解析不了的初始值或“设置值”，以及任何是 NaN 或无穷的值。
- 工作站被一次运行锁住或者已停止期间的任何改动：字段会重新载入成工作站里的内容，记录条会说明原因。

## 编辑器不做什么 {#what-the-editor-does-not-do}

它不画曲线也不记录（那是通道的事），不写点位（脚本或者仪表画面控件把一个内部存储值往下送），不引用别的表达式变量，不保留任何数值历史，也不会让一个非掉电保持的值挺过一次重启。
