# 在线实时读值 · 使用指南（只读，不改 PLC）

> 这份指南讲怎么用本工具**在线读取 PLC 当前的实时变量值**——比如"现在小车坐标多少""心跳是多少""急停有没有触发"。
> **全程只读**：只看不写，不下发、不强制、不改 CPU 运行模式，对在跑的设备安全。
> 适合：会用博途、想用 AI 快速查变量当前值的工程师。

---

## 一、一句话原理

PLC 工程接口（TIA Openness）是"改项目"用的，**读不到实时值**。实时值要走**运行时通道直连 CPU**：

```
                 ┌── S7 协议(端口102) ───────┐
 AI / MCP 服务 ──┤── OPC UA(端口4840) ───────┤──→  直连 CPU 读当前值
                 └── 监控表(取地址)+S7 读值 ──┘
```

所以**读值这件事跟"项目开没开"无关**（详见第五节）。

---

## 二、四个工具，分别干嘛

| 工具 | 干什么 | 什么时候用 |
|---|---|---|
| **ReadPlcLiveValuesS7** | 最快。给绝对地址直接读 CPU | 知道地址、要快(几十~几百 ms)。首选 |
| **ReadPlcLiveValuesOpcUa** | 按 OPC UA NodeId 读 | 想用符号名读、或读结构体/数组 |
| **MonitorWatchTableLiveS7** | 读你在博途建好的"监控表"里的那些点 | 已经建好监控表，想一次性读整张表 |
| **ProbeS7CpuIdentity** | 只连一下、读 CPU 型号/序列号 | 下读之前先确认 IP 是不是目标 PLC |
| **TraceTagCause** | 离线分析"某个变量为什么是这个值/被谁写的" | 排查"故障码为啥=1"这类因果问题 |

> 还有个 `ReadPlcWatchTableCurrentValuesReadOnly` 是经 Openness 读监控表条目定义（地址/名称/格式），它**拿不到当前值**——当前值要靠上面的 S7/OPC UA。

---

## 三、地址怎么写

### S7 绝对地址（ReadPlcLiveValuesS7）
| 写法 | 含义 |
|---|---|
| `M0.0` | 位存储器 M0.0 (Bool) |
| `MW12` / `MD14` | M 字 / 双字 |
| `DB34.DBX98.1` | DB34 第98字节第1位 (Bool) |
| `DB34.DBD116:DINT` | DB34 偏移116 的双字，按**有符号**整型读 |
| `DB10.DBD0:REAL` | DB10 偏移0 的双字，按**浮点**读 |
| `IW76` / `QW72` | 输入字 / 输出字 |

`:类型`后缀可选：`REAL`(浮点)、`DINT/INT/SINT`(有符号)、`DWORD/WORD/BYTE`(无符号)。不写则按地址宽度默认无符号。

### OPC UA NodeId（ReadPlcLiveValuesOpcUa）
见第六节"NodeId 是什么"。常用两种：
- 字符串(不用建接口)：`ns=3;s="数据"."DATA"`
- 数字(服务器接口自动分配)：`ns=4;i=13`

---

## 四、PLC 上需要先满足的前提

### 用 S7 读（端口 102）
1. CPU 属性 → 防护与安全 → 连接机制 → **✅ 允许来自远程对象的 PUT/GET 通信访问**。
2. 要读的 **DB 必须是"非优化"**（标准访问）才能按绝对偏移读；优化 DB 没有固定偏移，读不了。
   - DB 属性 → 取消勾选"优化的块访问"。
   - **M / I / Q 区不受此限制**，随便读。

### 用 OPC UA 读（端口 4840）
1. CPU 属性 → **OPC UA → 服务器 → ✅ 激活 OPC UA 服务器**。
2. 1200/1500 需要一个 **SIMATIC OPC UA 运行许可证**（否则只能组态、跑不起来）。
3. 要暴露哪些变量：可走标准命名空间(默认就有)，或自建"服务器接口"(见第六节)。

---

## 五、不打开项目，能读到值吗？

**能——只要你知道"地址 / NodeId"。**

| 方法 | 不开项目能用？ | 说明 |
|---|---|---|
| S7 `ReadPlcLiveValuesS7` | ✅ 能 | 直连 CPU，只认绝对地址，跟 TIA 无关 |
| OPC UA `ReadPlcLiveValuesOpcUa` | ✅ 能 | 直连 CPU，只认 NodeId，跟 TIA 无关 |
| 监控表 `MonitorWatchTableLiveS7` | ⚠️ 取地址那步要开项目 | 它先用 Openness 从项目里拿监控表的地址，再 S7 读值 |
| 溯因 `TraceTagCause` | ❌ 要开项目 | 要导出代码块解析 |

**关键区分：PLC 里只有"地址和值"，没有符号名。**
`Rec_Data.HeartBeat` 这种名字只存在于 TIA 项目里；CPU 那头只知道 `%DB34.DBD116`。
- **读值**永远不用开项目；
- **需要项目的，只是"把符号名翻译成地址"**这一步。翻译一次记下来，以后就不用项目了。

> 小技巧：让 AI 帮你把常监控的点导成一张"符号名 ↔ 地址 ↔ NodeId"对照表，以后项目关着也能照表直接读。

---

