Skip to content

数据读写 ​

以下端点用于读取核心缓存的最新标签值,以及向设备下发写入指令(反向控制)。

所有端点均需认证。

获取所有标签最新值 ​

获取所有驱动下全部标签的最新采集值。数据来自核心 LatestCache,以标签名为键聚合所有驱动;当多个驱动存在同名标签时,后遍历到的驱动覆盖先者(DataPoint 自身携带 driver 字段,来源不会丢失)。

请求 ​

http
GET /tags
GET /tags?driver={name}
GET /tags?prefix={prefix}
GET /tags?tag={substr}

查询参数(roadmap #35) ​

参数类型必填说明
driverstring否只返回该驱动的最新值快照(跨驱动同名标签不受平铺冲突影响)
prefixstring否标签名前缀匹配(如 ?prefix=temp_)
tagstring否标签名子串匹配(如 ?tag=hum)

多个参数可组合(如 ?driver=plc2&tag=hum)。数千 tag 部署建议按驱动或前缀分片查询,避免整表拉取。

响应 ​

200 OK

返回以标签名为键、DataPoint 为值的映射,包含所有驱动的数据(以下示例展示单个 DataPoint 的序列化结构):

json
{
  "tags": {
    "temperature": {
      "driver": "plc1",
      "device": "",
      "group": "g1",
      "tag": "temperature",
      "value": 42.5,
      "type": "float64",
      "quality": 0,
      "timestamp": "2024-09-08T10:30:00.123456789Z"
    },
    "motor_speed": {
      "driver": "plc2",
      "device": "",
      "group": "g1",
      "tag": "motor_speed",
      "value": 1500,
      "type": "int32",
      "quality": 0,
      "timestamp": "2024-09-08T10:30:00.987654321Z"
    }
  }
}

按驱动过滤

如需仅获取单个驱动的标签值,使用 GET /drivers/{name}/tags,详见驱动管理。

DataPoint 字段说明 ​

字段类型说明
driverstring采集该值的驱动名称
devicestring设备标识。当前配置无 Device 来源(DriverConfig/TagConfig 均无 Device 字段),始终为空字符串 ""
groupstring采集分组名称
tagstring标签名称
valueany标签值
typestring数据类型枚举(DataType 实现了自定义 MarshalJSON/UnmarshalJSON):序列化为可读字符串 bool, int8, int16, int32, int64, uint8, uint16, uint32, uint64, float32, float64, string, bytes。反序列化同时接受字符串(如 "float32")和历史整数形式(9 = float32)
qualityint数据质量枚举(Quality 为 int,按整数序列化):0=good, 1=bad, 2=uncertain
timestampstring (RFC 3339)采集时间戳
is_stalebool数据是否陈旧(超过引擎 stale-threshold 未更新),仅在启用陈旧检测时出现
metadataobject附加元数据(可选)

示例 ​

bash
curl http://localhost:9090/tags \
  -H "Authorization: Bearer corec-secret-token"

写入标签值 ​

向指定设备的指定标签写入值。写入指令通过核心 WriteTag 方法转发至对应驱动的 Write 接口,不阻塞上行采集管道。

请求 ​

http
POST /write
Content-Type: application/json

请求体 — WriteCommand ​

json
{
  "driver": "plc1",
  "device": "192.168.1.10",
  "tag": "setpoint",
  "value": 50.0,
  "type": "float64"
}
字段类型必填说明
driverstring是目标驱动实例名称
devicestring否目标设备标识(当前未使用,保留字段)
tagstring是目标标签名称
valueany是待写入的值(默认为工程值,见下方 scale 说明)
typestring | int是值的数据类型。接受可读字符串(如 "float32",推荐)或历史整数形式(10 = float64)。完整映射:bool=0, int8=1, int16=2, int32=3, int64=4, uint8=5, uint16=6, uint32=7, uint64=8, float32=9, float64=10, string=11, bytes=12
rawbool否默认 false。设为 true 时跳过 scale/offset 反演,把 value 按设备原始值直接编码(用于调试/原始报文下发场景)

与 scale/offset 的关系(写路径反演)

读取方向配置了 scale/offset 的标签,读取上报的是工程值(原始值 × scale + offset)。写入时默认接受工程值,驱动在编码前自动反演回原始值((value - offset) / scale),保证读写往返一致:UI 显示 1.5(scale=0.01),下发 1.5,设备寄存器收到 150。需要直接写设备原始值的调试场景,传 "raw": true。bool/string/bytes 类型不参与变换,原样透传。

请求体限制

请求体最大 1 MiB。超出限制时,JSON 解码将失败并返回 400 Bad Request。

省略 type 会被 400 拒绝

type 是必填字段:请求体缺失 "type" 键时返回 400 Bad Request 与错误信息 "type" is required (one of: bool, int8, …)。此校验在 API 边界执行——缺失该键曾经会静默按零值 bool 类型写入设备,属数据损坏隐患。MQTT 命令通道(反向流)不受此校验影响,其载荷契约仍按传输配置执行。

响应 ​

写入成功 — 200 OK ​

json
{
  "success": true
}

目标不存在或未连接 — 400 Bad Request ​

错误信息中含 not found / not connected(如驱动名写错、目标驱动未连接)时返回 400,并原样携带该错误信息(可由调用方修复):

json
{
  "error": "driver not found: plc1"
}

其他写入失败 — 500 Internal Server Error ​

其余失败(驱动内部错误、通信异常等)可能包含设备地址等内部细节,仅记录在服务端日志中,响应体返回脱敏的通用错误:

json
{
  "error": "internal server error"
}

请求格式错误 — 400 Bad Request ​

json
{
  "error": "invalid character 'x' looking for beginning of value"
}

批量写入 — POST /write/batch ​

配方下发、开机整定等场景一次提交一组写入指令(roadmap #37),顺序执行、逐条返回结果——部分失败不影响后续指令,每条尝试照常进入写入审计历史与(重试耗尽后的)死信队列,语义与单条写完全一致。

json
{
  "writes": [
    { "driver": "plc1", "tag": "setpoint", "value": 50.0, "type": "float64" },
    { "driver": "plc1", "tag": "motor_enable", "value": true, "type": "bool" }
  ]
}
状态码说明
200 OK至少一条成功(含部分失败);results 按请求顺序逐条返回,succeeded/failed 汇总
422 Unprocessable Entity全部失败(请求合法、设备层面全部拒绝)——与 2xx 部分成功区分
400 Bad Request空数组、超过 128 条、请求体非法、或任一元素缺失 type(错误信息形如 writes[N]: "type" is required …,整批拒绝、不执行任何写入)

响应示例:

json
{
  "results": [
    { "success": true },
    { "success": false, "error": "modbus: device rejected" }
  ],
  "succeeded": 1,
  "failed": 1
}

顺序性与体积上限

指令严格按数组顺序执行(不并行——保持与写信号量调度一致的可预测性);单批上限 128 条(请求体同时受 1 MiB 全局上限约束),更大批次拆分为多个请求。

WriteResult 字段说明 ​

字段类型说明
successbool写入是否成功
errorstring错误信息(仅失败时出现)

示例 ​

bash
curl -X POST http://localhost:9090/write \
  -H "Authorization: Bearer corec-secret-token" \
  -H "Content-Type: application/json" \
  -d '{
    "driver": "plc1",
    "device": "192.168.1.10",
    "tag": "setpoint",
    "value": 50.0,
    "type": "float64"
  }'
bash
curl -X POST http://localhost:9090/write \
  -H "Authorization: Bearer corec-secret-token" \
  -H "Content-Type: application/json" \
  -d '{
    "driver": "plc1",
    "device": "192.168.1.10",
    "tag": "motor_enable",
    "value": true,
    "type": "bool"
  }'
