Skip to content

4. 接口参考 ​

本章按功能分组给出 DASS2 的 HTTP 接口契约。所有接口:

  • BaseURL:http://<设备地址>:8085
  • 路径前缀:/v1
  • 鉴权:除标注「免认证」者外,均需携带 beegosessionID + 同名用户 Cookie(见 2.3)
  • 响应:统一信封 {"code","msg","count","data"},成功为 code:0 + msg:"ok"(见 2.2)

4.0 分组总览 ​

组主题前缀本章
1登录与鉴权/v1/login /v1/logout /v1/isLogin /v1/lang /v1/setLang4.1
2采集服务管理/v1/das/*4.2
3驱动管理/v1/driver/*4.3
4采集测点/v1/point/*4.4
5上传点/v1/upoint/*4.5
6系统信息与资源/v1/baseInfo /v1/resource /v1/flow /v1/mqtt/* /v1/system/license4.6
7实时与历史数据/v1/calcModel/* /v1/opconsole/*4.7
8系统运维/v1/system/*4.8

编排约定:核心链路接口给出完整字段表与示例;次要接口给出字段表与响应结构;纯管理界面用途的接口合并为表格列出,但保证路径完整。

【警告】标注 ⚠️破坏性 的接口会删除数据或中断业务,执行前请阅读 7.6。


4.1 登录与鉴权 ​

4.1.1 POST /v1/login — 登录 ​

项值
实现MainController.Login(controllers/default.go:74)
路由beego.Router("/v1/login/", &controllers.MainController{}, "*:Login")
鉴权免认证(URL 含 login)
Content-Typeapplication/x-www-form-urlencoded

请求字段:

参数名位置类型必填说明
usernameformstring是用户名。前后空格会被 trim
passwordformstring是md5(明文) 的小写 hex,32 位

响应字段:

字段类型说明
codeint0 成功;-1 失败
msgstring"ok" / "error"
countint恒为 0,不要拿来判成功
data.Userobject用户对象(Password 被清空,含 Menus 菜单树)
data.IsFirstbool是否首次启动

响应头下发 3 个 Cookie(beegosessionID / lang / <用户名>),见 2.3.2。

失败情况:

场景响应
用户名或密码为空{"code":-1,"count":0,"data":"只允许POST方法","msg":"error"}(实测:非 POST 时)。空值走密码错误的同分支
密码错误{"code":-1,"count":0,"data":"账号或密码错误","msg":"error"}
非 POST 方法{"code":-1,"count":0,"data":"只允许POST方法","msg":"error"}

【注意】该方法不区分「用户不存在」「密码错」「传了明文密码」——三种情况都回同一句 账号或密码错误(有意的防枚举设计),排障时不要指望靠报错定位。

4.1.2 POST|GET /v1/logout/ — 注销 ​

项值
实现MainController.Logout(controllers/default.go:142)
鉴权免认证(URL 含 logout)——但代码里会直接取 session

请求参数:无。

响应:成功 {"code":0,"count":1,"data":"注销成功","msg":"ok"}

【警告】未登录时调用会返回 HTTP 500(实测)。原因是 Logout() 第一行就执行 self.GetSession("user").(*system.User),session 为空时类型断言 panic。注销前请确保处于登录态,或容忍 500。

4.1.3 GET /v1/isLogin/ — 判断登录态 ​

项值
实现MainController.IsLogin(controllers/default.go:153)
鉴权需认证("isLogin" 含大写 L,匹配不到白名单的 login)

响应:已登录 {"code":0,"count":1,"data":{"User":{...},"IsFirst":false},"msg":"ok"}

未登录:HTTP 302 重定向到 /(不是 401,也不是 JSON)。

【注意】该接口可用于「轻量探测登录态 + 拿菜单树」,但必须处理 302。

4.1.4 语言设置 ​

方法路径实现说明
任意/v1/langBaseController.Language返回当前语言
任意/v1/setLangBaseController.SetLangVer设置语言并写 lang Cookie

参数:lang(query,zh-CN / en-US)。

【警告】带有效 ?lang=xx 调用任意 /v1 接口时,Prepare 会先返回 302 到去掉 query 的同一路径。用 curl 记得加 -L,否则拿到的是空的重定向响应而不是业务数据。


4.2 采集服务管理(/v1/das/*) ​

采集服务是 DASS2 的核心对象:一条采集服务 = 一个驱动实例 + 一组配置 + 一组测点。{id} 即 2.4 所述的 dasId。

4.2.1 GET /v1/das — 查询采集服务列表 ​

项值
实现DasController.Get(controllers/data/das.go:57)
鉴权需认证

请求参数:

参数名位置类型必填说明
idpathint否指定 dasId 精确查询
typequerystring否驱动名模糊匹配
destTypequerystring否驱动类型:das / upload
descquerystring否描述模糊匹配
offsetqueryint否缺省时被置为 1000(注意,不是 0)
limitqueryint否限制条数

响应字段:见 2.4 的完整字段表(id / name / type / driverType / port / desc / host / isLink / nodeName / status / total / timeout)。

【注意】offset 缺省被置为 1000 是刻意的兜底(保证一次能捞全),但它意味着默认不分页。服务很多时请注意 offset 语义与 4.3 的驱动列表不一致。

4.2.2 POST /v1/das — 创建采集服务 ​

项值
实现DasController.Post(controllers/data/das.go:112)
鉴权需认证
Content-Typeapplication/x-www-form-urlencoded

请求字段(平铺表单,不是 JSON):

参数名位置类型必填说明
das_nameformstring是服务名。必须匹配 ^[A-Za-z0-9_]+$,长度 ≤ MaxLengthServiceName(实测 11),全局唯一
das_descformstring是服务描述,不能为空
driver_nameformstring是驱动名(来自 GET /v1/driver 的 name)
(驱动配置字段)formstring视驱动其余键 = GET /v1/driver/conf/{driverId} 返回项的 key,如 node_name、isLink 等。密码类字段需 base64(AES(明文))

后端自动填充(不要传):web_listen_address = :<自动分配的端口>、web_path = ../www、node_name 会拼节点并转大写、isLink 缺省为 "true"。

端口分配:采集服务从 5000~7500、上传服务从 7500~10000 自动分配(与《使用手册》一致;实测实例为 5480 / 9486)。

响应:成功返回建好的服务对象(data 含 id / name / isLink / host 等)。建服务后会自动启动该服务。

失败情况:

场景data
超过服务数上限(Service.MaxNumber,实测 10)超过服务数量限制
服务名为空服务名不能为空
服务名超长服务名称超过长度限制
服务名格式非法服务名格式错误
服务名已存在服务已存在
驱动不存在驱动不存在
描述为空描述信息不能为空
设备型号与授权不匹配设备授权不匹配

【提示】建服务前必须先调 GET /v1/driver/conf/{driverId}?scope=addServer 与 ?scope=advanced,把两段的 key 拼成表单字段;否则会因缺少驱动必需配置而失败。详见 4.3。

4.2.3 DELETE /v1/das/{id} — 删除采集服务 ⚠️破坏性 ​

项值
实现DasController.Delete(controllers/data/das.go:304)
鉴权需认证

路径参数:id = dasId。

响应:成功 {"code":0,"count":1,"data":"删除成功","msg":"ok"}

动作:删除实时库节点 → 完整删除服务文件与数据库记录 → 清内存状态 → 通知云端。

限制:W3.DATAPOINT 节点不支持删除,返回 W3.DATAPOINT节点不支持删除。

4.2.4 GET /v1/das/conf/{id} — 读取服务配置 ​

项值
实现DasController.GetConf(controllers/data/das.go:650)
鉴权需认证

响应(实测 count: 17):data 是配置项数组,每项字段:

字段类型说明
keystring配置项名(改配置时的表单字段名)
valuestring当前值。密码类为 base64(AES(明文))
namestring中文名
edstring说明 / 取值提示
exScopestring作用域:advance / advanced
textTypestring控件/校验类型:select / disable / ispassword / password / 空
textDatastring枚举候选,a|b|c 形式
driver / model / flag / id / data1 / data2—辅助字段

特殊分支:id == 10000 时返回云平台连接配置(services.GetMagusCloudDasConf()),不走服务查询。

失败情况:dasId 不存在时返回 {"code":0,"count":0,"data":"DAS服务配置错误…no row found","msg":"error"} —— 注意 code 是 0(见 7.2)。

4.2.5 GET /v1/das/conf/third?name={服务名} — 按服务名读配置 ​

同 4.2.4,但用 name(query)寻址。适合按名字定位而不知道 dasId 的场景。

4.2.6 PUT /v1/das/conf/{id} — 更新服务配置 ​

项值
实现DasController.PutConf(controllers/data/das.go:784)
鉴权需认证
Content-Typeapplication/x-www-form-urlencoded

请求字段:平铺表单,键名 = 4.2.4 返回项的 key。其中 desc 键会同时更新服务描述。

行为:与驱动的模板配置比对,缺失的 key 自动补插;保存后自动重启该服务使配置生效。

响应:成功 {"code":0,"count":1,"data":"修改成功","msg":"ok"}

【注意】改配置会重启服务(业务会短暂中断)。批量改多个服务时注意叠加影响。

4.2.7 服务启停与重启 ​

接口实现说明
POST /v1/das/stop/{id}DasController.Stop(das.go:434)停止采集服务
POST /v1/das/boot/{id}BootDasController.Boot(das.go:507)启动采集服务
POST /v1/das/reboot/{id}DasController.Reboot(das.go:575)重启(内部 stop → 等 2 秒 → boot)

响应:成功 {"code":0,"count":1,"data":"<中文成功文案>","msg":"ok"}

幂等反馈(是失败语义但属正常):

场景data
服务已在运行采集已经运行
服务已停止采集已经停止

双机热备限制(见 7.21):

角色被禁止文案
masterstop / reboot主机禁止停止服务
slaveboot备机禁止启动服务

【警告】这三个路由注册为 *:方法,用 GET 访问也会真的执行(见 7.5)。
【警告】/v1/das/boot/{id} 的 {id} 必须是数字 dasId。代码里虽有按 name 查的分支,但路由只定义了 :id,该分支不可达(见 7.17)。

项值
实现DasController.GetDeviceStatus(controllers/data/das.go:1146)
鉴权需认证

响应(实测):{"code":0,"count":1,"data":true,"msg":"ok"}

data 为布尔值:true 表示该采集服务与设备的链路正常。

【注意】这个接口比列表里的 status 更可信:/v1/das 的 status 来自内存态,进程还在但链路断了时仍可能显示 running。做健康检查请用本接口。

4.2.9 日志相关接口 ​

方法路径实现请求参数说明
任意/v1/das/log/exist/{id}LogIsExist(das.go:961)id=dasId判断日志文件是否存在
任意/v1/das/log/{id}DownloadLog(das.go:905)id=dasId下载服务日志(文件流)
任意/v1/das/displayLog/{id}LogList(das.go:985)id=dasId列出日志文件
任意/v1/das/downloadFileDownloadLogs(das.go:946)fileName(query)按文件名下载日志
任意/v1/das/runtime/{id}RuntimeLog(das.go:1044)id=dasId服务实时运行日志(反代到采集服务自身端口的 /sys/logs)
POST/v1/das/serviceLogServiceLog(das.go:1170)JSON body {"sn":"<服务名>"}列出该服务的日志文件

日志清单响应:data 为文件信息数组,元素含 Name / Size / IsDir / ModTime / Mode / Dir。

【注意】/v1/das/runtime/{id} 是反向代理:DASS2 内部再请求 http://127.0.0.1:<该服务端口>/sys/logs,因此它的响应结构由采集服务自己的 Web 接口决定,不受 DASS2 统一信封约束。采集服务未运行时该接口会失败。
【注意】/v1/das/serviceLog 与 /v1/das/conf/?:sn(POST 取配置)用的是 JSON 请求体,而 /v1/das 的创建/改配置用的是表单——同一前缀下 Content-Type 约定不统一。

4.2.10 POST /v1/das/conf/{sn} — 按 SN 取配置(JSON 体) ​

项值
实现DasController.GetConfBySN(controllers/data/das.go:1201)
请求体{"sn":"<服务名>"}
鉴权需认证

另有 /v1/das/conf/encrypt/{sn}(GetConfEncryptBySN,das.go:1280)——新增版本,不过滤密码加密字段。

4.2.11 其它 ​

方法路径实现说明
GET/v1/das/appAppGet(das.go:391)关键字查询(desc / offset / limit;offset 缺省 10000)
任意/v1/das/validate/{name}ValidateDasController.Validate(das.go:1010)校验服务名是否合规(建服务前预校验)
POST/v1/das/history/{id}HistoryRecall(das.go:1090)历史回补。参数 time,格式为 开始时间 - 结束时间(用 - 分隔,两侧各一个空格)
任意/v1/das/linkOPLinkPool(das.go:1128)实时库连接信息(HOST/PORT/User/Pwd/Version)
DELETE/v1/das/delete/{id}DeleteLocalServer(das.go:359)双机热备:仅删除本机服务记录,不动实时库

【警告】/v1/das/link 返回实时库明文口令(Pwd),与 7.11 同类问题,不要外传响应内容。
【注意】HistoryRecall 要求服务处于 running 状态,否则报 采集已经停止,无法下发命令;time 参数用固定分隔串切分,格式不合法会直接数组越界。


4.3 驱动管理(/v1/driver/*) ​

4.3.1 驱动是什么 ​

驱动 = dastemplate/<驱动名>/ 下的可执行程序(或动态库)+ Version.ini 元数据 + config.csv 参数模板。
一条采集服务 = 某个驱动的可执行文件 + 一组配置参数跑起来的进程。

驱动的三个来源:

来源说明
出厂内置随镜像的 dastemplate/ 目录预置,不经 HTTP 接口
接口上传POST /v1/driver,本组唯一的写入入口
应用市场GET /v1/driver/appMarketURL 只返回市场地址,前端跳转过去;市场下载安装不走这三个路由

4.3.2 GET /v1/driver — 查询驱动列表 ​

项值
实现DriverController.Get(controllers/data/driver.go:44)
鉴权需认证

请求参数:

参数名位置类型必填说明
idpathint否驱动 ID 精确匹配。非数字按 0 处理(等价于查全部)
typequerystring否驱动类型模糊匹配(das / upload)
namequerystring否驱动名模糊匹配
descquerystring否描述模糊匹配
limitqueryint否实为 SQL 的起始行偏移
offsetqueryint否实为 SQL 的每页条数;传 0 或缺省被强制为 20

响应(实测):

json
{"code":0,"count":2,"data":[
  {"desc":"GB_Demo_Jenkins 更新说明:new","fileName":"GB_Demo_Jenkins.v3.0.0-beta3.20250418.arm.tar.gz",
   "id":97,"isAuth":false,"name":"GB_Demo_Jenkins","protocal_version":"",
   "publishTime":"2025-04-18 13:50:54","type":"das",
   "uploadTime":"2017-04-18 22:33:42","version":"v3.0.0-beta3"},
  {"desc":"upload mqtt。","fileName":"OP2_MQTT_JC.v3.0.5.20250925.arm.tar.gz",
   "id":96,"isAuth":false,"name":"OP2_MQTT_JC","protocal_version":"",
   "publishTime":"2025-08-18 11:46:14","type":"upload",
   "uploadTime":"2017-04-18 22:11:40","version":"V3.0.4"}
],"msg":"ok"}
字段类型说明
idint驱动 ID,/v1/driver/conf/{id} 要用它
namestring驱动名,建服务时的 driver_name
typestringdas=采集 / upload=上传
versionstring驱动版本
fileNamestring上传时的原始包名
descstring描述
uploadTime / publishTimestring上传时间 / 发布时间
isAuthbool恒为 false(后端 SQL 未查询该列,不是真实授权状态)
protocal_versionstring拼写少一个 o(应为 protocol_version),对应驱动包 Version.ini 的 commStyle

【警告】分页参数与 SQL 语义相反(LIMIT <limit>,<offset>):limit 是"跳过几条"、offset 是"取几条"。而且不带参数时 offset 被强制为 20 → 生成 LIMIT 0,20,能查到 20 条;但 ?offset=0 也会被改成 20。要取第 2 页请传 ?limit=20&offset=20。
【注意】该接口的 name / type / desc 会直接拼接进 SQL 的 LIKE 子句,未参数化(见 7.24)。

4.3.3 POST /v1/driver — 上传驱动包 ​

项值
实现DriverController.Post(controllers/data/driver.go:89)
鉴权需认证
Content-Typemultipart/form-data

请求参数:

参数名位置类型必填说明
driverform(file)file是文件字段名固定为 driver

响应:成功 {"code":0,"count":1,"data":"上传成功","msg":"ok"};失败 {"code":-1,"count":1,"data":"<错误文案>","msg":"error"}

包格式与内容要求:

项要求
容器格式.tar.gz(gzip + tar),不是 zip
目录结构dastemplate/<驱动名>/ 下必须有 Version.ini 与同名可执行文件;可选 config.csv、lib/*.so
Version.ini 键name(驱动名)、type(必须 das 或 upload)、suffix、version、alias_name、description、publish_time、commStyle、platform
platform 取值qualcomm / arm / linux / windows / loongson / all;校验是子串包含,all 恒通过
可执行文件后缀.exe / .dll / .so / .jar

常见失败文案:

场景data
平台不兼容Get driver package not compatible with platform.
type 既非 das 也非 upload, 请上传采集或上传驱动程序包
缺少可执行文件The driver executable file does not exist.
缺少 Version.inican not found Version.ini.
扩展名非 .tar.gz文件格式必须是tar.gz

【警告】同名驱动上传 = 覆盖升级,无法拒绝。而且上传成功后会把所有正在使用该驱动的采集服务停掉 → 换可执行文件 → 重启,属于有业务影响的操作。
【警告】该接口还做了部署级副作用:解压落盘、重建 example_conf 配置模板、Linux 下把 lib/*.so 拷到 /usr/lib/。这不是"传个文件"。
【注意】校验顺序是先解压落盘、后做业务校验——校验失败时文件已经留在磁盘上(只回滚数据库)。

4.3.4 GET /v1/driver/conf/{id} — 读取驱动配置字段(建服务前必做) ​

项值
实现DriverConfController.Get(controllers/data/driver.go:185)
鉴权需认证

请求参数:

参数名位置类型必填说明
idpathint是驱动 ID
scopequerystring否all(默认)/ noPoint / addServer / advanced / addPoint

响应(实测 count: 16):data 是配置项数组,字段:id / driver / model / exScope / name / key / value / ed / flag / textType / textData / data1 / data2。

json
{"code":0,"count":16,"data":[
  {"driver":"GB_Demo_Jenkins","name":"测点添加模式","key":"isLink","value":"true",
   "exScope":"advanced","textType":"select","textData":"true|false"},
  {"driver":"GB_Demo_Jenkins","name":"接口模式","key":"comm_mode","value":"...","textType":"select"}
],"msg":"ok"}

【提示】这个数组就是"建服务请求体"的字段清单——每项的 key 就是 POST /v1/das 的参数名,value 是默认值。
建服务必须取两段拼起来:?scope=addServer + ?scope=advanced。缺一段会漏字段。
【注意】textType 为 ispassword / password 的项,value 是 base64(AES(明文)) 密文(见 7.4)。

4.3.5 DELETE /v1/driver/{ids} — 删除驱动 ⚠️破坏性 ​

项值
实现DriverController.Delete(controllers/data/driver.go:147)
鉴权需认证

路径参数:id = 一个或多个逗号分隔的驱动 ID(如 /v1/driver/97,96)。

响应:成功 {"code":0,"count":1,"data":"删除成功","msg":"ok"}

【警告】该接口会连带删除 example_conf 表里该驱动的全部配置行——删掉后即使重新放回文件,也无法凭原模板重建,必须重新上传驱动包。
【警告】不检查驱动是否正在被服务使用:删掉后服务进程仍在跑,但下次重启会因找不到可执行文件而启不来。
【注意】ID 不存在时静默跳过,全部不存在也返回「删除成功」。

4.3.6 GET /v1/driver/appMarketURL — 应用市场地址 ​

响应:{"code":0,"count":1,"data":"http://www.magusiot.cn:8586","msg":"ok"}

data 是裸字符串。取值来自 conf/.env 的 appMarketURL,读不到则回退到硬编码默认值。无失败分支,恒 code:0。


4.4 采集测点(/v1/point/*) ​

【警告】这一组的路径参数 {id} 含义随接口而变,是本手册最重要的注意点。速查:

接口{id} / {name} 的含义
GET /v1/point/{id}测点 UID(实时库点号)
POST /v1/point无路径参数
PUT /v1/point/{id}测点 UID
DELETE /v1/point/{id}逗号分隔的测点 UID 列表
import / import/local / multimport / export / empty采集服务 ID(dasId)
empty/local/{name}采集服务名
clean / forceDelete / template完全被忽略

4.4.1 GET /v1/point — 查询采集测点 ​

项值
实现PointController.Get(controllers/data/point.go:49)
鉴权需认证

请求参数:

参数名位置类型必填说明
idpathint否测点 UID(优先于 query 的 ID)
IDqueryint否测点 UID
SNquerystring否采集服务名,精确匹配 ← 按服务查测点就用这个
PNquerystring否点名模糊匹配(_ % 会被转义)
EDquerystring否描述模糊匹配(未转义)
ASquerystring否状态过滤:Good → DS=0;其它非空值 → DS<0。该分支走实时库
DSqueryint否读出但未使用(死参数)
HOSTquerystring否本接口实际未使用(死参数)
limitqueryint否实为 SQL 起始行偏移(跳过几条)
offsetqueryint否实为 SQL 每页条数(取几条);传 0/缺省被强制为 20

实测对照(同一台设备共 4 个测点):

请求实测结果
?SN=jencount:4,返回 4 条
?SN=mqttcount:0
?limit=2(→ LIMIT 2,20)count:4,返回 2 条(UID 1026、1025,即跳过前 2 条)
?limit=0&offset=2(→ LIMIT 0,2)count:4,返回 2 条(UID 1030、1029)

响应(实测,单条测点约 80 个字段):

json
{"code":0,"count":4,"data":[
  {"ID":226,"UID":1026,"PID":0,"PN":"AX002","GN":"W3.JEN.AX002","SN":"jen","RT":0,
   "AD":"","EU":"","TV":100,"BV":0,"DT":1,"DB":0.2,"KZ":1,"DS":-32768,"PH":100,"PL":0,
   "ED":"这地方哈哈","FQ":1,"FM":2,"FLAG":"adding"}
],"msg":"ok"}

【警告】列表数据全部来自关系库(SQLite),不是实时库:DS 恒为 -32768、AV 恒为 null、TM 恒为 0。要实时值必须用 /v1/point/app 或 AS= 分支。
【警告】SN / ED / AS 直接拼进 SQL(只有 PN 走了转义),存在注入面。
【注意】SQL 出错时返回 code:0 + msg:"ok" + 空列表(静默失败),见 7.8。

4.4.2 GET /v1/point/third?name={服务名} — 按服务名取全部测点 ​

项值
实现PointController.GetThirdPoint(controllers/data/point.go:95)
参数name(query)= 采集服务名
上限data 最多 100000 条

等价于 GET /v1/point?SN={服务名} 的"一次拉全"版本。name 为空时会返回全库前 10 万条。

4.4.3 GET /v1/point/app — APP 关键字查询(带实时值) ​

项值
实现PointController.AppGet(controllers/data/point.go:949)

请求参数:ED(同时当 PN 和 ED 用)、SN、ID、AS、limit / offset(语义同 4.4.1)、HOST(本接口真正使用)。

与 4.4.1 的关键差异:DS / AV / TM 是实时库真值(实时库取不到时逐点写 DS=-32768, TM=0, AV=0)。

【警告】ED 条件在 SQL 里是用 or 拼接的(sn='x' and uid=1 or ed like '%y%'),因此传了 ED 可能返回不属于该 SN/ID 的测点。用这个接口时要么别传 SN/ID,要么别传 ED。

4.4.4 POST /v1/point — 新建单个测点 ​

项值
实现PointController.Post(controllers/data/point.go:136)
Content-Typeapplication/x-www-form-urlencoded

请求参数:SN(服务名,必填)、PN(点名,必填)、ID(实时库 UID)、DN(驱动名,用于 SR 白名单校验)、RT、SR、AN、ED、KR、EU、AD、CP、EX、DB、DT、KZ、FQ、FK、FB、TV、BV、PH、PL、EVENT、FM、PT、KT、AP、LC、H4`L4`、`C1`C8。完整含义见 5-数据字典。

响应:成功 {"code":0,"count":1,"data":"添加成功","msg":"ok"};失败 {"code":-1,"count":1,"data":"<服务名> 添加失败 GN:... error:...","msg":"error"}

校验规则:PN 非空、字符集合法、长度 ≤ 32;TV ≥ BV;PH ≥ PL;FK 传 0 会被强制改成 1;SR 必须在驱动白名单内;受 License 点数容量约束。

【注意】先写实时库、再写关系库(isLink="true" 时),实时库失败会回滚已写入的点。
【注意】SN 取自内存服务表而非数据库;dasserver 刚启动时可能短暂报「服务不存在」。

4.4.5 POST /v1/point/import/{dasId} — 点表导入(本组核心接口) ​

项值
实现PointController.Import(controllers/data/point.go:1035)
鉴权需认证
Content-Typemultipart/form-data
路径参数dasId = 采集服务 ID

请求参数:

参数名位置类型必填说明
dasIdpathint是采集服务 ID(das_service.id)
pointcsvform(file)file是文件字段名固定为 pointcsv
HOSTquery/formstring否实时库地址,空则用配置默认

上传文件要求(逐条来自源码,全部是硬约束):

项要求不满足时
扩展名必须是 .xlsx,大小写敏感(.XLSX 被拒),不支持 csv文件格式不是CSV(文案是遗留错误)
工作表名必须逐字是 Sheet1获取数据失败
表头必需列PN 与 RT(这两列大小写不敏感)不能没有PN列 / 不能没有RT列
表头列名其余列名必须全大写、在 5.2.1 的白名单内非法的字段:<列名>
行长度必须与表头长度严格相等(Excel 会丢掉行尾空单元格,所以每行都要填到最后一列)长度不匹配
空表没有任何行会 panic → HTTP 500500

导入语义:增量 + 更新。按 GN(节点名.点名)与该服务已有测点比对——已存在的走更新,不存在的走新增。不会清空原有测点,也不会删除文件里没提到的点。同一文件重复导入是幂等的。

响应:

json
// 成功(注意 count 恒为 0)
{"code":0,"count":0,"data":[],"msg":"ok"}

// 部分行入库失败(count = 文件解析出的数据总行数,不是失败行数)
{"code":-1,"count":500,"data":[{"ID":1046,"PN":"G02","Number":0,
  "Flag":"GN:W3.DEMO1.G02 ID:1046 error:UNIQUE constraint failed: point.gn"}],"msg":"error"}

// 早退错误(count = 1)
{"code":-1,"count":1,"data":[{"ID":0,"PN":"不能没有PN列","Number":0,"Flag":"不能没有PN列"}],"msg":"error"}

【警告】成功时 count 恒为 0,不是导入条数。要拿实际条数请导入前后各查一次 GET /v1/point?SN=<服务名> 的 count 做差(见 7.9)。
【警告】逐行失败原因在 data[].Flag 里(键名是 ID / PN / Number / Flag,大写)。部分行成功、部分行失败时已成功的行不会回滚。
【警告】同步阻塞、无异步任务:读 xlsx → 逐行校验 → 写实时库 → 写 SQLite → 通知采集服务,全在一个请求内完成。上万行会长时间不返回,且客户端超时断开不会中断后端处理。客户端超时请设 ≥300s,且不要自动重试(重试会再导入一次)。

常见逐行错误:见 6.5.3。

4.4.6 POST /v1/point/import/local/{dasId} — 仅导入关系库 ​

与 4.4.5 参数、格式、响应完全相同,唯一差异:只写关系库,完全不写实时库(也不推双机对端)。这是双机热备备机专用入口——让备机的关系库追上主机,实时库由实时库自身的复制机制同步。

【注意】误用后果:关系库有点、实时库没有点,两边不一致。常规导入请用 4.4.5。

4.4.7 POST /v1/point/multimport/{dasId} — CSV 批量强插 ​

项说明
文件字段名同样是 pointcsv
文件格式.csv,编码必须是 GBK(UTF-8 中文会乱码)
语义只插入、不比对、不更新(与 Import 完全不同)
容量校验无(可能超出 License)
写入顺序先写实时库 → sleep 1s → 再写 SQLite

【警告】IsExistDatabase 出错时该接口不写任何响应体,客户端会拿到空 body 的 200。
【注意】CSV 表头会被原地转大写,所以表头大小写不敏感(与 xlsx 路径不同)。

4.4.8 PUT /v1/point/{uid} / PUT /v1/point/update/{uid} — 更新测点(两者语义不同) ​

PUT /v1/point/{uid}PUT /v1/point/update/{uid}
实现PointController.Put(point.go:487)UpdateLocalData(point.go:676)
实时库写(isLink=true 时)不写(只改关系库)
双机对端同步不同步
定位uiduid
语义常规更新双机热备备机专用

【警告】PUT /v1/point/update/{uid} 未传的数值字段会被写成 0(不是"只改传了的字段")。回写前请先读取完整记录再整体提交。
【注意】PUT /v1/point/update/{uid} 的 PN 未做大写归一,与 Post 不一致,容易写出大小写不一的 GN。

4.4.9 DELETE /v1/point/{uids} — 删除测点 ⚠️破坏性 ​

路径参数:id = 逗号分隔的测点 UID 列表(如 /v1/point/1026,1025),不是 dasId、不是单个 id。

响应:{"code":0,"count":1,"data":"删除成功","msg":"ok"}

【警告】静默成功:只要列表里第一个 UID 查不到对应测点,整个删除会被跳过,但接口仍返回「删除成功」。排障时务必核对 point 表的实际行数。
【注意】它只根据第一个 UID 定位所属服务,因此跨服务批量删除时,只有第一个服务会收到刷新通知。
【注意】isLink="true" 时同时删除实时库的点。

4.4.10 GET /v1/point/empty/local/{name} — 仅清空关系库 ​

路径参数:name = 采集服务名(字符串,不是 ID)。响应 {"code":0,"count":1,"data":"清空成功","msg":"ok"}。

只清关系库,不碰实时库(对比 4.4.11)。双机热备备机专用。

4.4.11 其它接口速查 ​

方法路径实现{id} 含义说明
GET/v1/point/sr?DN={驱动名}GetPointSr(point.go:118)—返回该驱动支持的 SR 列表。data 是字符串数组
POST/v1/point/server/addServerAddPoints(point.go:987)—参数 pointIds(逗号分隔 UID)+ collectName(服务名)。把实时库已有点登记到关系库,不写实时库、不校验容量
POST/v1/point/export/{dasId}Export(point.go:1794)dasId导出该服务测点为 xlsx(文件流)
POST/v1/point/empty/{dasId}Empty(point.go:1916)dasId⚠️清空该服务全部测点(isLink=true 时连实时库一起删)
POST/v1/point/clean/{任意}ClearJunkData(point.go:1987)被忽略⚠️全局清理游离数据(不分服务),且恒返回成功
POST/v1/point/forceDelete/{任意}ForceDelete(point.go:2002)被忽略⚠️空实现,什么都不删(见 7.26)
POST/v1/point/template/{任意}DownTemplate(point.go:2016)被忽略下载通用导入模板(xlsx,实测)。任意 id 都得到同一份
POST/v1/point/insertInsertLocalData(point.go:323)—仅写关系库(备机专用)
DELETE/v1/point/delete/{uids}DeleteLocalData(point.go:898)UID 列表仅删关系库(备机专用)
GET/PUT/DELETE/v1/point/pnNPointController(npoint.go)—按点名操作;PUT/DELETE 用 JSON body;GET 的 sn 是模糊匹配

【警告】/v1/point/export/{dasId} 导出的 xlsx 里,P 列与 Q 列的标题与数据错位(H4 / LC 互换)。把导出文件再导入或人眼比对时会错位,请以导入模板的列序为准。
【警告】/v1/point/export/{dasId} 与 /v1/point/template 都先写到进程工作目录再下载删除,并发调用会互删/互相覆盖。
【注意】/v1/point/insert、/v1/point/delete/{uids}、/v1/point/update/{uid}、/v1/point/import/local/{dasId}、GET /v1/point/empty/local/{name} 都是双机热备专用:只动关系库。常规用途请用对应的非 local/insert/delete 版本。


4.5 上传点(/v1/upoint/*) ​

4.5.1 上传点与采集点的关系 ​

维度采集点 point上传点 upoint
方向设备/驱动 → 采进来实时库 → 传出去
SN 语义采集服务名上传服务名
UID 约束唯一(一个实时库点只属于一个采集服务)不唯一(同一点可被多个上传服务引用)
表的选用driverType 含 das 的服务读 point 表driverType = upload 的服务读 upoint 表
关联键UID(实时库点号)+ GN(全局名)同上

为什么需要上传点:本地实时库是共享的,同一份数据可能要同时上传给多个通道;而 point.uid 是唯一的,无法表达"一个点被多个服务引用",所以必须有第二张表放宽这个约束。上传点不是"新点",而是对实时库既有测点的订阅/投影清单——所以上传点的 PN/GN/RT 等字段都是从实时库回读得到的,不是文件里说了算。

字段结构两者同构,含义见 5-数据字典。

4.5.2 GET /v1/upoint — 查询上传点 ​

参数与 4.4.1 基本一致(id/ID = 测点 UID、SN = 上传服务名、PN、ED、AS、limit/offset 同样是反的)。

【注意】DS 与 host 在本接口是死参数;DS 恒为 -32768、AV 恒为 0(本接口不取实时值)。
【注意】DB 出错时该接口返回 code:0 + msg:"ok",而 data 是错误字符串(伪成功),见 7.8。

4.5.3 ANY /v1/upoint/import/{dasId} — 上传点导入 ​

项值
实现UPointController.Import(controllers/data/upoint.go:340)
路径参数dasId = 上传服务 ID
文件字段名pointcsv
文件格式仅 .xlsx(大小写敏感),工作表名必须 Sheet1
必需列ID(实时库测点 UID,≥1024、文件内不重复)

响应:

json
// 成功(count 恒为 0)
{"code":0,"count":0,"data":[],"msg":"ok"}

// 行级失败(count = 解析出的数据行数;Flag 是错误码或 ORM 原文)
{"code":-1,"count":3,"data":[{"ID":1046,"PN":"G02","Number":0,"Flag":"-104"}],"msg":"error"}

// 表单级错误(count = 1,PN 与 Flag 都是错误文案)
{"code":-1,"count":1,"data":[{"ID":0,"PN":"文件格式不是xlsx","Number":0,"Flag":"文件格式不是xlsx"}],"msg":"error"}

【警告】官方上传点模板是 CSV,而导入只吃 xlsx —— 拿模板直接导入必然失败(见 7.28)。
【注意】上传点导入的列白名单与采集点不同:GN 与全大写 REDATA2 会被判为「非法的字段」;表头大小写敏感(ID 除外)。
【注意】语义是按 GN 增量 upsert,不预先清空;文件里的 PN/GN 会被实时库的值覆盖,所以写什么基本不影响结果——真正决定导入内容的是 ID 列。

4.5.4 ANY /v1/upoint/export/{dasId} — 导出上传点 ​

返回文件流(application/octet-stream,文件名 <上传服务名>.xlsx),工作表 Sheet1,表头固定 13 列:

ID, AN, AD, SR, ED, UM, FK, FB, TV, BV, PH, PL, REDATA1

【警告】导出的 13 列 ≠ 导入所需的列集(没有 PN/GN/SN/CP/EU/EVENT),回灌要靠实时库补 PN/GN 才成立。
【注意】该服务下一条上传点都没有时,返回的是 {"code":-1,"data":"服务不存在"}——文案误导,实际是"没有数据"。

4.5.5 其它上传点接口速查 ​

方法路径{id} 含义说明
POST/v1/upoint—新增:pointIds(逗号分隔 UID)+ collectName(上传服务名)。按 同服务下 GN 不重复判重
PUT/v1/upoint/{uid}测点 UID更新。⚠️全量覆盖:未传字段会被清 0/清空。PN 无法通过本接口修改
DELETE/v1/upoint/{uids}UID 列表删除。⚠️只按第一个 UID 的 SN 限定范围
ANY/v1/upoint/empty/{dasId}dasId⚠️清空该上传服务全部上传点。isLink 无关,不动实时库
ANY/v1/upoint/clean/{任意}被忽略⚠️全局垃圾清理,会同时动 upoint、point 与实时库,且恒返回成功
ANY/v1/upoint/template/{任意}被忽略下载模板(实测为 CSV,见 7.28)
GET/PUT/DELETE/v1/upoint/pn—⚠️sn/pn 是小写;pn/pns 实际是全局名 GN(见 7.29);PUT/DELETE 用 JSON body;查不到会 HTTP 500

【警告】ANY 表示这些路由注册成了 *:方法——用 GET 就能触发导入/导出/清空(见 7.5)。生产环境建议在网关层限制方法。


4.6 系统信息与资源 ​

4.6.1 命名风格警告 ​

这一组的字段名大小写风格不统一,且大多没有 JSON tag(键名就是 Go 字段名):

接口风格示例
/v1/baseInfo/、/v1/resource/、/v1/system/license/、/v1/system/conf大驼峰(PascalCase)Model、DevCode、MEMTOTAL、Size
/v1/mqtt/baseInfo、/v1/mqtt/resource、/v1/mqtt/netInfo小驼峰(camelCase)boxName、licensePoint、systemTime

【警告】不要按同一套规则解析整组接口,逐接口对照。

4.6.2 GET /v1/baseInfo/ — 设备基础信息 ​

项值
实现device.BaseInfo.Get(controllers/device/running.go:28)
鉴权需认证

响应(实测,已截断 NetInfos):data 是对象(不是数组),count 为 1。

json
{"code":0,"count":1,"data":{
  "Model":"IA-2230","Version":"2.5.9.20241216","DevCode":"9TFM7-FE92M-Z1GZE-PD47H",
  "Sn":"IA22301000970001","BoxName":"","Desc":"","SysTime":1504116085,
  "NetInfos":[{"id":0,"netName":"eth0","ipv4Addr":"192.168.100.169"},
              {"id":0,"netName":"eth1","ipv4Addr":"192.168.50.121"}],
  "ActiveStandbyStatus":"","PwrStatus":"","CurrRunningTime":0,
  "TotalRunningTime":0,"Temperature":0,"FourGInfo":null,"GPSInfo":null,
  "ImageVersion":"","BoxCode":""
},"msg":"ok"}

关键字段:

字段说明
Model设备型号(实测 IA-2230)
Version软件版本(实测 2.5.9.20241216)
DevCode设备码 / 机器码,由外部程序 openplant/licrep 产生。授权就是绑这个值的
Sn序列号
BoxName / Desc盒子名称 / 描述,可写(见下)
SysTime设备当前时间(Unix 秒)
NetInfos网口数组。元素含 id/netName/ipv4Addr/status/netGroup/mask/gateway/isDHCP 等,但本接口实际只填 netName 与 ipv4Addr,其余恒为零值
CurrRunningTime实时库运行时长(秒,可能带小数)
TotalRunningTime累计运行时长(秒)
Temperature恒为 0(后端未实现采集)
ActiveStandbyStatus / PwrStatus恒为空串(未实现)
FourGInfo / GPSInfo恒为 null(要 4G 信息用 4.6.6)

【注意】多个字段"有字段没数据":温度、电源状态、主备状态、GPS、4G 都不要当真实值用。
【注意】DevCode 依赖外部可执行文件 openplant/licrep。该文件缺失时 DevCode 为空并且授权匹配会一起失效(License 的 Host 用的是同一个值)。

PUT /v1/baseInfo/ — 修改盒子名称与描述:参数 boxName、desc(form/query)。

4.6.3 GET /v1/resource/ — 设备资源占用 ​

项值
实现device.SourceInfo.Get(controllers/device/running.go:108)
鉴权需认证

实测响应:

json
{"code":0,"count":1,"data":{
  "CPU":0,"CPUTOTAL":1000,"CPUUSED":0,
  "MEM":12100,"MEMTOTAL":1,"MEMUSED":121,
  "DISK":100,"DISKTOTAL":1,"DISKUSED":1,"DISKFREE":0
},"msg":"ok"}

字段完整清单与单位:

字段单位说明
CPU%CPU 使用率
CPUTOTALMHzCPU 主频总量(实测 1000)
CPUUSEDMHz已用频率 = CPUTOTAL × CPU% ÷ 100。不是百分比
MEM%内存使用率 = 100 × MEMUSED ÷ MEMTOTAL
MEMTOTALMB内存总量。不是字节
MEMUSEDMB已用内存
DISK%磁盘使用率
DISKTOTALMB磁盘总量
DISKUSEDMB已用磁盘
DISKFREEMB空闲磁盘

【警告】单位是 MB,不是字节。 源码里有处注释写「单位:B」是错的,权威依据是 openplant 点表里 MEMTOTAL/VOLTOTAL 等点的工程单位 EU 都是 MB。按字节换算会差约 10⁶ 倍。
【警告】实测这台设备的返回值本身是失真的:MEMTOTAL=1、MEMUSED=121,导致 MEM=12100(即 12100%)。原因是内存总量取不到时被强制置为 1 以避免除零,于是比率爆掉。做监控告警前务必先判断 MEMTOTAL 是否为 1/0 这种哨兵值。
【注意】数据来自进程内缓存,最长约 3 分钟陈旧(后台每 180 秒刷新一次)。启动初期各字段可能全为 0。
【注意】实时库不可达时后台会尝试执行 sudo ./sh/op-repair(外部命令),失败只写日志。

4.6.4 GET /v1/system/license/ — 授权信息 ​

项值
实现system.LicenseController.Get(controllers/system/license.go:31)
鉴权需认证

实测响应:data 是数组(count 仍为 1,UI 取 data[0])。

json
{"code":0,"count":1,"data":[
  {"Project":"","Product":"IA-2230-Pro","Host":"9TFM7-FE92M-Z1GZE-PD47H",
   "Desc":"","Version":"","Size":1900,"Expired":"2027-03-24",
   "Atrributes":"","Valid":false}
],"msg":"ok"}
字段说明
Product授权产品名
Host设备码 / 机器码(与 /v1/baseInfo/ 的 DevCode 同源)
Size授权点数总量
Expired到期日 yyyy-MM-dd;永久授权为空串(或 "永久",视路径而定)
Atrributes授权属性(源码拼写就是 Atrributes)
Valid恒为 false(后端未赋值)→ 不要用它判断授权有效性
Project / Desc / Version恒为空串

【警告】Size 不是授权文件里的原始点数,而是 原始点数 − 100(硬编码)。实测 Size=1900 对应 /v1/mqtt/baseInfo 的 licensePoint=2000——两个接口的总量恒差 100。
【警告】Valid 恒为 false,别拿它当判据。

授权余量怎么算:

量从哪来
总量/v1/system/license/ 的 Size,或 /v1/mqtt/baseInfo 的 licensePoint(两者差 100,选定一边就别混用)
已用/v1/mqtt/baseInfo 的 userdPoint(拼写就少一个 e);等价于实时库里的 select count(*) from Point
余量总量 − 已用

实测对照:licensePoint=2000、userdPoint=5、Size=1900 → 余量约 1995。

【注意】POST 同一路径 = 上传授权(multipart 字段名 license,必须 .xml)。该操作会 kill 实时库进程、覆盖授权文件并写数据库,属高危运维动作,二次开发不要随手调。

4.6.5 GET /v1/system/conf — 读取全局配置 ​

响应:data 是一个大对象,含 Base / HostOP / Log / Grpc / Broker / Service / Point / License 八组。

实测关键值(真机):

键实测值说明
Base.MaxPnLength32点名长度上限(见 5.4)
Base.MaxLengthServiceName11服务名长度上限
Base.RequireLoginLogintrue本机免鉴权开关(见 2.3.4 与 7.12)
Base.BoxCodedefault_0011盒子编码
Service.MaxNumber10采集服务数上限
Point.MaxNumber2000测点数上限
License.DeviceIA-22授权绑定的型号前缀
Grpc.RpcClientSend127.0.0.1:7633MQTT 驱动 gRPC 目标
HostOP.IP / Port127.0.0.1 / 8200实时库地址

【警告】这是本产品最敏感的一个接口。 HostOP.Password 返回的是解密后的实时库明文口令,Broker.Password 是云平台 MQTT 口令,Base.RequireLoginLogin 顺带暴露"本机是否免鉴权"。任何已登录用户(包括低权限角色)都能读——权限模型只有菜单可见性,没有接口级 RBAC(见 7.11 与 4.8.7)。
【注意】RequireLoginLogin 这个字段名有误导性:注释写「本地访问是否鉴权」,但代码行为是为 true 时本机回环请求直接放行(即免鉴权),为 false 时回环也要登录。以代码行为为准。

4.6.6 /v1/mqtt/* — 面向云平台的上报接口 ​

这四条是给云平台 / 物联网网关(edge_mqtt_hub 等)周期上报用的契约,不是给浏览器 UI 用的(UI 用 4.6.2 / 4.6.3 / 4.6.4)。

接口用途data 形态
GET /v1/mqtt/infoMQTT 客户端自身状态(断连/发送/心跳/下载进度)对象或 null
GET /v1/mqtt/baseInfo扁平版设备基础信息 + 授权对象
GET /v1/mqtt/resource扁平版资源占用 + 运行时长对象
GET /v1/mqtt/netInfo网络信息(4G / IP 列表 / 流量)对象

【警告】这四条 mqtt 接口全部需要登录,没有免登录白名单。已核实:edge_iothub 自身就是先 POST /v1/login/ 拿 Cookie、再把 Cookie 附到每个 /v1/mqtt/* 请求上。不要指望它们像"上报口"那样开放。

GET /v1/mqtt/baseInfo(实测) ​

json
{"code":0,"count":1,"data":{
  "boxName":"","version":"2.5.9.20241216","hardVersion":"IA-2230",
  "mirrorVersion":"IA22xx_2.5.8_20240321320","devcode":"default_0011",
  "sn":"IA22301000970001","expire":"2027-03-24",
  "licensePoint":2000,"userdPoint":5
},"msg":"ok"}
字段说明
version软件版本
hardVersion型号(名字有歧义,不是"硬件版本")
mirrorVersion系统镜像版本
devcode盒子编码(.env 的 box.code)。⚠️ 与 /v1/baseInfo/ 的 DevCode(licrep 机器码)不是同一个值
expire授权到期日
licensePoint授权点数总量(未减 100,与 /v1/system/license/ 的 Size 差 100)
userdPoint已用测点数(拼写少一个 e)

【警告】若设备没有任何有效授权(关系库无匹配且 license.xml 打不开/为空/机器码不匹配),该接口会因 nil 解引用 panic → HTTP 500。

GET /v1/mqtt/resource ​

字段:systemTime(秒)、currentRunningTime(秒,float,可能带小数)、totalRunningTIme(注意 TIme 的大小写)、memTotal/memUsed/diskTotal/diskUsed(MB)、mhz(MHz)、usedPercent。

【警告】usedPercent 名不副实:它实际是已用频率(MHz),不是百分比。要真百分比请用 /v1/resource/ 的 CPU。
【警告】currentRunningTime 是 float32,会出现 500036.78 这样的值。消费方若声明成 int64 会反序列化失败导致整块数据取不到。

GET /v1/mqtt/netInfo ​

字段:fourGConnectStatusCode、fourGCSQ、fourGFlow(MB)、Ips、以及 WiFi / 热点相关字段。

【注意】Linux 实现下 WiFi 与热点的字段是写死的常量 0/空串,没有采集逻辑——不要把"WiFi 未连接"当真实状态。只有 4G 的三项有真值。
【注意】Ips 的键是大写 I,且值是 [{"ip":"192.168.1.10"}] 这样的对象数组,不是字符串数组。按 []string 解析会失败。

4.6.7 GET /v1/flow/ — 网口流量(空壳) ​

实测响应:{"code":0,"count":1,"data":"","msg":"ok"}

data 恒为空字符串。后端虽有 FlowResponse 结构定义,但全仓没有任何写入方——这个接口拿不到流量数据。要网口信息请用 /v1/baseInfo/ 或 /v1/mqtt/netInfo。

4.6.8 其它 ​

方法路径说明
PUT/v1/baseInfo/修改 boxName / desc
ANY/v1/baseInfo/conf⚠️名字极具误导性:它不改型号/序列号/平台,只有 menuAuth 参数会写 conf/auth.conf;执行后 3 秒 os.Exit(0) 杀掉整个 dasserver 进程(由守护拉起)
GET/v1/system/first/首次启动时间。⚠️当前代码下必然 HTTP 500(会话键 userId 从未写入 → 类型断言 panic)。需要该值请直接读设备上的 /usr/lib/system32box.so

【警告】/v1/baseInfo/conf 会直接杀掉 dasserver 进程(不是热重载)。虽然守护会把它拉起来,但这属于中断性操作。


4.7 实时与历史数据(/v1/calcModel/*、/v1/opconsole/*) ​

【警告】这一组里有一个致命陷阱,请先读 4.7.2:/v1/calcModel/gethistorybyid 返回的是伪造的演示数据,不是真实历史值。只有 /v1/opconsole/gethistorybyid(s) 能取到真实归档数据。

4.7.1 GET /v1/calcModel/list / detail/{modelid} — 计算模型 ​

接口实现说明
GET /v1/calcModel/listCalcModelController.Get(controllers/data/calcModel.go:78)计算模型列表
GET /v1/calcModel/detail/{modelid}ModelDetail(calcModel.go:132)模型详情与关联点 ID

实测 /v1/calcModel/list:

json
{"code":-1,"count":0,"data":"Get TimeStamp Error!","msg":"error"}

原因(源码确认):该组接口会先去请求一个外部 HTTP 服务获取时间戳与签名:

go
func getTimeStamp() { http.Get(GetTimeStamp) ... }
func getSign(timestamp, uri string) { http.Get(GetSign + "?param=appId=OFM...&secretKey=...&uri=" + uri) ... }

这个地址指向第三方(西门子侧)服务,在设备本地网络里通常不可达,于是报 Get TimeStamp Error!。

【警告】该接口不是纯本地接口,依赖外部网络。返回值不可用时无法通过本地配置修复。
【注意】getSign 里硬编码了 secretKey。这是源码中的凭据硬编码问题,不要外传。

4.7.2 GET /v1/calcModel/gethistorybyid — 历史数据(返回伪造数据) ​

项值
实现CalcModelController.GetArchiveByIDWithTime(controllers/data/calcModel.go:226)
请求参数HOST、begin、end、interval、ID

【警告】这个接口返回的是随机生成的假数据,不是真实历史值。

源码里 opconsole.GetArchive() 的第一行就是 return Temp(begin, end, idStr, interval),它下面所有真实的归档查询逻辑全是不可达的死代码。而 Temp() 的实现是这样的:

go
realTimeValue = []float64{40.0267, 50, 60, 70, 80, 90}
avAndtms = append(avAndtms, AVAndTM{
    AV: realTimeValue[num] + float64(rand.Float64()*10-5),   // 加随机抖动
    TM: int64(beginInt),
})
...
archiveData.PN = "TEST_POINT_0005"
archiveData.GN = "W3.DATAPOINT.TEST_POINT_0005"

也就是:曲线值来自一个硬编码的常量数组 + 随机数,点名恒为 TEST_POINT_0005。

实测两次不同请求的对比(ID 分别是 1026 与 999999):

json
{"code":0,"count":1,"data":[{"PN":"TEST_POINT_0005","GN":"W3.DATAPOINT.TEST_POINT_0005",
 "EstimatedCurve":[{"AV":90,"TM":1504116600},{"AV":80,"TM":1504116540}, ...],
 "ResidualCurve":[{"AV":0.033713963604995456,...}, ...]}]}

两次响应的 PN/GN 完全相同,曲线值也是同一组常量,只有 ResidualCurve 的抖动值不同。

【警告】任何基于此接口做历史分析、报表或趋势判断的实现都会得到无意义的结果。 需要真实历史数据请用 4.7.4 的 /v1/opconsole/gethistorybyid。

4.7.3 GET /v1/opconsole/getrealtime/ — 实时数据查询(真实数据) ​

项值
实现OPConsoleController.GetRealTime(controllers/opconsole/opdata.go:202)
鉴权需认证
数据源实时库 Realtime 表(SQL 直查)

请求参数:

参数名位置类型必填说明
collectNamequerystring否采集/上传服务名。传了会把该服务全部点的 UID 追加进 idsFilter
idsFilterquerystring否逗号分隔的测点 UID 列表
Nodequerystring否节点名,过滤 GN like '<Node>.%'
PNquerystring否点名模糊匹配
ED / ANquerystring否描述 / 别名模糊匹配
RTquerystring否类型过滤,拼进 RT in (...)
IDquerystring否精确 ID
fieldquerystring否返回字段列表。缺省为 ID,GN,PN,AN,RT,ED,EU,KR,DS,AP,TM
orderquerystring否排序
offsetqueryint否实为 SQL 起始行偏移;缺省被置为 20
limitqueryint否实为 SQL 每页条数
HOSTquerystring否实时库地址

响应:data 为行对象数组,count 为命中总数。

【警告】offset / limit 语义与直觉相反(同 4.4.1 与 4.3.2,后端拼 limit <offset>,<limit> 风格)。offset 缺省为 20。
【警告】Node / PN / RT / ID / field 等参数直接拼接进 SQL,存在注入面。field 尤其危险——它整段进 select 子句。
【注意】field 里若不含 ID,后端会自动补在最前面。

4.7.4 GET /v1/opconsole/gethistorybyid/ 与 /gethistorybyids/ — 历史数据(真实数据) ​

项gethistorybyidgethistorybyids
实现GetArchiveByIDWithTime(opdata.go:821)GetArchiveByIDsBetweenTime(opdata.go:851)
点参数ID = 单个测点 ID(实时库 ID)IDs = 逗号分隔的测点 ID 列表
时间参数begin / endbegin / end
HOST可选(实际被忽略)可选(实际被忽略)
底层 SQLselect ID,GN,TM,DS,AV from W3.Archive where ID =<ID> and TM between '<begin>' and '<end>' and mode='span' and interval=1同左,ID in (<IDs>)
data 结构完整测点对象数组(80+ 字段,TM 为毫秒)原始行数组,只有 5 个键:AV / DS / GN / ID / TM(TM 为秒)
count该点的行数所有点的行数合计

时间参数格式(实测两种都可用,推荐日期时间字符串):

格式示例实测结果
日期时间字符串(推荐)begin=2017-08-30 17:00:00&end=2017-08-30 21:00:00count: 14401(4 小时 = 14400 秒,正好每秒 1 条)
数字 Unix 秒begin=1504110000&end=1504120000同样返回数据(该窗口 count: 6593)

【警告】查询精度被写死为「1 秒 1 个点」:SQL 里 mode='span' and interval=1 是硬编码的,调用方无法指定原始值、最大/最小/平均值,也无法放大采样间隔。所以时间窗口必须开得很小——实测 4 小时单点就有 14401 条,跨天查询会直接把设备拖垮。
【警告】没有任何行数上限、也没有分页参数。实测 4 小时区间响应体约 1MB 量级;多点(byids)行数 = 点数 × 时间点数,极易打爆内存。请自行把窗口切成 ≤1 小时并串行请求。
【警告】两个接口的 data 结构与 TM 单位都不同(byid 毫秒 / byids 秒),不能共用同一套解析器。
【警告】ID / IDs / begin / end 都直接拼接进 SQL,存在注入面。
【警告】只有本接口能取到真实历史数据;/v1/calcModel/gethistorybyid 返回的是假数据(见 4.7.2)。

实测样本:

json
// gethistorybyid,ID=1026
{"code":0,"count":6593,"data":[{"ID":0,"UID":1026,"PN":"AX002","GN":"W3.JEN.AX002",
  "AV":15902161,"DS":0,"TM":1504116592000, ...}]}

// gethistorybyids,IDs=1026,1025
{"code":0,"count":22,"data":[
  {"ID":1025,"GN":"W3.JEN.AX001","TM":1504098000,"DS":0,"AV":15883569}]}

【注意】gethistorybyid 返回的对象里 ID 恒为 0,真正可用的测点标识在 UID 字段。这是映射实现导致的(SQL 里的 ID 被写进了 UID)。

4.7.5 其它 OPConsole 接口速查 ​

/v1/opconsole/* 是给内置 OPConsole 前端用的数据面接口,直接读写实时库。二次开发如非必要不建议依赖(参数即 SQL 片段、无分页保护、无参数化)。

只读:

方法路径关键参数说明
任意/v1/opconsole/getdatabase/HOST库列表(实测返回 ID/ED/PN/GN/CT/UD)
任意/v1/opconsole/getnodelist/HOST、db_name、PN、filter、children节点列表
任意/v1/opconsole/getnode/HOST节点
任意/v1/opconsole/getnodedetail/HOST、DBName节点详情
任意/v1/opconsole/getpointfield/HOST可用字段列表(配合 getrealtime 的 field 用)
任意/v1/opconsole/getpointinfo/HOST点信息
任意/v1/opconsole/exportpoint/HOST、Node、PN、ED、AN、RT、field导出点(文件流)
任意/v1/opconsole/getProxyConn/HOST当前实时库连接信息
任意/v1/opconsole/TestConn/HOST、PORT、User、Pwd测试连接

写操作(⚠️ 直改实时库):

方法路径关键参数说明
任意/v1/opconsole/addpoint/HOST、Node、PN、RT、ID 及大量点属性⚠️ 向实时库新增点
任意/v1/opconsole/updatepoint/同上⚠️ 修改实时库点
任意/v1/opconsole/removepoints/HOST、ids⚠️ 删除实时库点
任意/v1/opconsole/importpoint/HOST⚠️ 导入点(文件)
任意/v1/opconsole/setNodeLc/HOST、GN、LC设置节点 LC
任意/v1/opconsole/UpdateConn/HOST、PORT、User、Pwd改实时库连接
任意/v1/opconsole/Reconnect/HOST、PORT、User、Pwd重连实时库
POST/v1/opconsole/calc/addpointpn、rt、pt、kt、ex、ed、an、eu、unit新增计算点
POST/v1/opconsole/calc/editpn、kt、ex、ed、an、eu、node编辑计算点
POST/v1/opconsole/calc/removepointssn、pns删除计算点
POST/v1/opconsole/calc/getpointinfosn、pn计算点信息

【警告】上表"写操作"全部直接改动实时库,绕过 DASS2 的关系库与 isLink 联动逻辑。误用会造成关系库与实时库不一致(表现为 /v1/point 查不到但实时库有点,或反之)。常规点表维护请用 4.4 的 /v1/point/*。
【警告】这些路由注册成了 *:方法——GET 也会真的执行写操作。


4.8 系统运维(/v1/system/*) ​

4.8.1 系统服务管理(/v1/system/service/*) ​

【警告】先分清两套"服务"——这是本组最容易搞错的地方:

/v1/system/service/*/v1/das/*
管什么设备端系统服务 / 进程:dasserver、magusdog、openplant(实时库)、auth-center、edge_mqtt_hub 等DAS 采集服务实例
主键name(服务名)数字 id(dasId)
数据源magusdog 的管理面(127.0.0.1:8089)dasserver 自己的 db/data.db
启停执行者magusdog(守护程序)dasserver 自己
有守护/自启概念有(Valid = 守护开关)无

一句话:/v1/system/service/* 是盒子级进程编排(改它 = 改开机自启与存活),/v1/das/* 是采集业务编排。不要拿 service/status 去启停采集服务。

方法路径关键参数说明
GET/v1/system/service/id(被忽略)服务列表。实质只返回前 50 个,无分页
POST/v1/system/service/multipart 文件字段 systemService⚠️ 上传系统服务包(解包+落盘+装库+写配置)
PUT/v1/system/service/无死代码:写死了 proxy 服务,不要用
DELETE/v1/system/service/{服务名}id = 服务名(支持逗号)⚠️ 删除守护记录。多选删除静默失效(把 a,b,c 当成一个名字)
GET/v1/system/service/statusname(服务名)、status(start/stop)⚠️ 启停系统服务
ANY/v1/system/service/protect/Name、Valid、Path 等(整行回传)守护开关(Valid=true 开守护)。⚠️ 是 upsert,名字不存在会新建
GET/PUT/v1/system/service/conf/{服务名}GET 用 path;PUT 必须在表单里再传一次 name读写系统服务的配置项(example_conf 表)
POST/v1/system/service/add/name、path、version、description新增服务记录(默认 守护开 + 标为运行)
GET/v1/system/service/displayLog/{服务名}name日志文件列表
GET/v1/system/service/log/{服务名}name(正则 [\w]+,不能含 -)、fileName⚠️ 下载日志,下载后文件被删除
GET/v1/system/service/log/exist/{服务名}name判断日志是否存在

服务列表的字段(大驼峰,来自 magusdog):Name / Path / PID / Valid(守护开关) / Running / Version / StartDelay / LastTime / Desc / Error。

【警告】Valid 才是守护开关(不是它字面的"有效")。Valid=true 表示该服务会被 magusdog 守护拉起。
【警告】GET /v1/system/service/status?name=<服务名>&status=stop 停服务时,magusdog 会同时关闭守护(否则会被立刻拉起)。停掉 dasserver 就是停掉本接口自身。
【警告】GET /v1/system/service/log/{服务名}?fileName=... 有两个高危点:① 下载后 os.Remove 删除文件,不可重复下载;② fileName 未做路径约束,可路径穿越读取并删除任意文件。二次开发不要暴露这个接口。
【注意】DELETE /v1/system/service/{服务名} 服务端不保护 dasserver / magusdog / openplant(只有前端拦),删掉 magusdog 等于自毁守护。
【注意】GET /v1/system/service/ 的 count 与实际条数可能差 1:代码会硬塞一行 magusdog(PID 恒 1122、Version 恒 7.0.9),不是真实进程状态。

4.8.2 操作审计日志(/v1/system/log/*) ​

方法路径关键参数说明
GET/v1/system/log/ID、Type、Level、Module、Desc、beginDate、endDate、limit、offset查询审计日志
DELETE/v1/system/log/{id}id(支持逗号批量)⚠️ 物理删除审计日志
GET/v1/system/log/download/{id}id(不传=全部)导出日志(TSV 文件流)
GET/v1/system/log/system无下载设备 /var/log/syslog 打包
GET/v1/system/log/empty/无⚠️ 清空全部审计日志
GET/v1/system/log/realtime/无⚠️ 不可用:路由挂到了没有 Get 方法的控制器上,请求会 HTTP 500

【警告】limit / offset 又是反的:SQL 为 order by id desc limit <limit>,<offset>。而且 limit 默认为 0,会导致 LIMIT 0 返回 0 行——必须显式传 limit。
【注意】/v1/system/log/download/ 的响应头写 charset=gbk 但内容其实是 UTF-8,用 Excel 直接打开可能乱码。
【注意】实时日志功能(内存环形缓冲)虽有实现,但路由未注册——该功能当前不可用。

4.8.3 用户与授权(/v1/system/user/*) ​

方法路径关键参数说明
GET/v1/system/user/id、name、limit、offset用户列表(loginName 为 ceshi 的行被硬编码跳过)
POST/v1/system/user/loginName、realName、email、phone新增用户。初始密码固定为 000000;不能指定角色
PUT/v1/system/user/{id}loginName、email、phone⚠️ 实现有 bug:id 未赋给实体 → 更新永远命中不了目标行(但返回「修改成功」)
DELETE/v1/system/user/{id}id(支持逗号)⚠️ 删除用户。无"不能删自己/不能删最后一个管理员"的保护
ANY/v1/system/user/changPwd/pldPwd(旧密码 MD5)、NewPwd(新密码 MD5)改自己的密码(:id 被忽略)
ANY/v1/system/user/resetPwd/{id}id = 目标用户⚠️ 弱口令重置:需 Role=="1"
GET/v1/system/auth无当前用户菜单树

口令算法(关键):数据库里存的是 md5(md5(明文));而接口收的是 md5(明文)。所以客户端要先自己算一次 md5(明文) 再传。传明文会得到「原密码错误」。

【警告】resetPwd 是弱口令重置:目标 admin → 密码重置为 sis;目标 magus → 重置为 openplant(与出厂默认一致);其它用户仅被 status=0。接口不要求操作者输入自己的密码,只要 Role=="1"。这是一个高危后门,拿到管理员会话即可一键弱口令化。
【警告】DELETE /v1/system/user/{id} 把用户全删光后,下次登录会重建出厂默认账号(admin/sis、magus/openplant、ceshi),等于把口令重置回默认值。
【注意】参数名 pldPwd(拼写就是 pld,不是 old)、NewPwd(大写 N)——传错不报错,只会「原密码错误」。
【注意】改密不会踢掉已有会话(Cookie 校验不验签名),旧会话在有效期内继续可用。

4.8.4 备份与恢复(/v1/system/backup*) ​

备份/恢复由外部可执行文件 dass-backup 完成(须位于 dasserver 工作目录),后端只是 exec 它并转发结果。

方法路径关键参数说明
GET/v1/system/backup无备份记录列表。字段:FileName / FilePath / Status / Size / TimeStamp
POST/v1/system/backup无立即备份并下载(文件流)
GET/v1/system/backup/recoverfilePath(来自上一条的 FilePath)⚠️ 整库覆盖恢复
POST/v1/system/backup/recover/importmultipart 文件字段 fileName⚠️ 上传备份文件恢复

备份内容 = db/data.db(关系库)——即服务、测点、用户、配置、日志。不包含:openplant/ 实时库数据、各服务 logs/、conf/。

【警告】recover 与 recover/import 都会整库覆盖:当前所有服务/测点/用户/配置被回滚到备份时刻,恢复后的新增数据全部丢失,不可撤销。恢复过程会中断服务(后端超时 5 分钟),恢复后必须重启服务或整机,否则内存里的旧状态与新库不一致。
【警告】recover/import 上传的文件不校验类型/魔数,直接落盘 chmod 0777 后交给外部程序执行。
【建议】任何恢复操作前,先自己 POST /v1/system/backup 存一份当前状态。

4.8.5 缓存镜像(/v1/system/mirror/*) ​

镜像 = 本机实时库向另一台实时库做数据点复制。共 17 条路由,全部注册为 *:方法。

路径说明关键参数
/v1/system/mirror/readMirrorFile读镜像配置(树形)无
/v1/system/mirror/addRep新增镜像US、PW(base64+AES)、PN、IP、PO、SP、SY、LN、Name
/v1/system/mirror/updateRep编辑镜像同上 + id
/v1/system/mirror/delRep/{id}删除镜像(连带其数据点)id
/v1/system/mirror/delAllReps删除全部镜像无
/v1/system/mirror/addItem新增镜像数据点PN、TN、XF、pid
/v1/system/mirror/updateItem更新数据点id(= 真实 id + 10000)、PN、TN、XF、pid
/v1/system/mirror/delItem/{id}删除数据点id(= 真实 id + 10000)
/v1/system/mirror/delAllItems/{id}删除某镜像全部数据点id = 镜像 id(无偏移)
/v1/system/mirror/geneRepFile⚠️ 生成镜像配置并应用无
/v1/system/mirror/filePreview / fileDownload预览 / 下载 replicator.xml无
/v1/system/mirror/checkMirror查询是否有未应用的改动无
/v1/system/mirror/checkInfo连通性 / 配置自检sign、US、PW、PN、IP、PO 等
/v1/system/mirror/clearRtdbBuffer「清理实时库缓存」无 —— ⚠️ 空操作,函数体全被注释
/v1/system/mirror/getObjNode连目的实时库取库名/节点名ip、po、usr、pwd(base64+AES)
/v1/system/mirror/checkPointExist查目的库测点是否存在ip、po、usr、pwd(明文!与上一条口径不同)、gn

【警告】geneRepFile 在检测到配置变化时会主动 kill 实时库进程(rtnn),依赖 magusdog 守护把它拉回来。若该服务的守护被关闭,实时库会一直挂着。 调用前务必确认「openplant 的 Valid=true」。
【警告】getObjNode 与 checkPointExist 的 pwd 加密口径不一致(一个要密文、一个要明文),是明显的实现不一致。
【注意】镜像数据点的 id 有 +10000 偏移,而镜像本身没有——增删改时容易错位。
【注意】AES 密钥与 IV 硬编码在源码里(0f90023fc9ae101e),且镜像口令最终会以明文写进 replicator.xml。

4.8.6 双机热备(/v1/system/dualActive/*) ​

【注意】ARM 机型或 IA-22 型号上热备模块根本不启动,相关接口不可用。

方法路径关键参数说明
ANY/v1/system/dualActive/baseInfo无基本信息:role、foreignIP、nativeIP、status、errCode、serviceStatuses[]
POST/v1/system/dualActive/setParameterJSON body:role、foreignIP设置对端(nativeIP 被服务端覆盖)
ANY/v1/system/dualActive/start / stop无⚠️ 双向操作(会同时改变对端的热备开关)。恒返回成功,实际是否生效要看 baseInfo.status
POST/v1/system/dualActive/recordsJSON body:limit、offset(页码,从 1 起)切换记录

baseInfo 字段:role(master/slave/空)、foreignIP、nativeIP、status(总开关)、errCode(0 正常 / -1 配置不完整 / -2 两侧服务不一致)、errCodeString、serviceStatuses[](name/mStatus/sStatus/mExist/sExist/msg)。

【警告】start / stop 几乎没有失败路径——底层 gRPC 调用失败也只是 return,接口仍报成功。判断是否真的生效必须查 baseInfo.status。
【警告】start/stop 会同时通知对端切换开关,单机测试时不要随意调。
【注意】baseInfo 返回 code:0 不代表配置正常,要看 errCode。

4.8.7 云平台接入 ​

方法路径关键参数说明
ANY/v1/system/accessaccess_token(base64 + AES,密钥硬编码)保存绑定,返回 boxCode 与 cloudCenterUrl;会写 conf/.env
ANY/v1/system/setCloudKeycloud_key写 conf/.env(源码注释标注「暂不使用」)
GET/v1/system/getCloudKeycloud_key⚠️ 不回显已保存的 key,只是把你传的解一下再返回
ANY/v1/system/boxCode无取本机盒子编码(来自内存,改 .env 后需重启进程才变)

4.8.8 数据同步与重启 ​

方法路径说明
ANY/v1/system/sync「系统缓存落盘」—— ⚠️ 空操作:只构造了 sync 命令对象,从未执行
ANY/v1/system/reboot/⚠️ 重启整机(3 秒后 reboot)
ANY/v1/system/serviceBoot/⚠️ 仅退出 dasserver 进程(os.Exit(0))

【警告】这两个重启接口的响应体是两段 JSON 串接(不是合法 JSON),不能靠解析响应判断成功,应轮询设备可达性。详见 7.27。
【警告】serviceBoot 的「能否自动回来」取决于 magusdog 是否在守护 dasserver。若未受守护,进程退出后永远不会回来——等于远程关机。

4.8.9 其它 ​

方法路径说明
ANY/v1/system/reset/⚠️ 恢复出厂设置(会删除首次启动时间文件)。需 admin 角色 + base64/AES 口令
ANY/v1/system/serviceBoot/见 4.8.8
GET/v1/system/first/首次启动时间。⚠️ 当前必然 HTTP 500(见 4.6.8)
GET/v1/system/conf⚠️ 泄露明文口令,见 4.6.5

【注意】关于本产品的权限模型:auth 表只决定前端菜单可见性,没有任何接口级 RBAC 拦截。也就是说任何登录用户都能调用全部 /v1/system/* 接口——包括重置密码、清空日志、上传服务包、重启设备。做集成时不要依赖"角色"来限制能力。