## 六、NodeId 是什么 & 在 PLC 哪里配

NodeId 是 OPC UA 里**一个值的"地址"**，作用跟 S7 的 `%DB34.DBD116` 一样，只是换了写法：

```
ns=4 ; i=13
└┬┘   └┬─┘
命名空间  标识符(i=数字 / s=字符串)
```

西门子 PLC 上 NodeId 有两种来源：

**① 标准命名空间（不用配，自动有）**
开了 OPC UA 服务器后，每个变量自动有个**字符串 NodeId**＝它的符号路径：
```
ns=3;s="数据"."DATA"
ns=3;s="5T_Crane_ComData"."Rec_Data"."HeartBeat"
```
知道符号名就能拼出来，**不用建接口**。

**② 服务器接口（你挑变量，数字 NodeId 自动给）**
项目树 → PLC → **OPC UA 通信 → 服务器接口 → 添加新的服务器接口** → 把要暴露的变量拖进去。TIA 会**自动分配**数字 NodeId（`ns=4;i=13`）。
> 数字 NodeId 不是手填的——你配的是"暴露哪些变量"，NodeId 是系统生成的结果。

**怎么查到实际 NodeId 数值**
- 用客户端 **browse**（浏览）CPU——**不用开项目**，直接问 CPU 要节点列表；
- 或 TIA 里"服务器接口 → 导出"，XML 里列了每个节点的 NodeId。

---

## 七、典型用法示例

**查当前坐标 / 频率（最快，S7）**
> 用 ReadPlcLiveValuesS7 读 192.168.0.32 的 `DB34.DBD116:DINT`、`DB34.DBD120:DINT`、`DB34.DBX98.1`

**读监控表整张表（S7）**
> 用 MonitorWatchTableLiveS7 读 安全PLC 的 监控表_1，IP 192.168.0.32

**用 OPC UA 读你暴露的数据**
> 用 ReadPlcLiveValuesOpcUa 读 opc.tcp://192.168.0.32:4840 的 `ns=4;i=13`

**排查"故障码为什么=1"**
> 用 TraceTagCause 分析 安全PLC 的 "故障码"，拿到写入网络和门控条件，再用 ReadPlcLiveValuesS7 读这些条件看当前哪个为 True

---

## 八、安全保证（为什么敢对在跑的设备用）

- 四个读值工具**只调读服务**：S7 的 `ReadArea`/`GetCpuInfo`、OPC UA 的 `Read`。**从不**调 `WriteArea`/写/强制/启停。
- 返回里自带 `safety:{readOnly:true, writesValues:false, usesForce:false, changesCpuMode:false}` 自证。
- `expectModuleContains` 身份护栏：填型号(如 `1211C`)，读之前先核对 CPU 型号，型号对不上直接中止，避免误读到别的 PLC。
- 已在真机（安全PLC，CPU 1211C @192.168.0.32）实测：读 M/I/Q、非优化 DB、OPC UA 节点，**无任何值或运行模式改变**。

---

## 九、性能与小常识

- **S7**：单次往返几十~几百 ms，最快。适合频繁查。
- **OPC UA**：会话**按 endpoint 缓存复用**——首次建链约 1.7 s，之后每次约 150~220 ms。所以别担心"每次都慢"，慢的只有第一次。
- **心跳类计数器**每次读都不一样是正常的（它本来就在自增）。
- OPC UA 浏览/读取**不需要 Runtime 许可证之外的东西**，但服务器本身要那张许可证才能开。

---

## 十、读不到？对照排查

| 现象 | 原因 / 处理 |
|---|---|
| S7 连不上(端口102) | CPU 没开 PUT/GET；或 IP/网段不通；或防火墙挡了 102 |
| S7 读 DB 报 "Address out of range" | DB 不存在，或读了优化 DB（改成非优化），或偏移超界 |
| S7 读出来符号是对的但值像乱的 | 类型选错：有符号量要加 `:DINT/:INT`，浮点要 `:REAL` |
| OPC UA 连不上(端口4840) | 没激活 OPC UA 服务器，或没装 OPC UA 许可证 |
| OPC UA 某节点 "Bad status" | NodeId 写错，或该变量没暴露到接口/标准命名空间 |
| 监控表读出 0 条 | 表是空的，或项目没打开（取地址需要 Openness） |
| 身份护栏中止 | CPU 型号和 `expectModuleContains` 对不上——确认 IP 是不是目标 PLC |

---

## 附：与博途监控表的核对结论（可信度参考）

在真机上用三种方法交叉核对过博途监控表/DB 的实时显示，结果一致：

- **DB15 数据数组**：同一时刻 S7 与 OPC UA **逐字节一致**（如 `DA C1 56 1F 53 01 04 E0`），与博途监控表显示相同。
- **DB34 Rec_Data**：`EMG_STOP`=True、`DownAllow`=1 与博途一致；`HeartBeat` 是自增心跳，读到的是当时的瞬时值。
- **同一时刻** S7 读 `DB34.DBD116` 与 OPC UA 读 `Rec_Data.HeartBeat` **数值完全相同**，证明两条通道读的是同一份实时数据。

> 相关安全策略见 [`online-monitoring-safety.md`](online-monitoring-safety.md)，Openness 能力边界见 [`openness-limitations.md`](openness-limitations.md)。
