# Simulated

> 应用内合成的信号，用来在没有硬件时做测试、培训和搭建仪表画面。

Simulated 驱动在应用内部产生合成信号，既不牵涉仪器，也不牵涉网络。它是一个正经的驱动，不是一条捷径：它的设备走的是和真实硬件完全一样的运行时通路，一样地登记、轮询和缓存，所以曲线、数据类型、质量指示和写入行为都和在现场时一模一样。用它来试 [Connector](connector) 的模型、在设备到货之前先搭仪表画面，或者布置一次演示。

## 设备 {#device}

仿真设备没有连接字段，只有它的名称和默认的**轮询间隔（ms）**（默认 1000）。它的基础 URI 是 `simulated://<device>`；设备那一段只是说明性的，因为所有行为都住在点位上。

## 设备发现 {#discovery}

**发现设备**不需要目标，它列出内置的那份目录：每一种原型各给一个可以直接添加的实例，点位也在内。添加之前，把你不想要的通道取消勾选。

| 型号 | 实例 | 通道 |
| --- | --- | --- |
| Universal Controller | UC-01 | ProcessValue（正弦）、SetPoint（可写的设定值，默认 50）、Output（斜坡）、RunStatus（Run/Stop/Pause/Fault） |
| Electrical Power Meter | PM-01 | Voltage、Current、ActivePower 和 Frequency（分别围绕 230、10、2300 和 60 的随机游走，每 250 毫秒轮询一次）、PowerFactor（正弦） |
| Production Counter | CNT-01 | Count（Int32 计数器）、Rate（围绕 100 的随机游走）、Running（来回翻转的布尔） |
| Status Panel | STS-01 | State（Run/Stop/Pause/Fault）、Alarm（来回翻转的布尔）、Uptime（Int64 计数器） |
| Type Showcase | SHOW-01 | 每一种 OPC UA 内置类型各一个通道，一共十五个，用来把每一种数值显示都练一遍 |

