REST API 参考
概述
OpenPLC Runtime v4 提供了一个内部 REST API,通过 HTTPS 协议提供服务,供 OpenPLC Editor 桌面应用程序使用。该 API 并非设计为供最终用户直接交互,但可用于高级集成或诊断。
所有端点均可通过 https://<主机>:8443/api/<端点> 地址访问。
基础 URL
https://localhost:8443/api
认证
运行时使用基于 JWT 的认证:
- 首次创建用户 :
POST /api/create-user(创建第一个用户无需认证) - 登录 :
POST /api/login返回 JWT 访问令牌 - 认证请求 :所有其他端点都需要
Authorization: Bearer <令牌>请求头
注意:OpenPLC Editor 会自动处理认证。高级集成者必须手动实现认证流程。
通用响应格式
所有 API 响应均为 JSON 对象。成功响应通常包含一个 status 字段,而错误响应则包含描述性的错误消息。
认证端点
创建用户
创建新的用户账户。第一个用户可以在没有认证的情况下创建。后续创建用户需要 JWT 认证。
请求:
http
POST /api/create-user
Content-Type: application/json
{
"username": "admin",
"password": "your_password",
"role": "admin"
}
响应(成功):
json
{
"msg": "用户已创建",
"id": 1
}
响应(错误):
json
{
"msg": "用户名已存在"
}
状态码:
201 Created- 用户创建成功400 Bad Request- 缺少用户名或密码401 Unauthorized- 用户已存在且未提供有效的 JWT409 Conflict- 用户名已存在
登录
进行身份验证并获取 JWT 访问令牌。
请求:
http
POST /api/login
Content-Type: application/json
{
"username": "admin",
"password": "your_password"
}
响应(成功):
json
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
响应(错误):
json
"用户名或密码错误"
状态码:
200 OK- 登录成功401 Unauthorized- 凭据无效500 Internal Server Error- 数据库错误
注意事项:
- 访问令牌应包含在所有后续请求的
Authorization: Bearer <令牌>请求头中 - 令牌在配置的持续时间(默认:24 小时)后过期
PLC 控制端点
所有 PLC 控制端点都需要 JWT 认证。
启动 PLC
启动 PLC 程序执行。
请求:
http
GET /api/start-plc
Authorization: Bearer <令牌>
响应:
json
{
"status": "PLC 启动成功"
}
可能的 Status 值:
"PLC 启动成功"- PLC 已切换到 RUNNING 状态"PLC 已在运行"- PLC 已处于 RUNNING 状态"未加载 PLC 程序"- 没有可用的已编译程序"运行时无响应"- 运行时进程无响应
停止 PLC
停止 PLC 程序执行。
请求:
http
GET /api/stop-plc
Authorization: Bearer <令牌>
响应:
json
{
"status": "PLC 停止成功"
}
可能的 Status 值:
"PLC 停止成功"- PLC 已切换到 STOPPED 状态"PLC 已停止"- PLC 已处于 STOPPED 状态"运行时无响应"- 运行时进程无响应
获取 PLC 状态
查询当前的 PLC 状态。
请求:
http
GET /api/status
Authorization: Bearer <令牌>
响应:
json
{
"status": "RUNNING"
}
可能的 Status 值:
"EMPTY"- 未加载 PLC 程序"INIT"- 程序已加载,正在初始化"RUNNING"- 正在主动执行扫描周期"STOPPED"- 程序已加载但未执行"ERROR"- 可恢复的错误状态"运行时无响应"- 运行时进程无响应
运行时 Ping
检查运行时进程是否响应。
请求:
http
GET /api/ping
Authorization: Bearer <令牌>
响应:
json
{
"status": "pong"
}
可能的 Status 值:
"pong"- 运行时进程响应正常null- 运行时进程无响应
程序管理端点
上传 PLC 程序
上传一个包含由 OpenPLC Editor v4 生成的 PLC 程序源文件的 ZIP 文件。
请求:
http
POST /api/upload-file
Authorization: Bearer <令牌>
Content-Type: multipart/form-data
file: <ZIP 文件>
成功响应:
json
{
"UploadFileFail": "",
"CompilationStatus": "COMPILING"
}
错误响应:
json
{
"UploadFileFail": "错误消息",
"CompilationStatus": "FAILED"
}
编译状态值:
"IDLE"- 无构建进行中"UNZIPPING"- 正在解压 ZIP 文件"COMPILING"- 正在运行编译脚本"SUCCESS"- 构建成功完成"FAILED"- 构建失败
错误条件:
- 请求中没有文件
- 文件过大(>10 MB 每文件,>50 MB 总计)
- ZIP 验证失败(路径遍历、压缩率、不允许的扩展名)
- 另一个编译正在进行中
- 文件系统错误
注意事项:
- 编译在后台线程中异步运行
- 使用
/api/compilation-status端点来监视进度 - PLC 在编译期间会自动停止
- OpenPLC Editor 在本地编译程序(JSON → XML → ST → C),并将源文件作为 ZIP 上传
获取编译状态
查询最近一次编译的状态。
请求:
http
GET /api/compilation-status
Authorization: Bearer <令牌>
响应:
json
{
"status": "SUCCESS",
"logs": [
"[INFO] 开始编译",
"[INFO] 正在编译 Config0.c...",
"[INFO] 正在编译 Res0.c...",
"[INFO] 正在编译 debug.c...",
"[INFO] 正在编译 glueVars.c...",
"[INFO] 正在编译 c_blocks_code.cpp...",
"[INFO] 正在编译共享库...",
"[INFO] 构建成功完成"
],
"exit_code": 0
}
响应字段:
status- 当前构建状态(IDLE, UNZIPPING, COMPILING, SUCCESS, FAILED)logs- 来自构建过程的日志消息数组exit_code- 编译脚本的退出代码(0 = 成功,非零 = 错误,null = 进行中)
注意事项:
- OpenPLC Editor 轮询此端点以监视编译进度
- 日志在编译期间累积
- 错误消息以
[ERROR]为前缀 - 退出代码在编译完成前为
null
获取运行时日志
从 PLC 运行时进程检索日志。
请求:
http
GET /api/runtime-logs
GET /api/runtime-logs?id=<最小_ID>
GET /api/runtime-logs?level=<日志等级>
Authorization: Bearer <令牌>
查询参数:
id(可选) - 要检索的最小日志 ID(用于分页)level(可选) - 按日志等级过滤(DEBUG, INFO, WARNING, ERROR)
响应:
json
{
"runtime-logs": [
{
"id": 1,
"timestamp": "2024-01-01T12:00:00.000Z",
"level": "INFO",
"message": "PLC 启动成功"
},
{
"id": 2,
"timestamp": "2024-01-01T12:00:05.000Z",
"level": "DEBUG",
"message": "扫描计数: 100"
}
]
}
日志等级:
DEBUG- 详细的诊断信息INFO- 一般信息性消息WARNING- 警告消息ERROR- 错误消息
错误处理
HTTP 状态码
200 OK- 请求成功201 Created- 资源创建成功400 Bad Request- 请求参数无效401 Unauthorized- 需要认证或令牌无效409 Conflict- 资源冲突(例如,用户名已存在)500 Internal Server Error- 服务器错误
错误响应格式
json
{
"error": "错误消息描述"
}
或对于某些端点:
json
{
"msg": "错误消息描述"
}
使用示例
完整的认证流程
cURL 示例:
bash
# 步骤 1:创建第一个用户
curl -k -X POST https://localhost:8443/api/create-user \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"admin123","role":"admin"}'
# 步骤 2:登录并获取 JWT 令牌
TOKEN=$(curl -k -X POST https://localhost:8443/api/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"admin123"}' \
| jq -r '.access_token')
echo "令牌: $TOKEN"
# 步骤 3:使用令牌进行认证请求
# 获取 PLC 状态
curl -k https://localhost:8443/api/status \
-H "Authorization: Bearer $TOKEN"
# 启动 PLC
curl -k https://localhost:8443/api/start-plc \
-H "Authorization: Bearer $TOKEN"
# 停止 PLC
curl -k https://localhost:8443/api/stop-plc \
-H "Authorization: Bearer $TOKEN"
# 上传程序
curl -k -X POST https://localhost:8443/api/upload-file \
-H "Authorization: Bearer $TOKEN" \
-F "file=@program.zip"
# 获取编译状态
curl -k https://localhost:8443/api/compilation-status \
-H "Authorization: Bearer $TOKEN"
# 获取运行时日志
curl -k https://localhost:8443/api/runtime-logs \
-H "Authorization: Bearer $TOKEN"
# Ping 运行时
curl -k https://localhost:8443/api/ping \
-H "Authorization: Bearer $TOKEN"
注意: -k 标志用于绕过自签名证书的验证。
Python 示例:
python
import requests
import urllib3
import time
# 禁用自签名证书的 SSL 警告
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
base_url = "https://localhost:8443/api"
# 步骤 1:创建第一个用户(无需认证)
response = requests.post(
f"{base_url}/create-user",
json={"username": "admin", "password": "admin123", "role": "admin"},
verify=False
)
print(f"用户创建: {response.json()}")
# 步骤 2:登录获取 JWT 令牌
response = requests.post(
f"{base_url}/login",
json={"username": "admin", "password": "admin123"},
verify=False
)
token = response.json()["access_token"]
print(f"接收到的令牌: {token[:50]}...")
# 步骤 3:使用令牌进行认证请求
headers = {"Authorization": f"Bearer {token}"}
# 获取 PLC 状态
response = requests.get(f"{base_url}/status", headers=headers, verify=False)
print(f"PLC 状态: {response.json()}")
# 启动 PLC
response = requests.get(f"{base_url}/start-plc", headers=headers, verify=False)
print(f"启动 PLC: {response.json()}")
# 上传程序
with open("program.zip", "rb") as f:
files = {"file": f}
response = requests.post(
f"{base_url}/upload-file",
files=files,
headers=headers,
verify=False
)
print(f"上传: {response.json()}")
# 监视编译
while True:
response = requests.get(
f"{base_url}/compilation-status",
headers=headers,
verify=False
)
status = response.json()
print(f"编译状态: {status['status']}")
if status["status"] in ["SUCCESS", "FAILED"]:
print("编译日志:")
for log in status["logs"]:
print(f" {log}")
break
time.sleep(1)
JavaScript/Node.js 示例:
javascript
const https = require('https');
const fetch = require('node-fetch');
const FormData = require('form-data');
const fs = require('fs');
// 禁用自签名证书的 SSL 验证
const agent = new https.Agent({ rejectUnauthorized: false });
const baseUrl = "https://localhost:8443/api";
async function main() {
// 步骤 1:创建第一个用户
let response = await fetch(`${baseUrl}/create-user`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
username: 'admin',
password: 'admin123',
role: 'admin'
}),
agent
});
console.log('用户创建:', await response.json());
// 步骤 2:登录获取 JWT 令牌
response = await fetch(`${baseUrl}/login`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
username: 'admin',
password: 'admin123'
}),
agent
});
const { access_token } = await response.json();
console.log('接收到的令牌:', access_token.substring(0, 50) + '...');
// 步骤 3:使用令牌进行认证请求
const headers = { 'Authorization': `Bearer ${access_token}` };
// 获取 PLC 状态
response = await fetch(`${baseUrl}/status`, { headers, agent });
console.log('PLC 状态:', await response.json());
// 启动 PLC
response = await fetch(`${baseUrl}/start-plc`, { headers, agent });
console.log('启动 PLC:', await response.json());
// 上传程序
const formData = new FormData();
formData.append('file', fs.createReadStream('program.zip'));
response = await fetch(`${baseUrl}/upload-file`, {
method: 'POST',
headers: { ...headers },
body: formData,
agent
});
console.log('上传:', await response.json());
// 监视编译
while (true) {
response = await fetch(`${baseUrl}/compilation-status`, { headers, agent });
const status = await response.json();
console.log('编译状态:', status.status);
if (status.status === 'SUCCESS' || status.status === 'FAILED') {
console.log('编译日志:');
status.logs.forEach(log => console.log(' ' + log));
break;
}
await new Promise(resolve => setTimeout(resolve, 1000));
}
}
main();
速率限制
目前没有强制执行速率限制。但是,请注意:
- 一次只能运行一个编译
- 频繁的启动/停止命令可能会导致状态转换问题
- 日志查询在大容量日志时可能占用大量资源
安全注意事项
- 仅限 HTTPS:所有通信必须使用 HTTPS(端口 8443)
- JWT 认证:除首次创建用户和登录外,所有端点都需要 JWT 认证
- 自签名证书:默认安装使用自签名证书(OpenPLC Editor 会自动处理)
- 文件上传验证:ZIP 文件在解压前会经过全面的安全检查
- 大小限制:文件大小受到限制以防止资源耗尽(10 MB 每文件,50 MB 总计)
- 路径验证:所有文件路径都经过验证以防止遍历攻击
- 密码安全:密码使用 PBKDF2-SHA256(600,000 次迭代)、盐值和胡椒值进行哈希处理
WebSocket 调试接口
对于实时调试和变量检查,OpenPLC Editor 使用位于 https://<主机>:8443/api/debug 的 WebSocket 接口。详情请参阅 调试协议。