# S7 HTTP 接口说明 ## 目标 本服务通过 HTTP 接口连接 Siemens S7 TCP 设备,支持连接组合扫描、原始字节读取、点位读取并转换。 当前 S7 接口使用 `python-snap7` 实现。`/s7/read` 返回的 `communication` 是语义化通信记录,用于说明本次连接、读取和断开过程;它不是 Wireshark 中的 `TPKT + COTP + S7Comm` 原始网络帧。 ## 接口列表 - `POST /api/dc-debugtool/s7/read`:读取一段 S7 地址数据,只返回语义化 `communication`。 - `POST /api/dc-debugtool/s7/read_points`:按点位定义读取,并返回转换后的业务值。 - `POST /api/dc-debugtool/s7/connect_scan`:传 `ip`,可选 `device_type`,扫描可用的 `rock`、`slot`、`tsap_conn_type` 组合。 ## 通用返回约定 业务接口统一返回 HTTP `200`,是否成功由 JSON 中的 `code` 判断。 成功: ```json { "code": 0, "msg": "success", "data": {} } ``` 失败: ```json { "code": 1, "msg": "TCP : Unreachable peer", "data": {} } ``` ## S7 通用设备字段 以下字段用于 `/s7/read` 和 `/s7/read_points`。 | 字段 | 是否必传 | 默认值 | 校验 | 含义 | |---|---:|---|---|---| | `device_type` | 否 | `S7-1200` | `S7-1200`、`S7-Smart200` | S7 设备类型。两者连接方式均为 S7 TCP,`S7-Smart200` 额外支持 `V` 区。 | | `ip` | 是 | 无 | 合法 IP 地址 | S7 设备 IP 地址。 | | `port` | 否 | `102` | `1..65535` | S7 TCP 端口,通常固定为 `102`。 | | `rock` | 是 | 无 | `0..31` | PLC rock。 | | `slot` | 是 | 无 | `0..31` | PLC slot。 | | `tsap_conn_type` | 否 | `PG` | `PG`、`OP`、`BASIC` | Snap7 连接类型。 | `tsap_conn_type` 大小写不敏感,服务内部统一转为大写。 连接类型映射: | tsap_conn_type | Snap7 值 | |---|---:| | `PG` | `0x01` | | `OP` | `0x02` | | `BASIC` | `0x03` | ## S7 地址字段 | 字段 | 是否必传 | 默认值 | 校验 | 含义 | |---|---:|---|---|---| | `area` | 是 | 无 | `DB`、`M`、`I`、`Q`、`V` | 读取区域。`V` 仅支持 `device_type=S7-Smart200`。 | | `db` | `area=DB` 时必填 | `0` | `>=0` | DB 块号。`area=DB` 时必须大于 `0`;`area=V` 时内部按 `DB1` 读取。 | | `start` | 是 | 无 | `>=0` | 起始字节偏移。 | | `size` | `/s7/read` 必填 | 无 | `1..65535` | 读取字节数。 | 区域说明: | area | 含义 | |---|---| | `DB` | 数据块 Data Block | | `M` | Merker 标志位区 | | `I` | 输入区 Inputs | | `Q` | 输出区 Outputs | ## communication 格式 `communication` 是字符串数组,记录连接、读取、断开过程。该字段只在 `/api/dc-debugtool/s7/read` 返回,`/api/dc-debugtool/s7/read_points` 不返回该字段。 示例: ```text Tx:S7_CONNECT ip=192.168.1.10 port=102 rock=0 slot=1 tsap_conn_type=PG Rx:S7_CONNECTED pdu_length=480 Tx:S7_READ area=DB db=1 start=0 size=4 Rx:11 22 33 44 Tx:S7_DISCONNECT Rx:S7_DISCONNECTED ``` 说明: | 片段 | 含义 | |---|---| | `Tx:S7_CONNECT` | 调试工具准备发起 S7 连接。 | | `Rx:S7_CONNECTED` | S7 连接成功,可能包含协商后的 `pdu_length`。 | | `Tx:S7_READ` | 调试工具准备读取指定区域、DB、起始偏移和字节数。 | | `Rx:11 22 33 44` | 读取返回的原始数据字节,十六进制大写,空格分隔。 | | `Tx:S7_DISCONNECT` | 调试工具准备断开连接。 | | `Rx:S7_DISCONNECTED` | 连接已断开。 | | `Rx:S7_ERROR` | 连接或读取失败,后面带错误消息。 | ## 接口一:原始读取 ### URL ```http POST /api/dc-debugtool/s7/read ``` ### 请求体 ```json { "device_type": "S7-1200", "ip": "192.168.1.10", "port": 102, "rock": 0, "slot": 1, "tsap_conn_type": "PG", "read": { "area": "DB", "db": 1, "start": 0, "size": 4 } } ``` ### 成功返回 ```json { "code": 0, "msg": "success", "data": { "device": { "device_type": "S7-1200", "ip": "192.168.1.10", "port": 102, "rock": 0, "slot": 1, "tsap_conn_type": "PG" }, "communication": [ "Tx:S7_CONNECT ip=192.168.1.10 port=102 rock=0 slot=1 tsap_conn_type=PG", "Rx:S7_CONNECTED pdu_length=480", "Tx:S7_READ area=DB db=1 start=0 size=4", "Rx:11 22 33 44", "Tx:S7_DISCONNECT", "Rx:S7_DISCONNECTED" ] } } ``` ### 错误返回 ```json { "code": 1, "msg": "TCP : Unreachable peer", "data": { "device": { "device_type": "S7-1200", "ip": "192.168.1.10", "port": 102, "rock": 0, "slot": 1, "tsap_conn_type": "PG" }, "communication": [ "Tx:S7_CONNECT ip=192.168.1.10 port=102 rock=0 slot=1 tsap_conn_type=PG", "Rx:S7_ERROR message=TCP : Unreachable peer" ] } } ``` ## 接口二:点位读取并转换 ### URL ```http POST /api/dc-debugtool/s7/read_points ``` ### 请求体 ```json { "device_type": "S7-1200", "ip": "192.168.1.10", "port": 102, "rock": 0, "slot": 1, "tsap_conn_type": "PG", "points": [ { "area": "DB", "db": 1, "start": 0, "type": "float32" }, { "area": "DB", "db": 1, "start": 4, "type": "bool", "bit": 0 } ] } ``` ### 支持的数据类型 | 类型 | 读取长度 | 说明 | |---|---:|---| | `bool` | 1 byte | 布尔值。传 `bit` 时读取指定 bit;不传 `bit` 时整个字节非 0 为 `true`。 | | `byte` | 1 byte | 无符号 8 位整数。 | | `int8` | 1 byte | 有符号 8 位整数。 | | `int16` | 2 bytes | 有符号 16 位整数。 | | `uint16` | 2 bytes | 无符号 16 位整数。 | | `int32` | 4 bytes | 有符号 32 位整数。 | | `uint32` | 4 bytes | 无符号 32 位整数。 | | `float32` | 4 bytes | IEEE 754 单精度浮点数。 | | `int64` | 8 bytes | 有符号 64 位整数。 | | `uint64` | 8 bytes | 无符号 64 位整数。 | | `float64` | 8 bytes | IEEE 754 双精度浮点数。 | S7 多字节数值按大端字节序解析。 ### 成功返回 ```json { "code": 0, "msg": "success", "data": { "device": { "device_type": "S7-1200", "ip": "192.168.1.10", "port": 102, "rock": 0, "slot": 1, "tsap_conn_type": "PG" }, "points": [ { "area": "DB", "db": 1, "start": 0, "type": "float32", "value": 12.34 }, { "area": "DB", "db": 1, "start": 4, "type": "bool", "value": true, "bit": 0 } ] } } ``` ## 接口三:连接扫描 ### URL ```http POST /api/dc-debugtool/s7/connect_scan ``` ### 请求体 只需要传入设备 IP。端口固定使用 S7 TCP 默认端口 `102`。 ```json { "device_type": "S7-Smart200", "ip": "192.168.1.10" } ``` ### 扫描范围 | 字段 | 扫描值 | |---|---| | `rock` | `0`、`1`、`2` | | `slot` | `0`、`1`、`2` | | `tsap_conn_type` | `PG`、`OP`、`BASIC` | 总计扫描 `27` 种组合。接口只返回可以连接成功的组合。 ### 成功返回 ```json { "code": 0, "msg": "success", "data": { "device": { "device_type": "S7-Smart200", "ip": "192.168.1.10", "port": 102 }, "available": [ { "rock": 0, "slot": 1, "tsap_conn_type": "PG" }, { "rock": 0, "slot": 1, "tsap_conn_type": "OP" } ] } } ``` 如果没有任何组合可连接,`available` 为空数组: ```json { "code": 0, "msg": "success", "data": { "device": { "device_type": "S7-Smart200", "ip": "192.168.1.10", "port": 102 }, "available": [] } } ``` ### 调用示例 ```bash curl -X POST http://127.0.0.1:8000/api/dc-debugtool/s7/connect_scan \ -H "Content-Type: application/json" \ -d '{ "device_type": "S7-Smart200", "ip": "192.168.1.10" }' ``` ## 运行方式 ```bash uvicorn main:app --host 0.0.0.0 --port 8000 ``` ## 验证方式 编译检查: ```bash python -m compileall app main.py ``` S7 接口测试: ```bash python -m unittest discover -s intergration/s7 -p "test_*.py" ``` S7 测试会在本机启动 `python-snap7` 模拟 server,用于验证 `/s7/read` 和 `/s7/read_points`;`/s7/connect_scan` 使用 mock 验证固定端口 `102` 和只返回可用组合的逻辑。