Files
SecMPS/doc/整合方案/SecMPS_最终整合方案_v3.0.md
2026-05-17 04:39:44 +08:00

22 KiB
Raw Blame History

SecMPS 整合方案最终版IntegrationGateway + 统一设备管理

版本: v3.0
日期: 2026-05-16
状态: 待实施
基准: 基于 v2.0 + Owl/ZLMediaKit 双文档验证 + 14 项修正


一、总体架构

前端层
  web.vite 管理端(设备管理+网关管理)  warehouse 大屏Map/Live/IoT/Alarm
         | HTTP REST                                 | HTTP REST + SignalR
         v                                              v
Vol.Pro 后端 (api_sqlsugar)
  DeviceManagerController / GatewayNodeController / SignalR Hubs
  Quartz: SyncDevicesJob / RealtimePollJob / AlarmPollJob
  数据库: 6 张表 (base_device / video_channel / video_record / iot_devicedata / iot_alarm / gateway_nodes)
         | 注册/下发设备/心跳/同步数据
         v
IntegrationGateway 实例A (:5100)       IntegrationGateway 实例B (:5101)
  NodeCode: gw-31ku                      NodeCode: gw-11ku
  Adapters: MC4 / Owl                    Adapters: MC4 / HikvisionISC
         | HTTP                              | HTTP
         v                                   v
MC4.0 (:3000)  Owl (:15123)           MC4.0 (:3000)  海康ISC (:80)