bash
curl -X POST http://localhost:9090/write \
  -H "Authorization: Bearer corec-secret-token" \
  -H "Content-Type: application/json" \
  -d '{
    "driver": "plc2",
    "device": "192.168.1.20",
    "tag": "target_speed",
    "value": 3000,
    "type": "int32"
  }'

反向控制流程 ​

API POST /write  →  Engine.WriteTag(cmd)  →  Driver.Write([]cmd)  →  设备

POST /write(及 /write/batch)在 HTTP 处理中同步单次执行:调用 Engine.WriteTag 转发至对应驱动的 Write 方法,结果立即返回给调用方——失败不重试、也不进入死信队列,由调用方决定后续动作。写路径不占用采集 goroutine,不影响上行数据流。

重试与死信队列仅作用于反向流指令(source=command,即经传输命令通道(如 MQTT command-topic)到达的写指令):引擎在独立 goroutine 中按 global.engine.write-retry-count(默认 3)做指数退避重试(100ms 起步、上限 2s,总尝试次数 = 重试次数 + 1);判定为永久性错误的指令(未知标签、只读区域、类型不匹配等配置类错误)跳过剩余重试立即进入死信队列,重试耗尽的指令同样进入死信队列。可通过 GET /write/failed 查询死信队列中的失败指令。

两条路径的每次写尝试(成功与失败)均会记入写入审计历史,可通过 GET /write/history 查询。


查询失败写入指令 ​

查询死信队列中所有重试耗尽后仍失败的写入指令。死信队列在内存中维护,上限 1000 条,超出时驱逐最旧条目;配置 global.engine.dead-letter-path 后,条目会同时追加写入 JSONL 文件并在进程启动时回放(重启不丢失)。

请求 ​

http
GET /write/failed
GET /write/failed?source=ingress

?source= 选择要查询的死信来源(默认 engine):

source说明
engine(默认)引擎死信队列——重试耗尽后仍失败的写指令
ingress入站丢弃——命令到达传输时因命令通道满而被丢弃的指令(如 MQTT command-topic 消费洪峰)。条目带 transport 字段标明丢弃它的传输

