phase/0-infrastructure #1

Merged
g82tt merged 25 commits from phase/0-infrastructure into master 2026-05-16 15:17:19 +08:00
2 changed files with 161 additions and 210 deletions
Showing only changes of commit bf3cbc71ae - Show all commits

View File

@@ -1,27 +1,29 @@
-- ============================================ -- ============================================
-- SecMPS v2.0 数据库建表脚本(8张表) -- SecMPS v2.0 数据库建表脚本(6张表)
-- 数据库: gljs_main -- 数据库: gljs_main
-- 点位=子设备, 通过 base_device.ParentDeviceId 级联 -- 扩展表已合并到 Base_Device.ExtraData(JSON)
-- ============================================ -- ============================================
USE gljs_main; USE gljs_main;
-- ============================================ -- ============================================
-- 1. 统一设备主表 -- 1. 统一设备主表
-- 采集器/摄像机=父设备(IsParent=1) 探头/通道=子设备(ParentDeviceId) -- ExtraData(JSON) 承载所有适配器特有字段
-- DeviceGroup 路由到正确的网关Adapter和前端按钮组
-- ============================================ -- ============================================
DROP TABLE IF EXISTS base_device; DROP TABLE IF EXISTS base_device;
CREATE TABLE base_device ( CREATE TABLE base_device (
DeviceId INT AUTO_INCREMENT COMMENT '设备ID', DeviceId INT AUTO_INCREMENT COMMENT '设备ID',
DeviceName NVARCHAR(100) NOT NULL COMMENT '设备名称', DeviceName NVARCHAR(100) NOT NULL COMMENT '设备名称',
AdapterCode NVARCHAR(50) NOT NULL COMMENT '来源适配器', AdapterCode NVARCHAR(50) NOT NULL COMMENT '来源适配器(类型:实例)',
SourceId NVARCHAR(100) NOT NULL COMMENT '源系统设备ID', SourceId NVARCHAR(100) NOT NULL COMMENT '源系统设备ID',
DeviceCategory NVARCHAR(50) NOT NULL COMMENT '设备种类(数据字典:门磁/空调/智能断路器/人行道闸/车辆道闸/485钥匙柜/网络钥匙柜/紧急报警按钮/红外报警器/门禁一体机/除湿_恒湿机/空调控制器/烟雾报警器/气体报警器/温湿度变送器/摄像机/硬盘录像机/动环采集器)', DeviceCategory NVARCHAR(50) NOT NULL COMMENT '设备种类(数据字典:门磁/空调/智能断路器/人行道闸/车辆道闸/485钥匙柜/网络钥匙柜/紧急报警按钮/红外报警器/门禁一体机/除湿_恒湿机/空调控制器/烟雾报警器/气体报警器/温湿度变送器/摄像机/硬盘录像机/动环采集器)',
DeviceGroup NVARCHAR(20) NOT NULL COMMENT '设备分组(数据字典:视频设备/IoT设备/门禁设备/道闸设备/报警设备)',
RegionId INT NULL COMMENT '所属区域ID', RegionId INT NULL COMMENT '所属区域ID',
GatewayNodeId INT NULL COMMENT '所属网关节点ID', GatewayNodeId INT NULL COMMENT '所属网关节点ID',
IsParent NVARCHAR(20) NOT NULL DEFAULT '0' COMMENT '是否父设备(数据字典:是/否)', IsParent NVARCHAR(20) NOT NULL DEFAULT '' COMMENT '是否父设备(数据字典:是/否)',
ParentDeviceId INT NULL COMMENT '父设备ID(自引用,子设备挂父设备下)', ParentDeviceId INT NULL COMMENT '父设备ID(自引用,子设备挂父设备下)',
IsOnline NVARCHAR(20) NOT NULL DEFAULT '0' COMMENT '在线状态(数据字典:在线/离线)', IsOnline NVARCHAR(20) NOT NULL DEFAULT '离线' COMMENT '在线状态(数据字典:在线/离线)',
IpAddress NVARCHAR(50) COMMENT 'IP地址', IpAddress NVARCHAR(50) COMMENT 'IP地址',
Port INT COMMENT '端口', Port INT COMMENT '端口',
Location NVARCHAR(200) COMMENT '安装位置', Location NVARCHAR(200) COMMENT '安装位置',
@@ -30,11 +32,11 @@ CREATE TABLE base_device (
MapModelId NVARCHAR(100) COMMENT '三维地图模型ID', MapModelId NVARCHAR(100) COMMENT '三维地图模型ID',
MapModelScale FLOAT DEFAULT 1.0 COMMENT '模型缩放比例', MapModelScale FLOAT DEFAULT 1.0 COMMENT '模型缩放比例',
MapModelRotation NVARCHAR(100) COMMENT '模型旋转角度(JSON)', MapModelRotation NVARCHAR(100) COMMENT '模型旋转角度(JSON)',
ExtraData TEXT COMMENT '源系统原始数据JSON', ExtraData TEXT COMMENT '适配器扩展数据JSON(Owl/MC4/门禁字段均存于此)',
LocalOverrides TEXT COMMENT '本地覆盖字段JSON', LocalOverrides TEXT COMMENT '本地覆盖字段JSON',
SyncVersion BIGINT DEFAULT 0 COMMENT '同步版本号', SyncVersion BIGINT DEFAULT 0 COMMENT '同步版本号',
LastSyncTime DATETIME COMMENT '上次同步时间', LastSyncTime DATETIME COMMENT '上次同步时间',
Enable NVARCHAR(20) DEFAULT '1' COMMENT '启用状态(数据字典:启用/禁用)', Enable NVARCHAR(20) DEFAULT '启用' COMMENT '启用状态(数据字典:启用/禁用)',
Remark NVARCHAR(500) COMMENT '备注', Remark NVARCHAR(500) COMMENT '备注',
CreateID INT COMMENT '创建人ID', CreateID INT COMMENT '创建人ID',
Creator NVARCHAR(50) COMMENT '创建人', Creator NVARCHAR(50) COMMENT '创建人',
@@ -46,40 +48,19 @@ CREATE TABLE base_device (
INDEX IX_Sync (AdapterCode, SourceId), INDEX IX_Sync (AdapterCode, SourceId),
INDEX IX_Region (RegionId), INDEX IX_Region (RegionId),
INDEX IX_Parent (ParentDeviceId), INDEX IX_Parent (ParentDeviceId),
INDEX IX_Category (DeviceCategory), INDEX IX_Gateway (GatewayNodeId),
INDEX IX_Gateway (GatewayNodeId) INDEX IX_Group (DeviceGroup)
) COMMENT '统一设备主表'; ) COMMENT '统一设备主表';
-- ============================================ -- ============================================
-- 2. 视频设备扩展 -- 2. 视频通道
-- DeviceId(INT) → base_device.DeviceId -- DeviceId(INT) → base_device.DeviceId
-- ============================================ -- ============================================
DROP TABLE IF EXISTS device_video_ext;
CREATE TABLE device_video_ext (
ExtId INT AUTO_INCREMENT COMMENT '扩展记录ID',
DeviceId INT NOT NULL COMMENT '关联设备ID',
OwlDeviceId NVARCHAR(64) NOT NULL COMMENT 'Owl系统设备ID',
Protocol INT DEFAULT 1 COMMENT '接入协议',
Manufacturer NVARCHAR(100) COMMENT '厂商',
Model NVARCHAR(100) COMMENT '设备型号',
ChannelCount INT DEFAULT 0 COMMENT '通道数量',
OwlStatus NVARCHAR(500) COMMENT 'Owl原始状态JSON',
CreateDate DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (ExtId),
INDEX IX_Device (DeviceId),
INDEX IX_Owl (OwlDeviceId)
) COMMENT '视频设备扩展表';
-- ============================================
-- 3. 视频通道扩展表
-- 存Owl通道的流地址/云台/录像能力等扩展信息
-- DeviceId(INT) → base_device.DeviceId一台NVR可有多条通道记录对应不同码流
-- ============================================
DROP TABLE IF EXISTS video_channel; DROP TABLE IF EXISTS video_channel;
CREATE TABLE video_channel ( CREATE TABLE video_channel (
ChannelId INT AUTO_INCREMENT COMMENT '通道记录ID', ChannelId INT AUTO_INCREMENT COMMENT '通道记录ID',
OwlChannelId NVARCHAR(64) NOT NULL COMMENT 'Owl系统通道ID', OwlChannelId NVARCHAR(64) NOT NULL COMMENT 'Owl系统通道ID',
DeviceId INT NOT NULL COMMENT '关联base_device设备ID', DeviceId INT NOT NULL COMMENT '关联Base_Device设备ID',
OwlStreamApp NVARCHAR(50) COMMENT 'Owl流应用名', OwlStreamApp NVARCHAR(50) COMMENT 'Owl流应用名',
OwlStreamName NVARCHAR(100) COMMENT 'Owl流名称', OwlStreamName NVARCHAR(100) COMMENT 'Owl流名称',
HasPtz TINYINT DEFAULT 0 COMMENT '是否支持云台', HasPtz TINYINT DEFAULT 0 COMMENT '是否支持云台',
@@ -90,10 +71,10 @@ CREATE TABLE video_channel (
PRIMARY KEY (ChannelId), PRIMARY KEY (ChannelId),
INDEX IX_Device (DeviceId), INDEX IX_Device (DeviceId),
INDEX IX_Owl (OwlChannelId) INDEX IX_Owl (OwlChannelId)
) COMMENT '视频通道扩展'; ) COMMENT '视频通道表';
-- ============================================ -- ============================================
-- 4. 录像记录表 -- 3. 录像记录表
-- ChannelId(INT) → video_channel.ChannelId -- ChannelId(INT) → video_channel.ChannelId
-- ============================================ -- ============================================
DROP TABLE IF EXISTS video_record; DROP TABLE IF EXISTS video_record;
@@ -115,32 +96,8 @@ CREATE TABLE video_record (
) COMMENT '录像记录表'; ) COMMENT '录像记录表';
-- ============================================ -- ============================================
-- 5. IoT设备扩展表含点位属性 -- 4. 设备数据归档表
-- DeviceId(INT) → base_device.DeviceId -- DeviceId(INT) → base_device.DeviceId
-- 子设备(点位)的额外属性: PointIndex/Unit/IsControlPoint
-- ============================================
DROP TABLE IF EXISTS device_iot_ext;
CREATE TABLE device_iot_ext (
ExtId INT AUTO_INCREMENT COMMENT '扩展记录ID',
DeviceId INT NOT NULL COMMENT '关联设备ID',
Mc4DeviceId INT NOT NULL COMMENT 'MC4.0设备ID',
PointIndex INT DEFAULT 0 COMMENT '点位索引(子设备用)',
PointTag NVARCHAR(100) COMMENT '点位标签',
Unit NVARCHAR(50) COMMENT '单位(数据字典:℃/%%/V/A/kW)',
IsControlPoint NVARCHAR(20) DEFAULT '0' COMMENT '是否控制点(数据字典:只读/可写)',
ObjectType INT COMMENT 'MC4.0对象类型',
Tag NVARCHAR(100) COMMENT '设备标签',
ParentId INT COMMENT 'MC4.0父级ID',
Mc4Option NVARCHAR(500) COMMENT 'MC4.0原始配置JSON',
CreateDate DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (ExtId),
INDEX IX_Device (DeviceId),
INDEX IX_Mc4 (Mc4DeviceId)
) COMMENT '采集设备扩展表';
-- ============================================
-- 6. 设备数据归档表
-- DeviceId(INT) → base_device.DeviceId直接指向子设备/点位)
-- ============================================ -- ============================================
DROP TABLE IF EXISTS iot_devicedata; DROP TABLE IF EXISTS iot_devicedata;
CREATE TABLE iot_devicedata ( CREATE TABLE iot_devicedata (
@@ -157,7 +114,7 @@ CREATE TABLE iot_devicedata (
) COMMENT '设备数据归档表'; ) COMMENT '设备数据归档表';
-- ============================================ -- ============================================
-- 7. 告警记录表(通用) -- 5. 告警记录表(通用)
-- DeviceId(INT) → base_device.DeviceId -- DeviceId(INT) → base_device.DeviceId
-- ============================================ -- ============================================
DROP TABLE IF EXISTS iot_alarm; DROP TABLE IF EXISTS iot_alarm;
@@ -166,14 +123,14 @@ CREATE TABLE iot_alarm (
SourceAlarmId NVARCHAR(100) NOT NULL COMMENT '源系统告警ID', SourceAlarmId NVARCHAR(100) NOT NULL COMMENT '源系统告警ID',
DeviceId INT NOT NULL COMMENT '关联设备ID', DeviceId INT NOT NULL COMMENT '关联设备ID',
AlarmType INT DEFAULT 0 COMMENT '告警类型', AlarmType INT DEFAULT 0 COMMENT '告警类型',
AlarmLevel NVARCHAR(20) DEFAULT '1' COMMENT '告警等级(数据字典:提示/普通/重要/紧急)', AlarmLevel NVARCHAR(20) DEFAULT '提示' COMMENT '告警等级(数据字典:提示/普通/重要/紧急)',
AlarmDesc NVARCHAR(500) COMMENT '告警描述', AlarmDesc NVARCHAR(500) COMMENT '告警描述',
AlarmValue DOUBLE COMMENT '触发值', AlarmValue DOUBLE COMMENT '触发值',
StartTime DATETIME NOT NULL COMMENT '告警开始时间', StartTime DATETIME NOT NULL COMMENT '告警开始时间',
EndTime DATETIME COMMENT '告警结束时间', EndTime DATETIME COMMENT '告警结束时间',
ConfirmTime DATETIME COMMENT '确认时间', ConfirmTime DATETIME COMMENT '确认时间',
ConfirmUser NVARCHAR(50) COMMENT '确认人', ConfirmUser NVARCHAR(50) COMMENT '确认人',
State NVARCHAR(20) DEFAULT '1' COMMENT '状态(数据字典:未确认/已确认/已结束)', State NVARCHAR(20) DEFAULT '未确认' COMMENT '状态(数据字典:未确认/已确认/已结束)',
AdapterCode NVARCHAR(50) COMMENT '来源适配器', AdapterCode NVARCHAR(50) COMMENT '来源适配器',
CreateDate DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', CreateDate DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (AlarmId), PRIMARY KEY (AlarmId),
@@ -183,10 +140,8 @@ CREATE TABLE iot_alarm (
INDEX IX_Level (AlarmLevel) INDEX IX_Level (AlarmLevel)
) COMMENT '告警记录表'; ) COMMENT '告警记录表';
-- ============================================ -- ============================================
-- 8. 网关节点注册表 -- 6. 网关节点注册表
-- NodeCode=网关唯一编码 AdapterTypes=网关上报
-- ============================================ -- ============================================
DROP TABLE IF EXISTS gateway_nodes; DROP TABLE IF EXISTS gateway_nodes;
CREATE TABLE gateway_nodes ( CREATE TABLE gateway_nodes (
@@ -197,8 +152,8 @@ CREATE TABLE gateway_nodes (
AdapterTypes NVARCHAR(200) COMMENT '支持的适配器类型(网关上报)', AdapterTypes NVARCHAR(200) COMMENT '支持的适配器类型(网关上报)',
BaseUrl NVARCHAR(200) COMMENT '网关自身地址(网关上报)', BaseUrl NVARCHAR(200) COMMENT '网关自身地址(网关上报)',
LastHeartbeat DATETIME COMMENT '上次心跳时间', LastHeartbeat DATETIME COMMENT '上次心跳时间',
IsOnline NVARCHAR(20) DEFAULT '0' COMMENT '在线状态(数据字典:在线/离线)', IsOnline NVARCHAR(20) DEFAULT '离线' COMMENT '在线状态(数据字典:在线/离线)',
Enable NVARCHAR(20) DEFAULT '1' COMMENT '启用状态(数据字典:启用/禁用)', Enable NVARCHAR(20) DEFAULT '启用' COMMENT '启用状态(数据字典:启用/禁用)',
Remark NVARCHAR(500) COMMENT '备注', Remark NVARCHAR(500) COMMENT '备注',
CreateID INT COMMENT '创建人ID', CreateID INT COMMENT '创建人ID',
Creator NVARCHAR(50) COMMENT '创建人', Creator NVARCHAR(50) COMMENT '创建人',

View File

@@ -3,7 +3,6 @@
> **版本**: v2.0 > **版本**: v2.0
> **日期**: 2026-05-16 > **日期**: 2026-05-16
> **状态**: 待实施 > **状态**: 待实施
> **替代**: v1.0 系列方案文档
--- ---
@@ -12,12 +11,12 @@
``` ```
前端层 前端层
web.vite 管理端(设备管理+网关管理) warehouse 大屏Map/Live/IoT/Alarm web.vite 管理端(设备管理+网关管理) warehouse 大屏Map/Live/IoT/Alarm
| HTTP REST | HTTP REST + SignalR | HTTP REST | HTTP REST + SignalR
v v v v
Vol.Pro 后端 (api_sqlsugar) Vol.Pro 后端 (api_sqlsugar)
DeviceManagerController / GatewayNodeController / SignalR Hubs DeviceManagerController / GatewayNodeController / SignalR Hubs
Quartz: SyncDevicesJob / RealtimePollJob / AlarmPollJob Quartz: SyncDevicesJob / RealtimePollJob / AlarmPollJob
数据库: 8 张表 (base_device / gateway_nodes / 扩展表...) 数据库: 6 张表 (base_device / video_channel / video_record / iot_devicedata / iot_alarm / gateway_nodes)
| 注册/下发设备/心跳/同步数据 | 注册/下发设备/心跳/同步数据
v v
IntegrationGateway 实例A (:5100) IntegrationGateway 实例B (:5101) IntegrationGateway 实例A (:5100) IntegrationGateway 实例B (:5101)
@@ -28,59 +27,33 @@ IntegrationGateway 实例A (:5100) IntegrationGateway 实例B (:5101)
MC4.0 (:3000) Owl (:80) MC4.0 (:3000) 海康ISC (:80) MC4.0 (:3000) Owl (:80) MC4.0 (:3000) 海康ISC (:80)
``` ```
### 网关多实例架构(方案 C+ ### 核心设计原则
- Vol.Pro 管理网关实例gateway_nodes 表注册所有网关节点 - **网关无状态**:配置仅 NodeCode/Token/VolProUrl挂了重装即恢复
- 网关启动时主动注册POST /api/gateway/register 上报 NodeCode/Token/AdapterTypes/BaseUrl - **AdapterCode 双段标识**"mc4:31号库房" 区分同类型多实例
- Vol.Pro 下发顶层设备Base_Device.GatewayNodeId 关联设备归属 - **DeviceGroup 路由**:基类表用字典字段决定适配器和行为,不依赖扩展表
- 网关无状态:配置只有 3 个值NodeCode/Token/VolProUrl挂了重装即恢复 - **ExtraData JSON**:所有适配器特有字段存入 ExtraData新增适配器不增表
- AdapterCode 双段标识:"mc4:31号库房" 区分同类型多实例 - **心跳机制**:网关 15s 心跳Vol.Pro 超 30s 级联设备离线
- 心跳机制:网关每 15s 上报心跳Vol.Pro 超 30s 无心跳级联设备离线
--- ---
## 二、IntegrationGateway 设计 ## 二、网关架构(方案 C+
### 2.1 项目结构(位于 gateway/ 独立文件夹) ### 2.1 网关注册与心跳流程
``` ```
gateway/ 管理端: gateway_nodes 表新增 → 生成 NodeCode + Token
├── IntegrationGateway.sln 网关配置: { NodeCode, Token, VolProUrl }
└── src/ 网关启动 → POST /register { nodeCode,token,adapterTypes,baseUrl }
├── IntegrationGateway.Host/ # WebAPI (:5100) Vol.Pro 校验 → 更新 AdapterTypes/BaseUrl/IsOnline=在线
| ├── Controllers/ 响应: { gatewayNodeId, devices: [base_device WHERE GatewayNodeId=当前] }
| | ├── HealthController.cs 网关按 AdapterCode 分流 → Adapter 连接第三方 → 发现子设备
| | ├── DevicesController.cs 网关 → POST /sync → Vol.Pro 写入 base_device含 ExtraData)
| | ├── PointsController.cs 网关每 15s → POST /heartbeat { nodeCode, token }
| | ├── StreamsController.cs Vol.Pro Job: IsOnline=在线 且 LastHeartbeat < now-30s → IsOnline=离线 → 级联设备离线
| | ├── AlarmsController.cs
| | ├── SyncController.cs
| | └── RegisterController.cs # 网关注册/心跳
| ├── appsettings.json # { NodeCode, Token, VolProUrl }
| └── Program.cs
├── IntegrationGateway.Core/
| ├── Abstractions/ → 7 个分型接口
| ├── Models/ → 10 个标准化模型
| └── Infrastructure/ → AdapterRegistry / TokenManager / RateLimiter
├── IntegrationGateway.Adapters.Owl/
└── IntegrationGateway.Adapters.MC4/
``` ```
### 2.2 网关注册与心跳流程 ### 2.2 网关配置
```
管理端: gateway_nodes 表新增记录 → 生成 NodeCode + Token
网关配置: appsettings.json { NodeCode, Token, VolProUrl }
网关启动 → POST /api/gateway/register { nodeCode,token,adapterTypes,baseUrl }
Vol.Pro 校验 Token → 更新 AdapterTypes/BaseUrl/IsOnline=1
响应: { gatewayNodeId, devices: [Base_Device WHERE GatewayNodeId=当前网关] }
网关根据 AdapterCode 分流 → Adapter 连接第三方 → 发现子设备
网关 → POST /api/gateway/sync → Vol.Pro 写入 Base_Device 及子设备
网关每 15s → POST /api/gateway/heartbeat { nodeCode, token }
Vol.Pro 后台任务: IsOnline=1 且 LastHeartbeat < now-30s → IsOnline=0 → 级联设备离线
```
### 2.3 网关配置
```json ```json
{ {
@@ -90,9 +63,7 @@ Vol.Pro 后台任务: IsOnline=1 且 LastHeartbeat < now-30s → IsOnline=0 →
} }
``` ```
适配器类型由网关启动时扫描已注册的 Adapter 类自动获取,无需在配置中声明。 ### 2.3 适配器能力矩阵
### 2.4 适配器能力矩阵
| 接口 | Owl | MC4.0 | 门禁(未来) | | 接口 | Owl | MC4.0 | 门禁(未来) |
|------|:---:|:-----:|:----------:| |------|:---:|:-----:|:----------:|
@@ -103,7 +74,7 @@ Vol.Pro 后台任务: IsOnline=1 且 LastHeartbeat < now-30s → IsOnline=0 →
| IHasAlarms | ⚠️ | ✅ | ✅ | | IHasAlarms | ⚠️ | ✅ | ✅ |
| IAcceptsMetadataPush | ✅ | ❌ | ⚠️ | | IAcceptsMetadataPush | ✅ | ❌ | ⚠️ |
### 2.5 双向同步引擎 ### 2.4 双向同步引擎
| 方向 | 说明 | MC4.0 | Owl | | 方向 | 说明 | MC4.0 | Owl |
|------|------|-------|-----| |------|------|-------|-----|
@@ -111,7 +82,7 @@ Vol.Pro 后台任务: IsOnline=1 且 LastHeartbeat < now-30s → IsOnline=0 →
| PushToSource | Vol.Pro→第三方 | 告警确认/控制 | 元数据/PTZ | | PushToSource | Vol.Pro→第三方 | 告警确认/控制 | 元数据/PTZ |
| Bidirectional | 先写第三方再更新本地 | 告警确认 | — | | Bidirectional | 先写第三方再更新本地 | 告警确认 | — |
### 2.6 Gateway API ### 2.5 Gateway API
``` ```
# 注册与心跳 # 注册与心跳
@@ -132,7 +103,7 @@ GET /api/gateway/health
--- ---
## 三、数据模型(8 张表) ## 三、数据模型(6 张表)
### 3.1 区域表 warehouse_regions现有 ### 3.1 区域表 warehouse_regions现有
@@ -142,7 +113,7 @@ GET /api/gateway/health
| RegionName | nvarchar(255) | | RegionName | nvarchar(255) |
| ParentId | int? (自引用树) | | ParentId | int? (自引用树) |
### 3.2 网关节点表 gateway_nodes(新建) ### 3.2 网关节点表 gateway_nodes
| 字段 | 类型 | 说明 | | 字段 | 类型 | 说明 |
|------|------|------| |------|------|------|
@@ -153,126 +124,151 @@ GET /api/gateway/health
| AdapterTypes | NVARCHAR(200) | 适配器类型(网关上报) | | AdapterTypes | NVARCHAR(200) | 适配器类型(网关上报) |
| BaseUrl | NVARCHAR(200) | 网关地址(网关上报) | | BaseUrl | NVARCHAR(200) | 网关地址(网关上报) |
| LastHeartbeat | DATETIME | 上次心跳时间 | | LastHeartbeat | DATETIME | 上次心跳时间 |
| IsOnline | TINYINT DEFAULT 0 | 在线状态 | | IsOnline | NVARCHAR(20) | 在线状态(字典: 在线/离线) |
| Enable | TINYINT DEFAULT 1 | 启用 | | Enable | NVARCHAR(20) | 启用状态(字典: 启用/禁用) |
### 3.3 统一设备主表 Base_Device ### 3.3 统一设备主表 base_device
| 字段 | 类型 | 说明 | | 字段 | 类型 | 说明 |
|------|------|------| |------|------|------|
| DeviceId | INT AUTO_INCREMENT | Vol.Pro内部ID | | DeviceId | INT AUTO_INCREMENT | Vol.Pro内部ID |
| DeviceName | NVARCHAR(100) | 本地名称 | | DeviceName | NVARCHAR(100) | 设备名称 |
| AdapterCode | NVARCHAR(50) | "mc4:31号库房"(类型:实例) | | AdapterCode | NVARCHAR(50) | "mc4:31号库房"(类型:实例) |
| SourceId | NVARCHAR(100) | 第三方原始ID | | SourceId | NVARCHAR(100) | 源系统设备ID |
| DeviceCategory | NVARCHAR(50) | Video/IoT/Access/Barrier/Alarm字典 | | **DeviceCategory** | NVARCHAR(50) | 设备种类(字典: 摄像机/温湿度变送器/...) |
| **DeviceGroup** | NVARCHAR(20) | 设备分组(字典: 视频设备/IoT设备/门禁设备/道闸设备/报警设备) |
| RegionId | INT? | 所属区域ID | | RegionId | INT? | 所属区域ID |
| GatewayNodeId | INT? | 所属网关节点ID | | GatewayNodeId | INT? | 所属网关节点ID |
| IsParent | TINYINT | 是否父设备 | | IsParent | NVARCHAR(20) | 是否父设备(字典: 是/否) |
| ParentDeviceId | INT? | 父设备自引用 | | ParentDeviceId | INT? | 父设备自引用 |
| IsOnline | TINYINT | 在线状态 | | IsOnline | NVARCHAR(20) | 在线状态(字典: 在线/离线) |
| MapModelId | NVARCHAR(100) | VgoMap模型ID | | MapModelId | NVARCHAR(100) | VgoMap模型ID |
| MapModelScale | FLOAT | | | MapModelScale | FLOAT | |
| MapModelRotation | NVARCHAR(100) | | | MapModelRotation | NVARCHAR(100) | |
| Lat/Lng | DOUBLE | WGS84 | | **ExtraData** | TEXT | ★ 适配器扩展JSON(Owl/MC4/门禁字段均存于此) |
| ExtraData | TEXT | 适配器原始JSON |
| LocalOverrides | TEXT | 本地覆盖JSON | | LocalOverrides | TEXT | 本地覆盖JSON |
| SyncVersion | BIGINT | 乐观锁 | | SyncVersion | BIGINT | 乐观锁 |
| LastSyncTime | DATETIME | | | LastSyncTime | DATETIME | |
| Enable | NVARCHAR(20) | 启用状态(字典: 启用/禁用) |
唯一约束: (AdapterCode, SourceId) 唯一约束: (AdapterCode, SourceId)
### 3.4 扩展表 ### 3.4 DeviceGroup 分组规则
- Device_Video_Ext: 视频设备扩展OwlDeviceId, Protocol, ChannelCount Vol.Pro 同步接口通过 DeviceGroup 路由,无需硬编码:
- Video_Channel: 视频通道扩展OwlChannelId, DeviceId, OwlStreamApp/Name, HasPtz, SnapshotUrl
- Video_Record: 录像记录ChannelId, OwlRecordId, StartedAt, FilePath
- Device_IoT_Ext: IoT设备扩展Mc4DeviceId, PointIndex, Unit, IsControlPoint, ObjectType
- IoT_DeviceData: 历史归档DeviceId→子设备, PointValue, 仅存快照)
- IoT_Alarm: 通用告警SourceAlarmId, DeviceId, AlarmLevel, State
### 3.5 层级示例 | DeviceGroup | 网关适配器 | 前端按钮组 | 同步模式 |
|:---:|------|------|:---:|
| 视频设备 | OwlAdapter → IHasStreams | 实时预览/云台/回放/快照 | Merge |
| IoT设备 | Mc4Adapter → IHasPoints | 实时数据/控制/告警 | FullReplace |
| 门禁设备 | AccessAdapter | 远程开门/记录/告警 | Merge |
| 道闸设备 | BarrierAdapter | 抬杆/落杆/记录 | Merge |
| 报警设备 | AlarmAdapter | 查看告警/布防撤防 | Merge |
### 3.5 ExtraData 格式示例
```json
// 摄像机 (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 (32号库房网关, MC4+Owl) gateway_nodes: gw-31ku
warehouse_regions: 厂区 → 新库区 → 31号库房 warehouse_regions: 厂区 → 新库区 → 31号库房
Base_Device (RegionId=3, GatewayNodeId=gw-31ku.NodeId): base_device (RegionId=3, GatewayNodeId=gw-31ku.NodeId):
东北角高位摄像机 (Category=1, Device_Video_Ext) 东北角高位摄像机 (DeviceGroup=视频设备, ExtraData={owlDeviceId,...})
人员计数摄像机 (Category=1, Device_Video_Ext) 人员计数摄像机 (DeviceGroup=视频设备)
动环采集器 (Category=2, IsParent=1, Device_IoT_Ext) 动环采集器 (DeviceGroup=IoT设备, IsParent=是)
├── 温湿度探头 (ParentDeviceId=采集器, Device_IoT_Ext.PointIndex=0) ├── 温湿度变送器 (ParentDeviceId=采集器, ExtraData={pointIndex:0,unit:"℃"})
├── 空调控制器 (ParentDeviceId=采集器, Device_IoT_Ext.IsControlPoint=1) ├── 空调控制器 (ParentDeviceId=采集器, ExtraData={pointIndex:2,isControlPoint:true})
├── 除湿机 (ParentDeviceId=采集器) ├── 除湿/恒湿机 (ParentDeviceId=采集器)
└── 紧急报警按钮 (ParentDeviceId=采集器) └── 紧急报警按钮 (ParentDeviceId=采集器, DeviceGroup=报警设备)
``` ```
--- ---
## 四、管理端统一设备页面 ## 四、Vol.Pro 同步接口(新增适配器零改动)
### 4.1 布局 ```csharp
// 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();
}
``` ```
+------------------+---------------------------------------+
| 顶部工具栏 | |
+------------------+---------------------------------------+
| 左侧区域树 | 右侧设备列表 |
| | |
| 📁 厂区 | 区域:31号库房 最后同步:05-15 |
| 📁 新库区 | +---------------------------------+ |
| 📁 31号库房 ● | | ▸动环采集器 MC4.0 █在线 | |
| 📁 11号库房 | | 东北角摄像机 Owl █在线 | |
| | +---------------------------------+ |
| [+新建区域] | |
+------------------+---------------------------------------+
```
### 4.2 前端文件
```
web.vite/src/views/warehouse/DeviceManager/
├── index.vue # 主页面
├── components/
│ ├── RegionTree.vue # el-tree 区域树
│ ├── DeviceTable.vue # el-table 可展开行
│ ├── DeviceEditDialog.vue # 编辑弹框
│ ├── MapBindingPanel.vue # 地图绑定面板
│ ├── VideoDeviceActions.vue # 视频按钮组
│ └── IoTDeviceActions.vue # IoT按钮组
└── api/deviceManager.js
```
### 4.3 后端 API
| 接口 | 说明 |
|------|------|
| GET /api/DeviceManager/GetRegionTree | 区域树+设备数量 |
| GET /api/DeviceManager/GetDevicesByRegion | 区域设备列表(含子设备) |
| PUT /api/DeviceManager/{deviceId} | 更新设备(含地图绑定) |
| POST /api/DeviceManager/SyncFromGateway | 手动同步 |
### 4.4 操作按钮矩阵
| Category | 按钮 |
|----------|------|
| 1-视频 | 实时预览/云台控制/查看回放/获取快照/同步通道 |
| 2-IoT | 查看实时数据/设备控制/刷新点位/查看告警 |
| 3-门禁 | 远程开门/查看记录/查看告警 |
| 4-道闸 | 抬杆/落杆/查看记录 |
--- ---
## 五、同步策略 ## 五、管理端统一设备页面
### 5.1 操作按钮矩阵(按 DeviceGroup 路由)
| DeviceGroup | 操作按钮 |
|:---:|------|
| 视频设备 | 实时预览 / 云台控制 / 查看回放 / 获取快照 / 同步通道 |
| IoT设备 | 查看实时数据 / 设备控制 / 刷新点位 / 查看告警 |
| 门禁设备 | 远程开门 / 查看记录 / 查看告警 |
| 道闸设备 | 抬杆 / 落杆 / 查看记录 |
| 报警设备 | 查看告警 / 布防撤防 |
### 5.2 前端按钮路由
```javascript
// DeviceTable.vue
const actionMap = {
'视频设备': VideoDeviceActions,
'IoT设备': IoTDeviceActions,
'门禁设备': AccessDeviceActions,
'道闸设备': BarrierDeviceActions,
'报警设备': AlarmDeviceActions,
}
// 渲染: actionMap[device.DeviceGroup]
```
---
## 六、同步策略
### MC4.0 → 区域树+设备 ### MC4.0 → 区域树+设备
- type=1 节点 → 名称匹配 warehouse_regions → 绑区或新建 - type=1 节点 → 名称匹配 warehouse_regions → 绑区或新建
- type=2 节点 → Upsert Base_Device, RegionId=叶子区域, GatewayNodeId=当前网关 - type=2 节点 → Upsert base_device, DeviceGroup=IoT设备, ExtraData 存点位属性
- 模式: FullReplace, 频率限制: 2次/秒 - 模式: FullReplace, 频率限制: 2次/秒
### Owl → 设备 ### Owl → 设备
- GET /devices → Upsert Base_Device (Category=1, IsParent=1) - GET /devices → Upsert base_device (DeviceGroup=视频设备, IsParent=)
- GET /channels → Upsert Base_Device (ParentDeviceId=NVR) - GET /channels → Upsert base_device (ParentDeviceId=NVR) + video_channel
- Owl 无区域概念 → RegionId=NULL, 管理员手动分配 - Owl 无区域概念 → RegionId=NULL, 管理员手动分配
- 模式: Merge - 模式: Merge
@@ -286,7 +282,7 @@ web.vite/src/views/warehouse/DeviceManager/
--- ---
## 、部署拓扑 ## 、部署拓扑
``` ```
Docker: Owl+ZLM (:80) MC4.0-1 (:3000) MC4.0-2 (:3000) Docker: Owl+ZLM (:80) MC4.0-1 (:3000) MC4.0-2 (:3000)
@@ -311,11 +307,11 @@ Docker: Owl+ZLM (:80) MC4.0-1 (:3000) MC4.0-2 (:3000)
--- ---
## 、实施路线 ## 、实施路线
| 阶段 | 工期 | 内容 | | 阶段 | 工期 | 内容 |
|------|------|------| |------|------|------|
| Phase 0 | Day 1-2 | Gateway骨架 + 8张表建表 + 代码生成 | | Phase 0 | Day 1-2 | Gateway骨架 + 6张表建表 + 代码生成 |
| Phase 1 | Day 3-6 | OwlAdapter + 管理端视频设备页 | | Phase 1 | Day 3-6 | OwlAdapter + 管理端视频设备页 |
| Phase 2 | Day 7-11 | Mc4Adapter + IoT管理 + 区域树匹配 + SignalR | | Phase 2 | Day 7-11 | Mc4Adapter + IoT管理 + 区域树匹配 + SignalR |
| Phase 3 | Day 12-17 | warehouse端改造 + 全链路联调 | | Phase 3 | Day 12-17 | warehouse端改造 + 全链路联调 |
@@ -325,19 +321,19 @@ Docker: Owl+ZLM (:80) MC4.0-1 (:3000) MC4.0-2 (:3000)
--- ---
## 、新增整合流程 ## 、新增整合流程
以接入「海康门禁」为例: 以接入「海康门禁」为例:
1. 新建 IntegrationGateway.Adapters.HikvisionAccess 项目 1. 新建 IntegrationGateway.Adapters.HikvisionAccess 项目
2. 实现 IHasFlatDevices + IHasAlarms 2. 实现 IHasFlatDevices + IHasAlarms → 设备同步时填充 DeviceGroup=门禁设备
3. 注册到 Host → 网关启动时自动上报到 Vol.Pro 3. 管理端字典加一条"门禁设备"分组 → 按钮自动出现
4. 前端新增 AccessDeviceActions.vue (~80行) 4. Vol.Pro 同步接口零改动ExtraData 承载门禁字段)
5. Vol.Pro 后端零改动 5. 前端新增 AccessDeviceActions.vue (~80行)
总工作量: 1-2 天 总工作量: 1-2 天
--- ---
## 、代码组织规范 ## 、代码组织规范
| 代码类型 | 路径 | 被覆盖? | | 代码类型 | 路径 | 被覆盖? |
|----------|------|:---:| |----------|------|:---:|