From eaa89f597cb05eeb5a0c5fe4e17af676072be4c6 Mon Sep 17 00:00:00 2001 From: root <13910913995@163.com> Date: Mon, 18 May 2026 17:05:23 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E4=BA=86=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/api/routes/config.py | 6 +- backend/app/services/platform_service.py | 52 +++- backend/config/channel.json | 182 +++++++++++- backend/config/setting.json | 109 ++++++- backend/tests/test_api.py | 35 ++- ...气量测控平台系统需求分析及技术框架设计.md | 276 +++++++++++------- 6 files changed, 535 insertions(+), 125 deletions(-) diff --git a/backend/app/api/routes/config.py b/backend/app/api/routes/config.py index 999fc30..ca517ae 100644 --- a/backend/app/api/routes/config.py +++ b/backend/app/api/routes/config.py @@ -1,6 +1,6 @@ from typing import Any, Dict, List -from fastapi import APIRouter, Depends +from fastapi import APIRouter, Depends, Query from app.core.response import success_response from app.core.security import verify_api_token @@ -32,8 +32,8 @@ def save_channel_config(payload: ChannelConfigIn) -> Dict[str, Any]: @router.get("/line_alarm_setting", dependencies=[Depends(verify_api_token)]) -def get_line_alarm_setting() -> Dict[str, Any]: - return success_response(platform_service.get_line_alarm_setting(), msg="获取线路报警设置成功") +def get_line_alarm_setting(line_no: int = Query(default=1, ge=1)) -> Dict[str, Any]: + return success_response(platform_service.get_line_alarm_setting(line_no=line_no), msg="获取线路报警设置成功") @router.post("/line_alarm_setting", dependencies=[Depends(verify_api_token)]) diff --git a/backend/app/services/platform_service.py b/backend/app/services/platform_service.py index 30225f5..791dc14 100644 --- a/backend/app/services/platform_service.py +++ b/backend/app/services/platform_service.py @@ -93,21 +93,9 @@ class PlatformService: data.update(current) return data - def save_line_alarm_setting(self, payload: LineAlarmSettingIn) -> Dict[str, Any]: - current = self.config_repo.read_json("setting.json") - if not isinstance(current, dict): - current = {} - current["line_alarm_setting"] = payload.model_dump() - path = self.config_repo.write_setting_config(current) - device_result = self.device_client.send_line_alarm_setting(payload) - return {"save_path": f"/config/{path.name}", **device_result} - - def get_line_alarm_setting(self) -> Dict[str, Any]: - current = self.config_repo.read_setting_section("line_alarm_setting") - if isinstance(current, dict): - return current + def _default_line_alarm_setting(self, line_no: int) -> Dict[str, Any]: return { - "line_no": 1, + "line_no": line_no, "over_limit_alarm": [ {"category": "电压", "limit": 180, "delay": 180, "output_node": "开出1", "enabled": True}, {"category": "电流", "limit": 180, "delay": 180, "output_node": "开出1", "enabled": True}, @@ -117,6 +105,42 @@ class PlatformService: "fault_alarm": [{"category": "PT断线", "delay": 180, "output_node": "开出1", "enabled": True}], } + def _normalize_line_alarm_settings(self, raw: Any) -> List[Dict[str, Any]]: + if isinstance(raw, list): + return [item for item in raw if isinstance(item, dict)] + if isinstance(raw, dict): + return [raw] + return [] + + def save_line_alarm_setting(self, payload: LineAlarmSettingIn) -> Dict[str, Any]: + current = self.config_repo.read_json("setting.json") + if not isinstance(current, dict): + current = {} + line_alarm_list = self._normalize_line_alarm_settings(current.get("line_alarm_setting")) + payload_data = payload.model_dump() + + updated = False + for index, item in enumerate(line_alarm_list): + if item.get("line_no") == payload.line_no: + line_alarm_list[index] = payload_data + updated = True + break + if not updated: + line_alarm_list.append(payload_data) + + current["line_alarm_setting"] = line_alarm_list + path = self.config_repo.write_setting_config(current) + device_result = self.device_client.send_line_alarm_setting(payload) + return {"save_path": f"/config/{path.name}", **device_result} + + def get_line_alarm_setting(self, line_no: int = 1) -> Dict[str, Any]: + current = self.config_repo.read_setting_section("line_alarm_setting") + line_alarm_list = self._normalize_line_alarm_settings(current) + for item in line_alarm_list: + if item.get("line_no") == line_no: + return item + return self._default_line_alarm_setting(line_no) + def save_ai_alarm_setting(self, payload: List[AiAlarmSettingIn]) -> Dict[str, Any]: current = self.config_repo.read_json("setting.json") if not isinstance(current, dict): diff --git a/backend/config/channel.json b/backend/config/channel.json index 0c863fe..2043af2 100644 --- a/backend/config/channel.json +++ b/backend/config/channel.json @@ -2,11 +2,99 @@ "ai_channel": [ { "ch": 1, - "singal_type": "4-20mA22222", - "line_no": 1, + "singal_type": "1-5v", + "line_no": 2, "type": "UA", "limit_low": 0.0, "limit_high": 20.0 + }, + { + "ch": 2, + "singal_type": "1~5V", + "line_no": 1, + "type": "UA", + "limit_low": 1.0, + "limit_high": 8.0 + }, + { + "ch": 3, + "singal_type": "4~20mA", + "line_no": 1, + "type": "UA", + "limit_low": 1.0, + "limit_high": 8.0 + }, + { + "ch": 4, + "singal_type": "1~5V", + "line_no": 1, + "type": "UA", + "limit_low": 1.0, + "limit_high": 8.0 + }, + { + "ch": 5, + "singal_type": "4~20mA", + "line_no": 1, + "type": "UA", + "limit_low": 1.0, + "limit_high": 8.0 + }, + { + "ch": 6, + "singal_type": "1~5V", + "line_no": 1, + "type": "UA", + "limit_low": 1.0, + "limit_high": 8.0 + }, + { + "ch": 7, + "singal_type": "4~20mA", + "line_no": 1, + "type": "UA", + "limit_low": 1.0, + "limit_high": 8.0 + }, + { + "ch": 8, + "singal_type": "1~5V", + "line_no": 1, + "type": "UA", + "limit_low": 1.0, + "limit_high": 8.0 + }, + { + "ch": 9, + "singal_type": "4~20mA", + "line_no": 1, + "type": "UA", + "limit_low": 1.0, + "limit_high": 8.0 + }, + { + "ch": 10, + "singal_type": "1~5V", + "line_no": 1, + "type": "UA", + "limit_low": 1.0, + "limit_high": 8.0 + }, + { + "ch": 11, + "singal_type": "4~20mA", + "line_no": 1, + "type": "UA", + "limit_low": 1.0, + "limit_high": 8.0 + }, + { + "ch": 12, + "singal_type": "4~20mA", + "line_no": 1, + "type": "UA", + "limit_low": 1.0, + "limit_high": 8.0 } ], "ao_channel": [ @@ -15,8 +103,96 @@ "singal_type": "1-5v", "line_no": 2, "type": "UA", - "limit_low": 0.0, + "limit_low": 10.0, "limit_high": 20.0 + }, + { + "ch": 2, + "singal_type": "1~5V", + "line_no": 1, + "type": "UA", + "limit_low": 1.0, + "limit_high": 8.0 + }, + { + "ch": 3, + "singal_type": "4~20mA", + "line_no": 1, + "type": "UA", + "limit_low": 1.0, + "limit_high": 8.0 + }, + { + "ch": 4, + "singal_type": "1~5V", + "line_no": 1, + "type": "UA", + "limit_low": 1.0, + "limit_high": 8.0 + }, + { + "ch": 5, + "singal_type": "4~20mA", + "line_no": 1, + "type": "UA", + "limit_low": 1.0, + "limit_high": 8.0 + }, + { + "ch": 6, + "singal_type": "1~5V", + "line_no": 1, + "type": "UA", + "limit_low": 1.0, + "limit_high": 8.0 + }, + { + "ch": 7, + "singal_type": "4~20mA", + "line_no": 1, + "type": "UA", + "limit_low": 1.0, + "limit_high": 8.0 + }, + { + "ch": 8, + "singal_type": "1~5V", + "line_no": 1, + "type": "UA", + "limit_low": 1.0, + "limit_high": 8.0 + }, + { + "ch": 9, + "singal_type": "4~20mA", + "line_no": 1, + "type": "UA", + "limit_low": 1.0, + "limit_high": 8.0 + }, + { + "ch": 10, + "singal_type": "1~5V", + "line_no": 1, + "type": "UA", + "limit_low": 1.0, + "limit_high": 8.0 + }, + { + "ch": 11, + "singal_type": "4~20mA", + "line_no": 1, + "type": "UA", + "limit_low": 1.0, + "limit_high": 8.0 + }, + { + "ch": 12, + "singal_type": "4~20mA", + "line_no": 1, + "type": "UA", + "limit_low": 1.0, + "limit_high": 8.0 } ] } \ No newline at end of file diff --git a/backend/config/setting.json b/backend/config/setting.json index e9c6f8b..8960fed 100644 --- a/backend/config/setting.json +++ b/backend/config/setting.json @@ -1,5 +1,7 @@ { - "line_alarm_setting": { + "line_alarm_setting": + [ + { "line_no": 1, "over_limit_alarm": [ { @@ -19,7 +21,9 @@ "enabled": true } ] - }, + } + ] + , "ai_alarm_setting": [ { "channel_no": 1, @@ -29,11 +33,110 @@ "delay": 180, "output_node": "开出1", "enabled": true + }, + { + "channel_no": 2, + "singal_type": "1~5V", + "limit_low": 0.0, + "limit_high": 8.0, + "delay": 8, + "output_node": "", + "enabled": true + }, + { + "channel_no": 3, + "singal_type": "4~20mA", + "limit_low": 0.0, + "limit_high": 8.0, + "delay": 8, + "output_node": "", + "enabled": false + }, + { + "channel_no": 4, + "singal_type": "1~5V", + "limit_low": 0.0, + "limit_high": 8.0, + "delay": 8, + "output_node": "", + "enabled": false + }, + { + "channel_no": 5, + "singal_type": "4~20mA", + "limit_low": 0.0, + "limit_high": 8.0, + "delay": 8, + "output_node": "", + "enabled": false + }, + { + "channel_no": 6, + "singal_type": "1~5V", + "limit_low": 0.0, + "limit_high": 8.0, + "delay": 8, + "output_node": "", + "enabled": false + }, + { + "channel_no": 7, + "singal_type": "4~20mA", + "limit_low": 0.0, + "limit_high": 8.0, + "delay": 8, + "output_node": "", + "enabled": false + }, + { + "channel_no": 8, + "singal_type": "1~5V", + "limit_low": 0.0, + "limit_high": 8.0, + "delay": 8, + "output_node": "", + "enabled": false + }, + { + "channel_no": 9, + "singal_type": "4~20mA", + "limit_low": 0.0, + "limit_high": 8.0, + "delay": 8, + "output_node": "", + "enabled": false + }, + { + "channel_no": 10, + "singal_type": "1~5V", + "limit_low": 0.0, + "limit_high": 8.0, + "delay": 8, + "output_node": "", + "enabled": false + }, + { + "channel_no": 11, + "singal_type": "4~20mA", + "limit_low": 0.0, + "limit_high": 8.0, + "delay": 8, + "output_node": "", + "enabled": false + }, + { + "channel_no": 12, + "singal_type": "1~5V", + "limit_low": 0.0, + "limit_high": 8.0, + "delay": 8, + "output_node": "", + "enabled": false } ], "system_config": { "time_sync": "manual", "brightness": 83, - "screen_saver": 60 + "screen_saver": 120 } } \ No newline at end of file diff --git a/backend/tests/test_api.py b/backend/tests/test_api.py index a36b72c..8ccfb5e 100644 --- a/backend/tests/test_api.py +++ b/backend/tests/test_api.py @@ -112,7 +112,10 @@ def test_config_query_endpoints(tmp_path) -> None: ) platform_service.config_repo.write_setting_config( { - "line_alarm_setting": {"line_no": 1, "over_limit_alarm": [{"category": "电压", "limit": 180, "delay": 180, "output_node": "开出1", "enabled": True}], "fault_alarm": []}, + "line_alarm_setting": [ + {"line_no": 1, "over_limit_alarm": [{"category": "电压", "limit": 180, "delay": 180, "output_node": "开出1", "enabled": True}], "fault_alarm": []}, + {"line_no": 2, "over_limit_alarm": [{"category": "电流", "limit": 200, "delay": 120, "output_node": "开出2", "enabled": True}], "fault_alarm": []}, + ], "ai_alarm_setting": [{"channel_no": 1, "singal_type": "4-20mA", "limit_low": 0, "limit_high": 20, "delay": 180, "output_node": "开出1", "enabled": True}], "system_config": {"time_sync": "manual", "brightness": 83, "screen_saver": 60}, } @@ -122,7 +125,7 @@ def test_config_query_endpoints(tmp_path) -> None: device_response = client.get("/api/config/device", headers=headers) channel_response = client.get("/api/config/channel", headers=headers) - line_alarm_response = client.get("/api/config/line_alarm_setting", headers=headers) + line_alarm_response = client.get("/api/config/line_alarm_setting", headers=headers, params={"line_no": 2}) ai_alarm_response = client.get("/api/config/ai_alarm_setting", headers=headers) system_response = client.get("/api/config/system", headers=headers) @@ -135,8 +138,34 @@ def test_config_query_endpoints(tmp_path) -> None: assert device_response.json()["data"]["password"] == "" assert device_response.json()["data"]["net"][0]["ip"] == "192.168.1.10" assert channel_response.json()["data"]["ai_channel"][0]["singal_type"] == "4-20mA" - assert line_alarm_response.json()["data"]["line_no"] == 1 + assert line_alarm_response.json()["data"]["line_no"] == 2 + assert line_alarm_response.json()["data"]["over_limit_alarm"][0]["category"] == "电流" assert ai_alarm_response.json()["data"][0]["channel_no"] == 1 assert system_response.json()["data"]["brightness"] == 83 finally: platform_service.config_repo = old_repo + + +def test_save_line_alarm_setting_stores_list(tmp_path) -> None: + old_repo = platform_service.config_repo + platform_service.config_repo = JsonConfigRepository(tmp_path) + + try: + headers = {"X-API-Token": settings.auth_password} + response = client.post( + "/api/config/line_alarm_setting", + headers=headers, + json={ + "line_no": 3, + "over_limit_alarm": [{"category": "频率", "limit": 51.5, "delay": 30, "output_node": "开出1", "enabled": True}], + "fault_alarm": [{"category": "PT断线", "delay": 20, "output_node": "开出2", "enabled": False}], + }, + ) + + assert response.status_code == 200 + + saved = platform_service.config_repo.read_setting_config() + assert isinstance(saved["line_alarm_setting"], list) + assert saved["line_alarm_setting"][0]["line_no"] == 3 + finally: + platform_service.config_repo = old_repo diff --git a/document/电气量测控平台系统需求分析及技术框架设计.md b/document/电气量测控平台系统需求分析及技术框架设计.md index e8e07d6..1f03491 100644 --- a/document/电气量测控平台系统需求分析及技术框架设计.md +++ b/document/电气量测控平台系统需求分析及技术框架设计.md @@ -1,87 +1,109 @@ # 电气量测控平台系统需求分析与架构设计技术方案 + **适配平台:RK3568 嵌入式平台(ubuntu 22.04系统)** **技术栈:FastAPI + HTTP + WebSocket + Python + Web前端** ## 一、文档概述 + ### 1.1 文档目的 + 本文档基于《电气量测控平台显示界面设计和数据接口说明》、`gui.h`头文件及项目功能描述,明确**RK3568嵌入式平台**上Python编写、Web界面展示的电气量测控配置软件的功能、性能、接口、数据存储需求,完成系统架构设计,为软件开发、测试及交付提供技术依据。 ### 1.2 适用范围 + 本文档适用于**RK3568嵌入式硬件**,以Python为开发语言、FastAPI为服务框架、Web为交互界面的电气量测控配置软件,涵盖参数配置、实时数据交互、状态监测、报警管理等全流程功能设计。 ### 1.3 参考资料 + 1. 《电气量测控平台显示界面设计和数据接口说明》 2. `gui.h`嵌入式程序头文件 3. 项目功能需求描述 ## 二、系统需求分析 + ### 2.1 运行环境需求 + - **硬件环境**:RK3568嵌入式开发板(支持Linux系统、文件系统、SQLite数据库) - **软件环境**:Linux操作系统、Python 3.8、**FastAPI**、Uvicorn ASGI服务、SQLite数据库、JSON文件存储支持 - **交互环境**:Web浏览器(支持WebSocket、动态数据渲染、按钮交互、表格/波形展示) ### 2.2 功能需求 + #### 2.2.1 参数配置管理功能 + 实现测控设备全维度参数配置,配置项支持**JSON文件本地存储**,关键参数通过接口下发至C语言嵌入式程序,具体配置项如下: + 1. **基础设备配置** - - 设备软件版本信息展示与配置 - - 通讯参数设置:串口、网口参数配置 - - 操作密码设置:支持密码修改、本地JSON文件存储 + - 设备软件版本信息展示与配置 + - 通讯参数设置:串口、网口参数配置 + - 操作密码设置:支持密码修改、本地JSON文件存储 2. **装置通道配置** - - AI通道设置:模拟量输入配置、信号类型、所属线路、数据阈值 - - AO通道设置:模拟量输出配置 - - 定值设置:电压/电流/功率等超限限值、检测延时、报警输出配置 + - AI通道设置:模拟量输入配置、信号类型、所属线路、数据阈值 + - AO通道设置:模拟量输出配置 + - 定值设置:电压/电流/功率等超限限值、检测延时、报警输出配置 3. **系统管理配置** - - 对时设置:参数实时下发嵌入式程序 - - 灯光设置:屏幕亮度、屏保时间,参数实时下发嵌入式程序 + - 对时设置:参数实时下发嵌入式程序 + - 灯光设置:屏幕亮度、屏保时间,参数实时下发嵌入式程序 4. **配置回显要求** - - 设备网络配置、通道配置、报警设置、系统设置页面在打开时需先读取已保存参数 - - 已保存配置从本地JSON文件读取,并通过HTTP查询接口返回给Web界面显示 + - 设备网络配置、通道配置、报警设置、系统设置页面在打开时需先读取已保存参数 + - 已保存配置从本地JSON文件读取,并通过HTTP查询接口返回给Web界面显示 #### 2.2.2 实时数据动态显示功能 + 1. **数据读取周期**:间隔**0.5秒**从C语言控制系统读取实时数据 2. **数据类型**:模拟量、开关量、AI采集数据 3. **推送方式**:**WebSocket主动推送**至Web界面,动态刷新展示 4. **存储规则**:动态数据仅内存缓存,**不保存到本地** #### 2.2.3 设备状态实时监测功能 + 从嵌入式系统动态读取设备状态,Web界面实时展示: + - 装置自检状态(正常/异常) - 网络1状态、网络2状态 - 串口1状态、串口2状态 #### 2.2.4 报警事件管理功能 + 1. **报警数据采集**:实时获取嵌入式设备报警事件 2. **数据存储**:报警事件保存至**SQLite数据库** 3. **展示方式**:**WebSocket实时推送**报警至Web界面,支持弹窗提醒+历史查询 ### 2.3 数据交互需求 + #### 2.3.1 上行数据(嵌入式系统→Python服务) + - 实时数据:0.5秒/次,模拟量、开关量、AI采集数据 - 状态数据:装置自检、网络、串口状态 - 报警数据:实时报警事件数据 #### 2.3.2 下行数据(Python服务→嵌入式系统) + - 配置数据:设备参数、通道配置、定值、对时、灯光参数 - 控制指令:开关量分/合控制信号 ### 2.4 数据存储需求 + 1. **JSON文件存储**:设备版本、通讯参数、密码、AI/AO配置、定值配置等**非实时参数** 2. **SQLite数据库**:报警事件数据(支持历史查询) 3. **动态数据**:仅内存缓存,不持久化存储 ### 2.5 性能需求 + 1. **数据刷新率**:实时数据读取与WebSocket推送间隔≤0.5秒 2. **响应速度**:参数配置下发、开关控制指令响应时间≤1秒 3. **稳定性**:7×24小时连续运行 4. **兼容性**:适配RK3568硬件,兼容嵌入式C程序接口 ### 2.6 通信服务需求 + 1. **HTTP服务**:基于FastAPI提供**RESTful API**,用于参数配置、状态查询、报警查询、控制指令 2. **WebSocket服务**:基于FastAPI提供双工通信,用于**实时数据、报警数据主动推送**至Web界面 ## 三、系统架构设计 + ### 3.1 总体架构 + 系统采用**分层架构 + 前后端分离**设计,从下到上分为: **硬件层 → 嵌入式C程序层 → Python FastAPI服务层 → Web界面层** @@ -96,11 +118,14 @@ Python FastAPI服务层(RESTful接口 + WebSocket推送 + 数据处理) ``` ### 3.2 分层详细设计 + #### 3.2.1 硬件层 + - 核心硬件:RK3568嵌入式开发板 - 外设模块:AI/AO采集模块、开关量模块、网络/串口模块、显示屏 #### 3.2.2 嵌入式C程序层 + - 数据采集模块:20ms周期采集模拟量、开关量、AI数据 - 控制执行模块:接收参数与指令,执行配置、对时、灯光、开关控制 - 状态监测模块:自检、网口、串口状态实时上报 @@ -108,7 +133,9 @@ Python FastAPI服务层(RESTful接口 + WebSocket推送 + 数据处理) - 通讯模块:与Python服务双向数据交互 #### 3.2.3 Python FastAPI服务层(核心) + 采用**FastAPI + Uvicorn**搭建,提供**HTTP接口**和**WebSocket推送**双服务: + 1. **RESTful API模块**:处理配置提交、状态查询、报警查询、控制指令 2. **WebSocket推送模块**:0.5秒推送实时数据、实时报警数据到Web端 3. **参数配置模块**:JSON文件读写、配置下发C程序 @@ -117,24 +144,29 @@ Python FastAPI服务层(RESTful接口 + WebSocket推送 + 数据处理) 6. **设备状态模块**:采集并缓存设备运行状态 #### 3.2.4 Web界面层 + - 菜单区:配置、状态、数据、报警、控制入口 - 状态区:自检、网络、串口实时状态 - 主显示区:实时数据表格、配置表单、控制按钮、报警列表 - 报警区:WebSocket实时弹窗+滚动显示 ### 3.3 数据流向设计 + 1. **实时/报警数据流向** -嵌入式C程序 → Python → WebSocket主动推送 → Web界面(0.5秒刷新) + 嵌入式C程序 → Python → WebSocket主动推送 → Web界面(0.5秒刷新) 2. **参数配置流向** -Web界面 → HTTP POST → Python → JSON存储 + 下发C程序 + Web界面 → HTTP POST → Python → JSON存储 + 下发C程序 3. **状态/查询流向** -Web界面 → HTTP GET → Python → 返回数据展示 + Web界面 → HTTP GET → Python → 返回数据展示 4. **配置回显流向** -Web界面进入设置页 → HTTP GET配置查询接口 → Python读取JSON配置 → 返回当前配置数据 → Web界面显示 + Web界面进入设置页 → HTTP GET配置查询接口 → Python读取JSON配置 → 返回当前配置数据 → Web界面显示 ## 四、接口详细设计(RESTful + WebSocket) + ### 4.1 RESTful API 接口详情(完整参数+返回值) + 所有接口统一返回格式: + ```json { "code": 200, // 200=成功,400=参数错误,500=服务异常 @@ -143,9 +175,10 @@ Web界面进入设置页 → HTTP GET配置查询接口 → Python读取JSON配 } ``` ---- +*** #### 1. GET /api/real-time-data + **作用**:获取最新一次实时数据(备用,主用WebSocket) **参数**:无 **返回data**: @@ -275,12 +308,14 @@ Web界面进入设置页 → HTTP GET配置查询接口 → Python读取JSON配 } ``` ---- +*** #### 2. GET /api/device-status + **作用**:获取设备当前运行状态 **参数**:无 **返回data**: + ```json { "self_check": "正常", @@ -291,12 +326,14 @@ Web界面进入设置页 → HTTP GET配置查询接口 → Python读取JSON配 } ``` ---- +*** #### 3. GET /api/config/device + **作用**:读取设备基础配置,用于设备/网络配置界面打开时回显当前参数 **参数**:无 **返回data**: + ```json { "password": "", @@ -332,11 +369,13 @@ Web界面进入设置页 → HTTP GET配置查询接口 → Python读取JSON配 } ``` ---- +*** #### 4. POST /api/config/device + **作用**:提交设备基础配置(版本、通讯、密码) **请求body参数**: + ```json { "password": "123456", @@ -386,14 +425,17 @@ Web界面进入设置页 → HTTP GET配置查询接口 → Python读取JSON配 ] } ``` + **返回data**:`{"save_path":"/config/device.json","send_status":"成功"}` ---- +*** #### 5. GET /api/config/channel + **作用**:读取通道配置,用于通道配置界面打开时回显当前参数 **参数**:无 **返回data**: + ```json { "ai_channel": [{"ch":1,"singal_type":"4-20mA","line_no":1,"type":"UA","limit_low":0,"limit_high":20}], @@ -401,9 +443,10 @@ Web界面进入设置页 → HTTP GET配置查询接口 → Python读取JSON配 } ``` ---- +*** #### 6. POST /api/config/channel + **作用**:提交AI/AO通道配置(AI:12通道,AO:12通道) **请求body参数**: @@ -413,14 +456,18 @@ Web界面进入设置页 → HTTP GET配置查询接口 → Python读取JSON配 "ao_channel": [{"ch":1,"singal_type":"1-5v","line_no":2,"type":"UA","limit_low":0,"limit_high":20}] } ``` + **返回data**:`{"save_path":"/config/channel.json","send_status":"成功"}` ---- +*** + +#### 7. GET /api/config/line\_alarm\_setting -#### 7. GET /api/config/line_alarm_setting **作用**:读取线路报警设置,用于报警设置界面打开时回显当前线路参数 -**参数**:无 +**参数**: +- `line_no=1`:按线路号读取对应线路的报警设置 **返回data**: + ```json { "line_no": 1, @@ -444,77 +491,85 @@ Web界面进入设置页 → HTTP GET配置查询接口 → Python读取JSON配 } ``` ---- +*** + +#### 8. POST /api/config/line\_alarm\_setting -#### 8. POST /api/config/line_alarm_setting **作用**:提交定值报警阈值配置 **请求body参数**: ```json -[ - {"line_no": 1, - "over_limit_alarm": [ - { - "category": "电压", - "limit": 180, - "delay": 180, - "output_node": "开出1", - "enabled": true - }, - { - "category": "电流", - "limit": 180, - "delay": 180, - "output_node": "开出1", - "enabled": true - }, - { - "category": "差流", - "limit": 180, - "delay": 180, - "output_node": "开出1", - "enabled": false - }, - { - "category": "功率", - "limit": 180, - "delay": 180, - "output_node": "开出1", - "enabled": true - }, - { - "category": "频率", - "limit": 180, - "delay": 180, - "output_node": "开出1", - "enabled": false - } - ], - "fault_alarm": [ - { - "category": "PT断线", - "delay": 180, - "output_node": "开出1", - "enabled": true - }, - { - "category": "CT断线", - "delay": 180, - "output_node": "开出1", - "enabled": false - } - ] - } - ] +{ + "line_no": 1, + "over_limit_alarm": [ + { + "category": "电压", + "limit": 180, + "delay": 180, + "output_node": "开出1", + "enabled": true + }, + { + "category": "电流", + "limit": 180, + "delay": 180, + "output_node": "开出1", + "enabled": true + }, + { + "category": "差流", + "limit": 180, + "delay": 180, + "output_node": "开出1", + "enabled": false + }, + { + "category": "功率", + "limit": 180, + "delay": 180, + "output_node": "开出1", + "enabled": true + }, + { + "category": "频率", + "limit": 180, + "delay": 180, + "output_node": "开出1", + "enabled": false + } + ], + "fault_alarm": [ + { + "category": "PT断线", + "delay": 180, + "output_node": "开出1", + "enabled": true + }, + { + "category": "CT断线", + "delay": 180, + "output_node": "开出1", + "enabled": false + } + ] +} ``` + **返回data**:`{"save_path":"/config/setting.json","send_status":"成功"}` ---- +说明: +- `setting.json` 中的 `line_alarm_setting` 按数组对象存储 +- 保存接口按单条线路配置提交 +- 服务端会根据 `line_no` 对数组中的对应线路进行覆盖更新,不存在时追加 + +*** + +#### 9. GET /api/config/ai\_alarm\_setting -#### 9. GET /api/config/ai_alarm_setting **作用**:读取AI报警设置,用于报警设置界面打开时回显当前AI报警参数 **参数**:无 **返回data**: + ```json [ { @@ -529,9 +584,9 @@ Web界面进入设置页 → HTTP GET配置查询接口 → Python读取JSON配 ] ``` ---- +*** -#### 10. POST /api/config/ai_alarm_setting +#### 10. POST /api/config/ai\_alarm\_setting **作用**:提交AI报警设置 **请求body参数**: @@ -562,9 +617,11 @@ Web界面进入设置页 → HTTP GET配置查询接口 → Python读取JSON配 **返回data**:`{"save_path":"/config/setting.json","send_status":"成功"}` #### 11. GET /api/config/system + **作用**:读取系统设置,用于系统设置界面打开时回显当前参数 **参数**:无 **返回data**: + ```json { "time_sync": "auto", @@ -573,12 +630,13 @@ Web界面进入设置页 → HTTP GET配置查询接口 → Python读取JSON配 } ``` ---- +*** #### 12. POST /api/config/system **作用**:提交系统对时、灯光配置 **请求body参数**: + ```json { "time_sync": "auto", @@ -586,13 +644,16 @@ Web界面进入设置页 → HTTP GET配置查询接口 → Python读取JSON配 "screen_saver": 60 } ``` + **返回data**:`{"send_status":"成功"}` ---- +*** #### 13. GET /api/alarm/list + **作用**:分页查询历史报警 **参数**: + - `page=1`:页码,从 `1` 开始 - `size=20`:每页条数,最大 `100` - `no=L1`:按线路号/通道号精确筛选 @@ -601,16 +662,19 @@ Web界面进入设置页 → HTTP GET配置查询接口 → Python读取JSON配 - `end_time=2026-05-16T11:30:00`:按报警结束时间筛选 **示例请求**: + ```text GET /api/alarm/list?page=1&size=20&no=L1&type=current&start_time=2026-05-16T10:30:00&end_time=2026-05-16T11:30:00 ``` 说明: + - 所有筛选条件均为可选 - 未传筛选条件时,默认按分页查询全部历史报警 - `start_time` 和 `end_time` 使用日期时间格式 **返回data**: + ```json [ { @@ -625,21 +689,27 @@ GET /api/alarm/list?page=1&size=20&no=L1&type=current&start_time=2026-05-16T10:3 ] ``` ---- +*** #### 14. POST /api/control/switch + **作用**:下发开关量控制指令 **请求body参数**: + ```json {"ch":1,"action":1} // 1=合,0=分 ``` + **返回data**:`{"control_status":"执行成功"}` ---- +*** ### 4.2 WebSocket 接口(主动推送) + #### 1. WebSocket 地址:`ws://ip:port/ws/real-time` + **推送内容**:实时数据(0.5秒/次) + ```json { "type": "real_time", @@ -765,7 +835,9 @@ GET /api/alarm/list?page=1&size=20&no=L1&type=current&start_time=2026-05-16T10:3 ``` #### 2. WebSocket 地址:`ws://ip:port/ws/alarm` + **推送内容**:实时报警事件(触发即推) + ```json { "alarm_type": "line_alarm", @@ -794,30 +866,37 @@ GET /api/alarm/list?page=1&size=20&no=L1&type=current&start_time=2026-05-16T10:3 ``` ## 五、数据存储设计 + ### 5.1 JSON配置文件 + - 路径:`/config/` - 文件:`device.json`、`channel.json`、`setting.json` ### 5.2 SQLite报警数据库 + 表:`alarm_event` -| 字段 | 类型 | 说明 | -|------|------|------| -| id | INTEGER | 主键 | -| alarm_type | TEXT | 告警类型(line_alarm,ai_alarm,operate_alarm) | -| time | DATETIME | 事件发生时间 | -| no | TEXT | 线路号/通道号 | -| type | TEXT | 类型 | -| content | TEXT | 告警详情 | -| level | TEXT | 告警等级 | + +| 字段 | 类型 | 说明 | +| ----------- | -------- | ------------------------------------------ | +| id | INTEGER | 主键 | +| alarm\_type | TEXT | 告警类型(line\_alarm,ai\_alarm,operate\_alarm) | +| time | DATETIME | 事件发生时间 | +| no | TEXT | 线路号/通道号 | +| type | TEXT | 类型 | +| content | TEXT | 告警详情 | +| level | TEXT | 告警等级 | ## 六、安全与可靠性设计 + 1. **密码加密存储**,接口权限校验 2. WebSocket断线自动重连 3. 配置文件修改自动备份 4. 服务异常自动重启 ## 七、总结 + 本方案基于**RK3568平台**,采用**FastAPI + HTTP + WebSocket**架构,满足电气量测控平台全部需求: + - RESTful API 负责配置、查询、控制 - WebSocket 负责实时数据、报警数据主动推送 - JSON 存储配置,SQLite 存储报警 @@ -825,5 +904,4 @@ GET /api/alarm/list?page=1&size=20&no=L1&type=current&start_time=2026-05-16T10:3 系统架构轻量化、高性能,完全适配嵌入式平台运行。 ---- - +***