从目录添加的设备可以在自己的页面上找回你删掉的通道：“点位”卡片标题栏里的**发现点位**列出它的型号中它还没有点位的通道。手工创建的设备没有型号，也就没有“发现点位”（见[发现一台设备的点位](connector-discovery#discovering-a-devices-tags)）。

## 点位 {#tags}

仿真点位没有地址要打：点位的名字就是那个点。每一种 OPC UA 内置数据类型都可用，三种读写方式也都可用。这个点位产出什么，由“信号源”卡片上的**信号种类**决定；选择器只提供和这个点位数据类型相容的那些形状，而改数据类型会把不相容的形状对齐到一个合理的默认值。读写方式允许写入的点位，只会拿到接受写入的那两种形状，也就是设定值保持和脉冲；改读写方式也会照同样的办法把形状对齐。

| 信号种类 | 产出什么 | 相容的类型 | 可写 |
| --- | --- | --- | --- |
| **正弦** | 一条带轻微涟漪的平滑波，种子取自点位名称，所以名字不同的点位动得不一样，而重启之后又能重现同一条曲线 | 数值 | 否 |
| **斜坡** | 一条三角波（先上后下），周期 10 到 59 秒，种子取自点位名称 | 数值 | 否 |
| **随机游走** | 像真仪表那样的噪声：从上一个值走一步，并向默认值（没设时是 50）回归，步长是中心值的 1%（至少 0.5） | 数值 | 否 |
| **计数器** | 一个单调递增的整数：从默认值（没设时是 0）开始，每读一次加 1，所以速率跟着轮询间隔走 | 数值 | 否 |
| **布尔翻转** | 每秒交替一次的布尔量 | 布尔 | 否 |
| **枚举文本** | 一段在一份固定状态列表里循环的文本，每 3 秒走一步 | 文本 | 否 |
| **时钟** | 当前时间，每读一次采一次 | 日期时间 | 否 |
| **随机 GUID** | 每读一次给一个新的随机 Guid | Guid | 否 |
| **随机字节** | 每读一次给一段 8 字节的随机负载 | ByteString | 否 |
| **设定值保持** | 保持一个默认值，直到被写；一次写入之后，后面每一次读都答那个写进去的值 | 任何类型 | 是 |
| **脉冲** | 一个瞬时点：平时停在基线上，一次写入会把写进去的值保持指定的秒数，然后自己落回去 | 任何类型 | 是 |

创建时没有明确指定种类的点位会拿到一个活泼的默认值：可写的点位变成**设定值保持**（这样写入才留得住），只读的点位拿到一个和类型相配的产生器（数值 → 正弦，布尔 → 布尔翻转，文本 → 枚举文本，日期时间 → 时钟，Guid → 随机 GUID，ByteString → 随机字节）。**脉冲**永远不会是默认值：一个把写进去的值还回来、随后又把它扔掉的点，是一个刻意的选择。例外是地址里写明了 `default` 值的只读点位（例如 `?default=true`）：它同样变成**设定值保持**，并回答这个值，因为产生器会忽略它。

### 面板里的参数 {#parameters-in-the-panel}

有两个参数直接在“信号源”卡片上编辑：

| 字段 | 哪些种类显示 | 含义 |
| --- | --- | --- |
| **默认值** | 随机游走、计数器、设定值保持 | 游走的中心、计数器的起始值，或者在有东西写进来之前设定值答的那个数。输入控件跟着点位类型走（布尔是勾选框，日期时间是日期选择器，其余是文本）。 |
| **状态** | 枚举文本 | 用分号分隔、循环走一遍的那份列表，例如 `Run;Stop;Pause`。留空或者一项都没有时，退回 `Run;Stop;Pause`。 |

### 高级信号参数 {#advanced-signal-parameters}

其余那些形状参数以查询参数的形式带在点位保存下来的地址上（例如 `ProcessValue?min=20&max=80&period=30`）。面板不编辑它们，但在每一次编辑中都把它们保留下来；随应用一起种下的例子用到它们，而代理可以通过[代理](agent)那一面的点位工具来设置它们。

| 参数 | 适用于 | 含义 | 不写时 |
| --- | --- | --- | --- |
| `min`、`max` | 正弦 | 波形运行的那个幅带；输出会被钳位到这个带里 | 一个种子取自点位名称的幅带（幅值 10 到 49，围绕 20 到 49 的偏置），不做钳位 |
| `period` | 正弦 | 波形周期，单位秒 | 一个种子取自点位名称的频率（0.05 到 0.24 Hz） |
| `phase` | 正弦 | 相位偏移，单位度 | 种子取自点位名称 |
| `default` | 随机游走、计数器、设定值保持 | 和面板上“默认值”字段编辑的是同一个值 | 50 / 0 / 该类型的默认值 |
| `values` | 枚举文本 | 和面板上“状态”字段编辑的是同一份列表 | `Run;Stop;Pause` |
| `baseline` | 脉冲 | 两次发生之间这个点停在的那个值，按点位的类型解析 | 该类型的零值（日期时间是当前时间，文本是空） |
| `hold` | 脉冲 | 一次写入引起的发生持续多少秒 | 10 秒 |
| `warmup` | 任何种类 | 连上之后到第一次读答话之间的秒数，像一台需要稳定一会儿的仪器；这段等待由轮询循环吸收掉 | 无 |
| `outage` | 任何种类 | 每个周期末尾点位读取失败的秒数，像一台不再应答的设备；该设备的其他点位照常应答，写入也照常到达 | 无 |
| `outageEvery` | 任何种类 | 一个中断周期持续多少秒，从连上时算起：点位在每个周期开头应答，在最后 `outage` 秒内失败；中断与周期一样长时，点位一直失败 | 60 秒 |

`outage` 让你不用任何现场设备，就能看到设备不应答时点位会怎样：`?outage=5&outageEvery=30` 应答 25 秒，读取失败 5 秒，然后从头再来。点位按 [设备没有回答](connector-value-pipeline#the-device-did-not-answer) 所述应用它选定的策略，读取失败会把声明的中断列为原因。

## 写入 {#writes}

- **设定值保持**接受写入，从那以后就用写进去的值来应答。保持住的那个值只在设备保持连接期间存在于内存里；它不会存进配置，所以把设备禁用掉或者把应用重启一次，这个点就回到它的默认值。
- **脉冲**把一次写入当作一段发生：它用写进去的值应答，直到保持时间走完，然后落回它的基线。一段发生期间的第二次写入会把它整个替换掉，所以按住一个按钮是把保持时间重新起算，而不是叠成两段。
- 其余每一种种类都拒绝写入；一次被尝试的写入按点位只记一条诊断日志，而不是每一轮轮询都往日志里灌。

和任何驱动一样，写入字段接收的是工程值，原始值到达信号之前会先把[数值转换链](connector-value-pipeline)反过来走一遍。

## 值得知道的限制 {#limits-worth-knowing}

- 数值是每一次运行现合成的。确定性的形状（正弦、斜坡、时钟）在重启之后重现同一条曲线；带状态的那些（计数器、随机游走、设定值保持、脉冲）则从头开始。
- 把一种信号种类和一种不相容的数据类型配在一起（只有通过程序化配置才做得到，面板会约束选择器），会让这个点位每一次读取都失败，并只记一条警告到诊断日志里。