注: Owl 管理端口为 15123ZLMediaKit 由 Owl 内部管理(:8000网关不直接接触

核心设计原则

  • 网关无状态:配置仅 NodeCode/Token/VolProUrl挂了重装即恢复
  • AdapterCode 双段标识"MC4:31ku" 区分同类型多实例,格式 {AdapterType}:{InstanceName}InstanceName 仅字母数字下划线
  • DeviceGroup 路由:基类表用字典字段决定适配器和行为,不依赖扩展表
  • ExtraData JSON:所有适配器特有字段存入 ExtraData新增适配器不增表
  • 心跳机制:网关 15s 心跳Vol.Pro 超 30s 级联设备离线
  • 字段分治网关字段ExtraData/IsOnline/IsParent/ParentDeviceId同步覆盖管理员字段DeviceName/Category/Location/MapModelId首次写入后不再覆盖

二、网关架构(方案 C+

2.1 网关注册与心跳流程

管理端: gateway_nodes 表新增 → 生成 NodeCode + Token
网关配置: { NodeCode, Token, VolProUrl }
网关启动 → POST /register { nodeCode,token,adapterTypes,baseUrl }
Vol.Pro 校验 Token:
  NodeCode 匹配 → Upsert: 更新 AdapterTypes/BaseUrl/IsOnline=在线 → 返回已有 NodeId
  NodeCode 不匹配且 Token 验证通过 → 插入新记录 → 返回新 NodeId
  验证失败 → 401
响应: { gatewayNodeId, devices: [base_device WHERE GatewayNodeId=当前] }
网关按 AdapterCode 分流 → Adapter 连接第三方 → 发现子设备
网关 → POST /sync/devices → Vol.Pro 写入/更新 base_device首次写全量后续仅更新网关字段
网关每 15s → POST /heartbeat { nodeCode, token }
Vol.Pro Job: IsOnline=在线 且 LastHeartbeat < now-30s → IsOnline=离线 → 级联: base_device.IsOnline=离线 WHERE GatewayNodeId=该节点

2.2 网关配置

{
  "VolProBaseUrl": "http://localhost:9100",
  "NodeCode": "gw-31ku",
  "NodeToken": "xxxxxxxxxx"
}

AdapterCode 格式规范:{AdapterType}:{InstanceName},例如 "MC4:31ku""Owl:main"。 AdapterType = 网关注册的 Adapter 类名字母数字InstanceName = 网关实例名(字母数字下划线),分隔符 :

2.3 适配器能力矩阵

接口 Owl MC4.0 门禁(未来)
IHasOwnDeviceTree
IHasFlatDevices
IHasPoints
IHasStreams
IHasAlarms ⚠️(AI事件可选)
IAcceptsMetadataPush ⚠️

Owl PTZ 限制:仅支持 continuous(方向移动) + stop(停止),不支持预设位/绝对定位/相对定位。ONVIF PTZ 未实现。

2.4 双向同步引擎

方向 说明 MC4.0 Owl
PullToVolPro 第三方→Vol.Pro FullReplace Merge
PushToSource Vol.Pro→第三方 告警确认/控制 元数据/PTZ
Bidirectional 先写第三方再更新本地 告警确认

2.5 对接 API 规范

网关与 Vol.Pro 之间有两组接口,调用方向不同。

A. 网关 → Vol.Pro网关主动调用

# 接口 说明
A1 POST /api/gateway/register 网关启动注册Upsert逻辑获取所管设备列表
A2 POST /api/gateway/heartbeat 心跳(每 15s
A3 POST /api/gateway/sync/devices 上送设备数据(新增/状态变更),仅更新网关字段
A4 POST /api/gateway/sync/alarms 上送告警数据

A1 注册 — 认证: NodeToken, 逻辑: Upsert

Request:  { nodeCode, token, adapterTypes, baseUrl }
Response: { nodeId, devices: [ 当前网关的顶层设备 ] }
Error:    401 认证失败Token 错误或 NodeCode 不存在且 Token 无效)

A2 心跳 — 认证: NodeToken

Request:  { nodeCode, token }
Response: { status: "ok" }

A3 设备同步 — 认证: NodeToken

Request:  { nodeCode, token, devices: [{ adapterCode, sourceId, name, category, group,
            isParent, parentSourceId, isOnline, ipAddress, port, extraData }] }
Response: { added, updated, removed }

字段分治规则:首次入库(DeviceId==0)写全量;已有记录仅更新 IsOnline、IsParent、ParentDeviceId、ExtraData、LastSyncTime、IpAddress、Port。DeviceName/DeviceCategory/DeviceGroup/Location/MapModelId 仅在首次入库时写入。 parentSourceId 解析:同步前按 (AdapterCode, SourceId) 批量查询已有 DeviceId 映射表,将 parentSourceId 转为 ParentDeviceId。

A4 告警同步 — 认证: NodeToken

Request:  { nodeCode, token, alarms: [{ sourceAlarmId, deviceSourceId, adapterCode,
            level, desc, value, startTime }] }
Response: { added }

B. Vol.Pro / 管理端 → 网关(查询与控制)

# 接口 说明
B1 GET /api/gateway/health 网关及所有适配器状态
B2 GET /api/gateway/devices?adapter=&page=&size= 设备列表
B3 POST /api/gateway/devices/sync?adapter= 手动触发全量同步
B4 GET /api/gateway/realtime/{adapter}/{deviceId} 实时点位值
B5 POST /api/gateway/realtime/{adapter}/control 反向控制
B6a GET /api/gateway/streams/{adapter}/{id}/live 实时取流
B6b GET /api/gateway/streams/{adapter}/{id}/playback?start=&end= 回放取流(HLS VOD)
B7 POST /api/gateway/streams/{adapter}/{id}/ptz 云台控制(仅方向移动+停止)
B8 GET /api/gateway/alarms/{adapter}?from=&to=&page=&size= 告警查询
B9 POST /api/gateway/alarms/{adapter}/{alarmId}/confirm 告警确认(写回第三方+更新本地State)

B 组接口由管理端或 Vol.Pro 后端直接调用网关,认证方式为内网直连或网关侧 IP 白名单。 B6a 实时取流 → Owl POST /channels/:id/playB6b 回放取流 → Owl GET /recordings/channels/:cid/index.m3u8?start_ms=&end_ms= B7 仅支持 continuous(方向移动: up/down/left/right/zoom_in/zoom_out) + stop,不支持预设位 B9 确认第三方成功后,同步更新本地 iot_alarm.State='已确认'


三、数据模型6 张表)

3.1 区域表 warehouse_regions现有

层级: warehouse_regions(区域) → warehouse_devicepoint(点位) → base_device(设备)

字段 说明
Id int PK
RegionName nvarchar(255)
ParentId int? (自引用树)

3.2 网关节点表 gateway_nodes

字段 类型 说明
NodeId INT AUTO_INCREMENT 网关节点ID
NodeCode NVARCHAR(50) 网关唯一编码
NodeName NVARCHAR(100) 网关名称
NodeToken NVARCHAR(100) 认证令牌
AdapterTypes NVARCHAR(200) 适配器类型(网关上报)
BaseUrl NVARCHAR(200) 网关地址(网关上报)
LastHeartbeat DATETIME 上次心跳时间
IsOnline NVARCHAR(20) 在线状态(字典: 在线/离线)
Enable NVARCHAR(20) 启用状态(字典: 启用/禁用)

3.3 统一设备主表 base_device

字段 类型 说明
DeviceId INT AUTO_INCREMENT Vol.Pro内部ID
DeviceName NVARCHAR(100) 设备名称(管理员字段)
AdapterCode NVARCHAR(50) "MC4:31ku"(类型:实例)
SourceId NVARCHAR(100) 源系统设备ID
DeviceCategory NVARCHAR(50) 设备种类(字典)
DeviceGroup NVARCHAR(20) 设备分组(字典)
PointId INT? 所属点位ID
GatewayNodeId INT? 所属网关节点ID
IsParent NVARCHAR(20) 是否父设备(字典: 是/否)
ParentDeviceId INT? 父设备自引用
IsOnline NVARCHAR(20) 在线状态(网关字段)
MapModelId NVARCHAR(100) VgoMap模型ID管理员字段
MapModelScale FLOAT
MapModelRotation NVARCHAR(100)
ExtraData TEXT ★ 适配器扩展JSON网关字段
LastSyncTime DATETIME 上次同步时间
Enable NVARCHAR(20) 启用状态(字典: 启用/禁用)

唯一约束: (AdapterCode, SourceId)

网关字段同步覆盖ExtraData/IsOnline/IsParent/ParentDeviceId/IpAddress/Port/LastSyncTime 管理员字段首次写入后不覆盖DeviceName/DeviceCategory/DeviceGroup/PointId/Location/Lat/Lng/MapModelId/MapModelScale/MapModelRotation

3.4 DeviceGroup 分组规则

DeviceGroup 网关适配器 前端按钮组 同步模式
视频设备 OwlAdapter → IHasStreams 实时预览/云台/回放/快照 Merge
IoT设备 Mc4Adapter → IHasPoints 实时数据/控制/告警 FullReplace
门禁设备 AccessAdapter 远程开门/记录/告警 Merge
道闸设备 BarrierAdapter 抬杆/落杆/记录 Merge
报警设备 AlarmAdapter 查看告警/布防撤防 Merge

3.5 ExtraData 格式示例

// 摄像机 (Owl)
{ "owlDeviceId": "gb_xxx", "protocol": "GB28181", "manufacturer": "海康" }

// 温湿度变送器 (MC4子设备)
{ "mc4DeviceId": 1001, "pointIndex": 0, "unit": "℃", "isControlPoint": false }

// 空调控制器 (MC4子设备)
{ "mc4DeviceId": 1002, "pointIndex": 2, "unit": null, "isControlPoint": true }

3.6 层级示例

gateway_nodes: gw-31ku

warehouse_regions → warehouse_devicepoint → base_device

例: 厂区 → 新库区 → 31号库房(点位) → 设备

base_device (PointId=点位ID, GatewayNodeId=gw-31ku.NodeId):
  NVR-01              (DeviceCategory=硬盘录像机, DeviceGroup=视频设备, IsParent=是)
  ├── 通道01           (ParentDeviceId=NVR, video_channel.DeviceId=通道01.DeviceId, OwlStreamApp="rtp")
  ├── 通道02           (ParentDeviceId=NVR, video_channel.DeviceId=通道02.DeviceId, OwlStreamApp="rtp")
  东北角高位摄像机      (DeviceCategory=摄像机, DeviceGroup=视频设备)
  动环采集器           (DeviceCategory=动环采集器, DeviceGroup=IoT设备, IsParent=是)
  ├── 温湿度变送器      (ParentDeviceId=采集器, ExtraData={pointIndex:0,unit:"℃"})
  ├── 空调控制器        (ParentDeviceId=采集器, ExtraData={pointIndex:2,isControlPoint:true})
  └── 紧急报警按钮      (DeviceGroup=报警设备, ParentDeviceId=采集器)

video_channel 表通道Owl流信息:
  { ChannelId=1, DeviceId=通道01.DeviceId(→base_device), OwlStreamApp="rtp", HasPtz=1 }
  { ChannelId=2, DeviceId=通道02.DeviceId(→base_device), OwlStreamApp="rtp", HasPtz=0 }

> 通道 = base_device 子记录(名称/在线/层级) + video_channel 扩展记录(流地址/云台能力/录像模式)
> video_channel 的 DeviceId 指向通道自身的 base_device.DeviceId不是 NVR 的 DeviceId
> video_channel 的 OwlStreamApp/OwlStreamName/SnapshotUrl 为缓存,首次取流后写入,后续优先读缓存

四、Vol.Pro 同步接口(新增适配器零改动)

// POST /api/gateway/sync/devices
public async Task SyncDevices(string nodeCode, List<StandardDevice> devices)
{
    var node = await _db.gateway_nodes.FirstAsync(n => n.NodeCode == nodeCode);

    // ★ 批量查询已有设备映射表 (用于 parentSourceId 解析)
    var existingIds = await _db.base_device
        .Where(x => x.GatewayNodeId == node.NodeId)
        .ToDictionaryAsync(x => (x.AdapterCode, x.SourceId), x => x.DeviceId);

    foreach (var d in devices)
    {
        var key = (d.AdapterCode, d.SourceId);
        existingIds.TryGetValue(key, out var existingId);
        var entity = existingId > 0
            ? await _db.base_device.FindAsync(existingId)
            : new base_device { AdapterCode = d.AdapterCode, SourceId = d.SourceId };

        bool isNew = entity.DeviceId == 0;

        // ★ 解析 parentSourceId → ParentDeviceId
        int? parentDeviceId = null;
        if (!string.IsNullOrEmpty(d.ParentSourceId))
        {
            existingIds.TryGetValue((d.AdapterCode, d.ParentSourceId), out var pid);
            if (pid > 0) parentDeviceId = pid;
        }

        // 仅首次入库写管理员字段
        if (isNew)
        {
            entity.DeviceName = d.Name;
            entity.DeviceCategory = d.Category;
            entity.DeviceGroup = d.Group;
        }

        // 每次同步写网关字段
        entity.IsOnline = d.IsOnline ? "在线" : "离线";
        entity.IsParent = d.IsParent ? "是" : "否";
        entity.ParentDeviceId = parentDeviceId;
        entity.ExtraData = d.ExtraDataJson;
        entity.GatewayNodeId = node.NodeId;
        entity.IpAddress = d.IpAddress;
        entity.Port = d.Port;
        entity.LastSyncTime = DateTime.UtcNow;

        if (isNew) _db.base_device.Add(entity);
        else _db.base_device.Update(entity);
    }
    await _db.SaveChangesAsync();
}

五、管理端设备操作集成

设计决策: 不再需要独立的设备管理页面。Vol.Pro 框架自带三级主从表显示能力warehouse_regions → warehouse_devicepoint → base_device可直接在框架生成的 base_device 列表页面中嵌入操作按钮,按 DeviceGroup 动态渲染。

5.1 操作按钮矩阵

DeviceGroup 操作按钮 说明
视频设备 实时预览 / 云台控制(仅方向键) / 查看回放 / 获取快照 / 同步通道 全部通过网关 B 组接口代理到 Owl
IoT设备 查看实时数据 / 设备控制 / 刷新点位 / 查看告警 通过网关 B4/B5 代理到 MC4.0
门禁设备 远程开门 / 查看记录 / 查看告警 Phase 3 接入海康ISC后启用
道闸设备 抬杆 / 落杆 / 查看记录 Phase 3 接入
报警设备 查看告警 / 布防撤防 Phase 3 接入

5.2 前端按钮嵌入方案

框架生成的 base_device 列表页面的"操作"列默认只有"编辑/删除"。在 base_deviceController Partial 中扩展前端分组逻辑,替换默认操作列为自定义渲染。

// web.vite/src/extension/warehouse/base_device.jsx 或 views/warehouse/base_device/components/ActionColumn.vue
const actionMap = {
  '视频设备': VideoDeviceActions,   // 实时预览/云台/回放/快照/同步通道
  'IoT设备':  IoTDeviceActions,    // 实时数据/控制/刷新/告警
  '门禁设备': AccessDeviceActions, // 远程开门/记录/告警
  '道闸设备': BarrierDeviceActions,// 抬杆/落杆/记录
  '报警设备': AlarmDeviceActions,  // 告警/布防撤防
}

// 操作列模板中根据 row.deviceGroup 动态渲染对应组件
<template v-for="(row, idx) in data">
  <component :is="actionMap[row.deviceGroup] || DefaultActions" :row="row" />
</template>

要点:

  • 框架"操作"列通过自定义插槽替换,不修改框架生成的 Vue 文件本体
  • 组件放在 views/warehouse/base_device/components/ 下,防代码生成覆盖
  • 五个分组组件各自独立(~80-150 行),新增分组仅加一个文件

5.3 前端操作组件清单

文件 大小 说明
VideoDeviceActions.vue ~120行 预览/云台/回放/快照按钮组,预览弹窗内嵌 Jessibuca 播放器(或
IoTDeviceActions.vue ~100行 实时数据表格弹窗(轮询 B4控制面板B5 写值),告警快捷入口
AccessDeviceActions.vue ~60行 远程开门按钮 + 记录查询入口
BarrierDeviceActions.vue ~60行 抬杆/落杆按钮
AlarmDeviceActions.vue ~60行 告警列表 + 布防/撤防开关

5.4 后端操作 API

以下 API 由网关 B 组接口代理Vol.Pro 前端不直连子系统,统一经网关中转。

接口 方法 说明 对应网关接口
/api/gateway/streams/{adapter}/{sourceId}/live GET 获取实时流地址 B6a
/api/gateway/streams/{adapter}/{sourceId}/playback?start=&end= GET 获取回放地址 B6b
/api/gateway/streams/{adapter}/{sourceId}/snapshot POST 获取截图
/api/gateway/streams/{adapter}/{sourceId}/ptz POST 云台方向控制 B7
/api/gateway/realtime/{adapter}/{sourceId} GET 获取实时点位值 B4
/api/gateway/realtime/{adapter}/control POST 设备控制写值 B5
/api/gateway/alarms/{adapter} GET 分页查询告警 B8
/api/gateway/alarms/{adapter}/{alarmId}/confirm POST 确认告警 B9

Vol.Pro 前端直接请求网关(:5100不经过 Vol.Pro 后端中转。跨域问题通过网关 CORS 或 nginx 反向代理解决。


六、同步策略

MC4.0 → 区域树+设备

  • type=1 节点 → 名称匹配 warehouse_regions → 绑区或新建
  • type=2 节点 → Upsert base_device (DeviceGroup=IoT设备, ExtraData 存点位属性)
  • 子设备在线状态由网关按 MC4.0 数据如实上报Vol.Pro 不做推断
  • 模式: FullReplace, 频率限制: 2次/秒

Owl → 设备

  • GET /devices → Upsert base_device (DeviceGroup=视频设备, IsParent=是)
  • GET /channels → Upsert base_device (ParentDeviceId=NVR) + video_channel 扩展记录
  • Owl 无区域概念 → PointId=NULL, 管理员手动分配
  • 可手动触发 POST /devices/:id/catalog 刷新通道目录
  • 模式: Merge

录像同步

  • 管理端点击"查看回放"时,网关实时调 Owl GET /recordings → 返回给前端 + 同步写入 video_record
  • Phase 3 可选:网关定时(每 10 分钟)后台同步录像记录

Owl AI 事件(可选)

  • OwlAdapter 可选实现 IHasAlarms将 Owl GET /events 的 YOLO AI 检测事件映射为 StandardAlarm走 A4 告警同步

反方向写回

操作 Owl MC4.0
设备改名 PUT /devices/:id
告警确认 ⚠️
设备控制 PTZ(仅方向键) 点位写值

七、部署拓扑

Docker: Owl+ZLM (:15123)    MC4.0-1 (:3000)   MC4.0-2 (:3000)
         |                         |                   |
         +----------+--------------+-------------------+
                    |
         +----------+-----------+
         |  Gateway gw-31ku     |    Gateway gw-11ku
         |    :5100              |      :5101
         +----------+-----------+
                    |
         +----------+-----------+
         |   VolPro.WebApi      |
         |      :9100           |
         |   MySQL / Redis       |
         +----------+-----------+
                    |
     +--------------+---------------+
     | web.vite :9000   warehouse :9200 |
     +--------------------------------+

八、实施路线

阶段 工期 内容
Phase 0 Day 1-2 Gateway骨架 + 6张表建表 + 代码生成 + 字典初始化
Phase 1 Day 3-6 OwlAdapter + base_device操作按钮组件(视频) + [可选]AI事件接入
Phase 2 Day 7-11 Mc4Adapter + IoT管理 + 区域树匹配 + SignalR
Phase 3 Day 12-17 warehouse端改造 + 全链路联调
Phase 4 Day 18-20 验证 + 缓冲

总计: 18-20 个工作日


九、新增整合流程

以接入「海康门禁」为例:

  1. 新建 IntegrationGateway.Adapters.HikvisionAccess 项目
  2. 实现 IHasFlatDevices + IHasAlarms → 设备同步时填充 DeviceGroup=门禁设备
  3. 管理端字典加一条"门禁设备"分组 → 按钮自动出现
  4. Vol.Pro 同步接口零改动ExtraData 承载门禁字段)
  5. 前端新增 AccessDeviceActions.vue (~80行) 总工作量: 1-2 天

十、代码组织规范

代码类型 路径 被覆盖?
第三方对接 gateway/
扩展Controller Controllers/*/Partial/
Entity扩展 DomainModels/*/partial/
前端业务逻辑 extension/warehouse/*.jsx
自定义页面 views/warehouse/DeviceManager/
自动生成代码 生成器默认路径

附录 A字典初始化清单

Phase 0 建表后需在 Vol.Pro 管理端创建以下数据字典:

字典名称 字典值
设备种类 门磁/空调/智能断路器/人行道闸/车辆道闸/485钥匙柜/网络钥匙柜/紧急报警按钮/红外报警器/门禁一体机/除湿_恒湿机/空调控制器/烟雾报警器/气体报警器/温湿度变送器/摄像机/硬盘录像机/动环采集器
设备分组 视频设备/IoT设备/门禁设备/道闸设备/报警设备
是否父设备 是/否
在线状态 在线/离线
启用状态 启用/禁用
是否控制点 只读/可写
告警等级 提示/普通/重要/紧急
告警状态 未确认/已确认/已结束

附录 BAdapterCode 格式规范

格式: {AdapterType}:{InstanceName}
示例: "MC4:31ku"、"Owl:main"、"HikvisionISC:center"
规则:
  - AdapterType: 网关注册的 Adapter 类名,仅字母数字
  - InstanceName: 网关实例名称,仅字母数字下划线
  - 分隔符: ':'
  - base_device.AdapterCode 存储完整双段标识

版本历史:

  • v1.0 (2026-04-29) — 初始 Owl+MC4 独立方案
  • v2.0 (2026-05-16) — 整合 Gateway 架构 + 数据字典 + ExtraData JSON
  • v3.0 (2026-05-16) — Owl/ZLMediaKit 双文档验证 + 14 项修正P0/P1/P2 修复)
  • v3.1 (2026-05-17) — 第五章修订:取消独立设备管理页面,改为框架主从表嵌入操作按钮