14 KiB
14 KiB
SecMPS 整合方案(最终版):IntegrationGateway + 统一设备管理
版本: v2.0
日期: 2026-05-16
状态: 待实施
一、总体架构
前端层
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 (:80) MC4.0 (:3000) 海康ISC (:80)
核心设计原则
- 网关无状态:配置仅 NodeCode/Token/VolProUrl,挂了重装即恢复
- AdapterCode 双段标识:"mc4:31号库房" 区分同类型多实例
- DeviceGroup 路由:基类表用字典字段决定适配器和行为,不依赖扩展表
- ExtraData JSON:所有适配器特有字段存入 ExtraData,新增适配器不增表
- 心跳机制:网关 15s 心跳,Vol.Pro 超 30s 级联设备离线
二、网关架构(方案 C+)
2.1 网关注册与心跳流程
管理端: gateway_nodes 表新增 → 生成 NodeCode + Token
网关配置: { NodeCode, Token, VolProUrl }
网关启动 → POST /register { nodeCode,token,adapterTypes,baseUrl }
Vol.Pro 校验 → 更新 AdapterTypes/BaseUrl/IsOnline=在线
响应: { gatewayNodeId, devices: [base_device WHERE GatewayNodeId=当前] }
网关按 AdapterCode 分流 → Adapter 连接第三方 → 发现子设备
网关 → POST /sync → Vol.Pro 写入 base_device(含 ExtraData)
网关每 15s → POST /heartbeat { nodeCode, token }
Vol.Pro Job: IsOnline=在线 且 LastHeartbeat < now-30s → IsOnline=离线 → 级联设备离线
2.2 网关配置
{
"VolProBaseUrl": "http://localhost:9100",
"NodeCode": "gw-31ku",
"NodeToken": "xxxxxxxxxx"
}
2.3 适配器能力矩阵
| 接口 | Owl | MC4.0 | 门禁(未来) |
|---|---|---|---|
| IHasOwnDeviceTree | ❌ | ✅ | ❌ |
| IHasFlatDevices | ✅ | ❌ | ✅ |
| IHasPoints | ❌ | ✅ | ❌ |
| IHasStreams | ✅ | ❌ | ❌ |
| IHasAlarms | ⚠️ | ✅ | ✅ |
| IAcceptsMetadataPush | ✅ | ❌ | ⚠️ |
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 |
网关启动注册,上报身份与能力,获取所管设备列表 |
| A2 | POST /api/gateway/heartbeat |
心跳(每 15s),Vol.Pro 更新在线状态 |
| A3 | POST /api/gateway/sync/devices |
上送设备数据(新增/变更/离线) |
| A4 | POST /api/gateway/sync/alarms |
上送告警数据 |
A1 注册 — 认证: NodeToken
Request: { nodeCode, token, adapterTypes, baseUrl }
Response: { nodeId, devices: [ base_device 列表(当前网关负责的顶层设备) ] }
Error: 401 认证失败
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 }
网关只发自己负责的字段(ExtraData 中的适配器属性 + 公共状态字段),不碰管理员字段(DeviceName/Category/Location/MapModelId…)。Vol.Pro 首次入库写全量,后续仅更新网关负责的列。
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 |
反向控制 { deviceSourceId, pointIndex, value } |
| B6 | GET /api/gateway/streams/{adapter}/{channelId}/live |
取流地址 → { wsFlv, httpFlv, hls, webrtc } |
| B7 | POST /api/gateway/streams/{adapter}/{channelId}/ptz |
云台控制 { direction, speed } |
| B8 | GET /api/gateway/alarms/{adapter}?from=&to=&page=&size= |
告警查询 |
| B9 | POST /api/gateway/alarms/{adapter}/{alarmId}/confirm |
告警确认(写回第三方) |
B 组接口由管理端或 Vol.Pro 后端直接调用网关,认证方式为内网直连或网关侧 IP 白名单。
三、数据模型(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:31号库房"(类型:实例) |
| SourceId | NVARCHAR(100) | 源系统设备ID |
| DeviceCategory | NVARCHAR(50) | 设备种类(字典: 摄像机/温湿度变送器/...) |
| DeviceGroup | NVARCHAR(20) | 设备分组(字典: 视频设备/IoT设备/门禁设备/道闸设备/报警设备) |
| 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(Owl/MC4/门禁字段均存于此) |
| LastSyncTime | DATETIME | |
| Enable | NVARCHAR(20) | 启用状态(字典: 启用/禁用) |
唯一约束: (AdapterCode, SourceId)
3.4 DeviceGroup 分组规则
Vol.Pro 同步接口通过 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 }
// 未来门禁
{ "hikDeviceId": "door_01", "doorType": "单门", "readerType": "IC卡" }
3.6 层级示例
gateway_nodes: gw-31ku
warehouse_regions → warehouse_devicepoint → base_device
区域 点位 设备
例: 厂区 → 新库区 → 31号库房(点位) → 设备
base_device (PointId=点位ID, GatewayNodeId=gw-31ku.NodeId):
东北角高位摄像机 (DeviceCategory=摄像机, DeviceGroup=视频设备, ExtraData={owlDeviceId,...})
人员计数摄像机 (DeviceCategory=摄像机, DeviceGroup=视频设备)
动环采集器 (DeviceCategory=动环采集器, DeviceGroup=IoT设备, IsParent=是)
├── 温湿度变送器 (DeviceCategory=温湿度变送器, ParentDeviceId=采集器, ExtraData={pointIndex:0,unit:"℃"})
├── 空调控制器 (DeviceCategory=空调控制器, ParentDeviceId=采集器, ExtraData={pointIndex:2,isControlPoint:true})
├── 除湿/恒湿机 (DeviceCategory=除湿/恒湿机, ParentDeviceId=采集器)
└── 紧急报警按钮 (DeviceCategory=紧急报警按钮, DeviceGroup=报警设备, ParentDeviceId=采集器)
四、Vol.Pro 同步接口(新增适配器零改动)
// POST /api/gateway/sync
public async Task SyncDevices(string nodeCode, List<StandardDevice> devices)
{
var node = await _db.gateway_nodes.FirstAsync(n => n.NodeCode == nodeCode);
foreach (var d in devices)
{
var entity = await _db.base_device
.FirstOrDefaultAsync(x => x.AdapterCode == d.AdapterCode && x.SourceId == d.SourceId)
?? new base_device();
entity.DeviceName = d.Name;
entity.DeviceGroup = d.Group; // 字典: 视频设备/IoT设备/...
entity.DeviceCategory = d.Category; // 字典: 摄像机/温湿度变送器/...
entity.IsOnline = d.IsOnline ? "在线" : "离线";
entity.IsParent = d.IsParent ? "是" : "否";
entity.ParentDeviceId = d.ParentSourceId; // 网关同步过来的父级关系
entity.GatewayNodeId = node.NodeId;
entity.ExtraData = d.ExtraDataJson; // ★ 一行,适配器字段全在这里
// ... 公共字段赋值 ...
_db.base_device.Upsert(entity);
}
await _db.SaveChangesAsync();
}
五、管理端统一设备页面
5.1 操作按钮矩阵(按 DeviceGroup 路由)
| DeviceGroup | 操作按钮 |
|---|---|
| 视频设备 | 实时预览 / 云台控制 / 查看回放 / 获取快照 / 同步通道 |
| IoT设备 | 查看实时数据 / 设备控制 / 刷新点位 / 查看告警 |
| 门禁设备 | 远程开门 / 查看记录 / 查看告警 |
| 道闸设备 | 抬杆 / 落杆 / 查看记录 |
| 报警设备 | 查看告警 / 布防撤防 |
5.2 前端按钮路由
// DeviceTable.vue
const actionMap = {
'视频设备': VideoDeviceActions,
'IoT设备': IoTDeviceActions,
'门禁设备': AccessDeviceActions,
'道闸设备': BarrierDeviceActions,
'报警设备': AlarmDeviceActions,
}
// 渲染: actionMap[device.DeviceGroup]
六、同步策略
MC4.0 → 区域树+设备
- type=1 节点 → 名称匹配 warehouse_regions → 绑区或新建
- type=2 节点 → Upsert base_device, DeviceGroup=IoT设备, ExtraData 存点位属性
- 模式: FullReplace, 频率限制: 2次/秒
Owl → 设备
- GET /devices → Upsert base_device (DeviceGroup=视频设备, IsParent=是)
- GET /channels → Upsert base_device (ParentDeviceId=NVR) + video_channel
- Owl 无区域概念 → PointId=NULL, 管理员手动分配
- 模式: Merge
反方向写回
| 操作 | Owl | MC4.0 |
|---|---|---|
| 设备改名 | ✅ PUT /devices/:id | ❌ |
| 告警确认 | ⚠️ | ✅ |
| 设备控制 | ✅ PTZ | ✅ 点位写值 |
七、部署拓扑
Docker: Owl+ZLM (:80) 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 + 管理端视频设备页 |
| Phase 2 | Day 7-11 | Mc4Adapter + IoT管理 + 区域树匹配 + SignalR |
| Phase 3 | Day 12-17 | warehouse端改造 + 全链路联调 |
| Phase 4 | Day 18-20 | 验证 + 缓冲 |
总计: 18-20 个工作日
九、新增整合流程
以接入「海康门禁」为例:
- 新建 IntegrationGateway.Adapters.HikvisionAccess 项目
- 实现 IHasFlatDevices + IHasAlarms → 设备同步时填充 DeviceGroup=门禁设备
- 管理端字典加一条"门禁设备"分组 → 按钮自动出现
- Vol.Pro 同步接口零改动(ExtraData 承载门禁字段)
- 前端新增 AccessDeviceActions.vue (~80行) 总工作量: 1-2 天
十、代码组织规范
| 代码类型 | 路径 | 被覆盖? |
|---|---|---|
| 第三方对接 | gateway/ | ❌ |
| 扩展Controller | Controllers/*/Partial/ | ❌ |
| Entity扩展 | DomainModels/*/partial/ | ❌ |
| 前端业务逻辑 | extension/warehouse/*.jsx | ❌ |
| 自定义页面 | views/warehouse/DeviceManager/ | ❌ |
| 自动生成代码 | 生成器默认路径 | ✅ |
取代: V1.0 系列所有整合方案文档