未知 source 值返回 400 Bad Request。死信深度与累计入队数也可经 corec_dead_letter_queue_size / corec_dead_letter_pushed_total 指标监控(入站丢弃量见 corec_transport_dropped_commands_total)。

响应 ​

200 OK

json
{
  "failed_writes": [
    {
      "command": {
        "driver": "plc1",
        "device": "192.168.1.10",
        "tag": "setpoint",
        "value": 50.0,
        "type": "float64"
      },
      "error": "modbus: connection refused",
      "failed_at": "2024-09-08T10:30:05.123456789Z",
      "attempts": 4
    }
  ],
  "count": 1
}

DeadLetterEntry 字段说明 ​

字段类型说明
commandobject原始 WriteCommand(driver, device, tag, value, type)
errorstring最后一次失败的错误信息(入站丢弃为丢弃原因,如 command channel full at ingress;传输被移除时为 transport removed before dispatch)
failed_atstring (RFC 3339)进入死信队列的时间
attemptsint写入尝试次数:重试耗尽类为 write-retry-count + 1,永久性错误类为实际尝试次数;入站丢弃、传输移除前未派发等从未执行写入的条目为 0
transportstring仅 source=ingress 时出现:丢弃该命令的传输实例名

示例 ​

bash
curl http://localhost:9090/write/failed \
  -H "Authorization: Bearer corec-secret-token"

查询写入审计历史 ​

返回写入审计历史(audit trail):最近 1000 条写入尝试,成功与失败均记录,按时间新→旧排列。控制类系统的合规刚需——不仅失败要可见(死信队列),成功的 PLC 写操作也必须可追溯(何时、何人、从何处写了哪个点)。

与 GET /write/failed 的区别:死信队列只保留重试耗尽的失败指令;审计历史是"最近发生了哪些写"的完整有界记录,每一条尝试记一条(反向流指令重试时,每次重试各记一条)。

请求 ​

http
GET /write/history
GET /write/history?driver={name}&tag={substr}
GET /write/history?since={rfc3339}&limit={n}

查询参数(roadmap #35) ​

参数类型必填说明
driverstring否只返回该驱动的写记录(精确匹配)
tagstring否标签子串匹配
sincestring (RFC 3339)否只返回该时刻及之后的记录(如 2026-10-20T12:00:00Z;非法值返回 400)
limitint否最多返回前 n 条(新→旧截断);无法解析、负数或不小于结果长度的值被忽略(返回全量)

参数可组合,过滤在服务端完成——合规检索不再需要拉全量 1000 条后客户端过滤。

响应 ​

200 OK

json
{
  "writes": [
    {
      "timestamp": "2024-09-08T10:30:00.123456789Z",
      "driver": "plc1",
      "device": "192.168.1.10",
      "tag": "setpoint",
      "value": 50.0,
      "type": "float64",
      "success": true,
      "source": "api",
      "remote": "192.168.1.50:53412"
    },
    {
      "timestamp": "2024-09-08T10:29:58.987654321Z",
      "driver": "plc1",
      "device": "192.168.1.10",
      "tag": "motor_enable",
      "value": true,
      "type": "bool",
      "success": false,
      "error": "modbus exception code 3",
      "source": "command"
    }
  ],
  "count": 2
}

WriteAuditEntry 字段说明 ​

字段类型说明
timestampstring (RFC 3339)该次写尝试的记录时间
driverstring目标驱动实例名称
devicestring目标设备标识(常为空字符串)
tagstring目标标签名称
valueany实际写入的值。与运行日志刻意不记录值不同,写审计历史的核心用途就是记录"写了什么"
typestring值的数据类型(DataType,按字符串序列化)
successbool该次尝试是否成功
errorstring失败信息(仅失败时出现)
sourcestring写来源:api(POST /write 直写)、api-batch(POST /write/batch 批量写,逐条各记一条)或 command(反向流指令,即 MQTT/HTTP 命令通道触发的写)
remotestringsource=api / api-batch 时为调用方网络地址(r.RemoteAddr);source=command 时省略(无对端地址)
rawbooltrue 表示该写跳过了 scale/offset 反演(记录的 value 即设备原始值);默认省略(value 为工程值)

审计来源不可伪造

source 与 remote 由服务端在信任边界处写入:POST /write 在解码请求体之后统一盖章 source="api" + 调用方地址;引擎命令监听器对每条来自传输通道的指令统一盖章 source="command"。请求体或指令载荷中携带的同名字段一律被覆盖,外部输入无法污染审计历史。

历史边界

审计历史为内存环形缓冲,上限 1000 条(超出驱逐最旧条目):跨配置 reload 存活,进程重启后清空;它是有界的最近记录,不是持久化审计日志。需要长期留存时请由外部系统轮询拉取归档。

引擎不支持时 — 501 Not Implemented ​

当引擎未实现可选角色接口 core.WriteAuditor(如测试替身、第三方 Engine 实现)时返回:

json
{
  "error": "write history not supported by this engine"
}

示例 ​

bash
curl http://localhost:9090/write/history \
  -H "Authorization: Bearer corec-secret-token"

Released under the MIT License.