{
  "openapi": "3.0.3",
  "info": {
    "title": "Darra PnS 用户 API",
    "version": "1.0.0",
    "description": "与语言 SDK 对等的本机 HTTP。过程数据优先走 SDK。不含授权、配置重载、DCP 探测、端点清单。"
  },
  "servers": [
    { "url": "http://127.0.0.1:18840" }
  ],
  "paths": {
    "/api/status": {
      "get": {
        "tags": ["运行态"],
        "summary": "运行态（对齐 Connected / State / WaitReason / ErrorCode）",
        "responses": {
          "200": {
            "description": "当前运行态",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Status" }
              }
            }
          }
        }
      }
    },
    "/api/io": {
      "get": {
        "tags": ["过程数据"],
        "summary": "读 I，并带回当前 Q 与 IOPS/IOCS",
        "responses": {
          "200": {
            "description": "过程数据快照",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/IoSnapshot" }
              }
            }
          }
        }
      },
      "post": {
        "tags": ["过程数据"],
        "summary": "写 Q",
        "description": "没连上也能写：写入留在服务侧，链路恢复后随周期帧自动下发。响应如实区分「已送出」与「只是排上了」——HTTP 仍 200，看 ok / delivered。",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/IoWrite" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "写入结果",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/IoWriteResponse" }
              }
            }
          },
          "400": {
            "description": "参数不合法或越界"
          },
          "413": {
            "description": "请求体过大"
          },
          "500": {
            "description": "服务内部错误"
          }
        }
      }
    },
    "/api/diag": {
      "get": {
        "tags": ["诊断"],
        "summary": "诊断快照（对齐 DiagSnapshot）",
        "responses": {
          "200": {
            "description": "链路、帧计数、周期、报警统计。从站未启动时 ok=false。",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Diag" }
              }
            }
          }
        }
      }
    },
    "/api/alarms": {
      "get": {
        "tags": ["报警"],
        "summary": "活动报警（对齐 Alarms）",
        "responses": {
          "200": {
            "description": "活动报警数组",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": { "$ref": "#/components/schemas/AlarmItem" }
                }
              }
            }
          }
        }
      }
    },
    "/api/alarms/history": {
      "get": {
        "tags": ["报警"],
        "summary": "报警历史（对齐 AlarmHistory，最近 500 条）",
        "responses": {
          "200": {
            "description": "历史报警数组",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": { "$ref": "#/components/schemas/AlarmItem" }
                }
              }
            }
          }
        }
      }
    },
    "/api/alarms/ack": {
      "post": {
        "tags": ["报警"],
        "summary": "确认一条报警",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["id"],
                "properties": { "id": { "type": "string" } }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "结果",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Ok" }
              }
            }
          }
        }
      }
    },
    "/api/alarms/ack-all": {
      "post": {
        "tags": ["报警"],
        "summary": "确认全部报警",
        "responses": {
          "200": {
            "description": "结果",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Ok" }
              }
            }
          }
        }
      }
    },
    "/api/im": {
      "get": {
        "tags": ["标识"],
        "summary": "I&M 设备标识（对齐 ImData）",
        "responses": {
          "200": {
            "description": "I&M0 与 I&M1–4",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Im" }
              }
            }
          }
        }
      }
    },
    "/api/records": {
      "get": {
        "tags": ["标识"],
        "summary": "用户区记录（对齐 Records）",
        "responses": {
          "200": {
            "description": "用户区记录",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Records" }
              }
            }
          }
        }
      }
    },
    "/api/start": {
      "post": {
        "tags": ["启停"],
        "summary": "启动（对齐 Start）",
        "responses": {
          "200": {
            "description": "结果",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/StartStop" }
              }
            }
          }
        }
      }
    },
    "/api/stop": {
      "post": {
        "tags": ["启停"],
        "summary": "停止（对齐 Stop）",
        "responses": {
          "200": {
            "description": "结果",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/StartStop" }
              }
            }
          }
        }
      }
    },
    "/api/reset": {
      "post": {
        "tags": ["启停"],
        "summary": "复位（对齐 Reset）",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["mode"],
                "properties": {
                  "mode": {
                    "type": "string",
                    "enum": ["communication", "factory"]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "结果",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ResetResult" }
              }
            }
          }
        }
      }
    },
    "/api/high-resolution-timer": {
      "post": {
        "tags": ["运行态"],
        "summary": "高精度时钟（对齐 HighResolutionTimer 写）",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["enable"],
                "properties": {
                  "enable": { "type": "boolean" }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "结果",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Ok" }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Status": {
        "type": "object",
        "properties": {
          "serviceName": { "type": "string" },
          "version": { "type": "string" },
          "state": { "type": "string", "description": "运行态。DataExchange 才是周期交换中" },
          "connected": { "type": "boolean", "description": "是否已在换周期数据" },
          "everConnected": { "type": "boolean", "description": "是否曾经连上。true 且 connected=false = 掉线" },
          "errorCode": { "type": "integer" },
          "ioEpoch": { "type": "integer", "format": "int64" },
          "criticalAlarmCount": { "type": "integer" },
          "alarmCount": { "type": "integer" },
          "hardwarePortPresent": { "type": "boolean" },
          "waitReason": { "type": "string" },
          "statusMessage": { "type": "string" },
          "masterAssignedIp": { "type": "string" },
          "masterAssignedMask": { "type": "string" },
          "masterAssignedGateway": { "type": "string" },
          "configStationName": { "type": "string" },
          "masterStationName": { "type": "string" },
          "dcpIpApplied": { "type": "boolean" },
          "dcpIpApplyMessage": { "type": "string" },
          "outputSyncedToDriver": { "type": "boolean", "description": "Q 区内容是否已送出。false = 写入还停在服务侧，控制器拿不到" },
          "timestamp": { "type": "string" }
        }
      },
      "IoSnapshot": {
        "type": "object",
        "properties": {
          "epoch": { "type": "integer", "format": "int64" },
          "inputHex": { "type": "string", "description": "I 模块输入（主站→模块）" },
          "outputHex": { "type": "string", "description": "Q 模块输出（模块→主站）" },
          "inputLength": { "type": "integer" },
          "outputLength": { "type": "integer" },
          "inputIopsHex": { "type": "string", "description": "I 区 IOPS，0x80=GOOD" },
          "inputIocsHex": { "type": "string" },
          "outputIopsHex": { "type": "string", "description": "Q 区 IOPS，0x80=GOOD" },
          "outputIocsHex": { "type": "string" },
          "inputValid": { "type": "boolean", "description": "本世代是否收到过 I 区数据" },
          "outputValid": { "type": "boolean", "description": "Q 区写过**且**已送到控制器可取的位置。只写过但还没送出 = false" },
          "outputSyncedToDriver": { "type": "boolean", "description": "Q 区当前内容是否已送出。false = 写入还停在服务侧，控制器拿不到" },
          "formatVersion": { "type": "integer", "description": "契约版本，当前 2。多出来的字段跳过就行" },
          "timestamp": { "type": "string" }
        }
      },
      "IoWrite": {
        "type": "object",
        "required": ["offset", "data"],
        "properties": {
          "offset": { "type": "integer", "minimum": 0 },
          "data": { "type": "string", "description": "写入 Q 的十六进制" }
        }
      },
      "IoWriteResponse": {
        "type": "object",
        "properties": {
          "ok": { "type": "boolean", "description": "写入是否已送到控制器可取的位置（与 delivered 同值）" },
          "delivered": { "type": "boolean", "description": "同上，直读用" },
          "queued": { "type": "boolean", "description": "写入已留在服务侧，链路恢复后自动下发，不需要重发。恒 true" },
          "connected": { "type": "boolean", "description": "这次写入时刻是否已在换周期数据" },
          "state": { "type": "string", "description": "这次写入时刻的运行态" },
          "reasonCode": {
            "type": "string",
            "description": "没送出的原因码（送出时为空）",
            "enum": [
              "",
              "PnS.Io.NotDelivered.LinkDown",
              "PnS.Io.NotDelivered.DataPlaneStopped",
              "PnS.Io.NotDelivered.NotFlushed"
            ]
          },
          "waitReason": { "type": "string", "description": "没送出时的等待原因（与 GET /api/status 的 waitReason 同源）" },
          "message": { "type": "string", "description": "没送出时的人读说明（送出时为空）" },
          "errorCode": { "type": "integer", "description": "服务当前错误码，0 = 无。掉线本身不是错误码，判失败看 reasonCode" },
          "offset": { "type": "integer" },
          "length": { "type": "integer" },
          "timestamp": { "type": "string" }
        }
      },
      "Diag": {
        "type": "object",
        "properties": {
          "ok": { "type": "boolean" },
          "sharedMemoryMapped": { "type": "boolean" },
          "message": { "type": "string" },
          "errorCode": { "type": "integer" },
          "state": { "type": "string" },
          "linkUp": { "type": "boolean" },
          "connected": { "type": "boolean" },
          "activeIocrCount": { "type": "integer" },
          "iocrActive": { "type": "integer" },
          "iocrHealthy": { "type": "integer" },
          "heartbeat": { "type": "integer", "format": "int64" },
          "cycleCounter": { "type": "integer", "format": "int64" },
          "lastError": { "type": "integer", "format": "int64" },
          "rxFrames": { "type": "integer", "format": "int64" },
          "txFrames": { "type": "integer", "format": "int64" },
          "rxRtFrames": { "type": "integer", "format": "int64" },
          "txRtFrames": { "type": "integer", "format": "int64" },
          "txControlFrames": { "type": "integer", "format": "int64" },
          "rxControlFrames": { "type": "integer", "format": "int64" },
          "droppedFrames": { "type": "integer", "format": "int64" },
          "invalidFrames": { "type": "integer", "format": "int64" },
          "watchdogTrips": { "type": "integer", "format": "int64" },
          "inputAreaLength": { "type": "integer" },
          "outputAreaLength": { "type": "integer" },
          "stackCycles": { "type": "integer", "format": "int64" },
          "stackFramesRx": { "type": "integer", "format": "int64" },
          "stackFramesTx": { "type": "integer", "format": "int64" },
          "stackMissedTicks": { "type": "integer", "format": "int64" },
          "stackMaxJitterUs": { "type": "integer", "format": "int64" },
          "stackAlarmRxCount": { "type": "integer", "format": "int64" },
          "stackAlarmTxAckOk": { "type": "integer", "format": "int64" },
          "stackAlarmTxAckFail": { "type": "integer", "format": "int64" },
          "timestamp": { "type": "string" }
        }
      },
      "AlarmItem": {
        "type": "object",
        "properties": {
          "id": { "type": "string" },
          "severity": { "type": "string", "description": "Info / Warning / Error / Critical" },
          "message": { "type": "string" },
          "occurredAt": { "type": "string" },
          "count": { "type": "integer" },
          "source": { "type": "string" }
        }
      },
      "Im": {
        "type": "object",
        "properties": {
          "vendorId": { "type": "integer" },
          "deviceId": { "type": "integer" },
          "orderId": { "type": "string" },
          "serialNumber": { "type": "string" },
          "hardwareRevision": { "type": "string" },
          "softwareRevision": { "type": "string" },
          "stationName": { "type": "string" },
          "productName": { "type": "string" },
          "imSupported": { "type": "string" },
          "im14Supported": { "type": "boolean" },
          "im1TagFunction": { "type": "string" },
          "im1TagLocation": { "type": "string" },
          "im2Date": { "type": "string" },
          "im3Descriptor": { "type": "string" },
          "im4SignatureHex": { "type": "string" },
          "timestamp": { "type": "string" }
        }
      },
      "Records": {
        "type": "object",
        "properties": {
          "count": { "type": "integer" },
          "note": { "type": "string" },
          "records": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "slot": { "type": "integer" },
                "subslot": { "type": "integer" },
                "index": { "type": "integer" },
                "length": { "type": "integer" },
                "dataHex": { "type": "string" }
              }
            }
          },
          "timestamp": { "type": "string" }
        }
      },
      "StartStop": {
        "type": "object",
        "properties": {
          "ok": { "type": "boolean" },
          "message": { "type": "string" },
          "errorCode": { "type": "integer" },
          "timestamp": { "type": "string" }
        }
      },
      "ResetResult": {
        "type": "object",
        "properties": {
          "ok": { "type": "boolean" },
          "mode": { "type": "string" },
          "message": { "type": "string" },
          "timestamp": { "type": "string" }
        }
      },
      "Ok": {
        "type": "object",
        "properties": {
          "ok": { "type": "boolean" },
          "message": { "type": "string" }
        }
      }
    }
  }
}
