V4完成: 预览+云台整合 操作列JSX按钮 全链路调通
This commit is contained in:
@@ -41,13 +41,10 @@ namespace VolPro.Entity.DomainModels
|
||||
/// <summary>
|
||||
///来源适配器(类型:实例)
|
||||
/// </summary>
|
||||
[SugarColumn(IsPrimaryKey = true)]
|
||||
[Key]
|
||||
[Display(Name ="来源适配器(类型:实例)")]
|
||||
[MaxLength(50)]
|
||||
[Column(TypeName="nvarchar(50)")]
|
||||
[Editable(true)]
|
||||
[Required(AllowEmptyStrings=false)]
|
||||
public string AdapterCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
@@ -57,7 +54,6 @@ namespace VolPro.Entity.DomainModels
|
||||
[MaxLength(100)]
|
||||
[Column(TypeName="nvarchar(100)")]
|
||||
[Editable(true)]
|
||||
[Required(AllowEmptyStrings=false)]
|
||||
public string SourceId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
@@ -73,8 +69,6 @@ namespace VolPro.Entity.DomainModels
|
||||
/// <summary>
|
||||
///设备分组(数据字典)
|
||||
/// </summary>
|
||||
[SugarColumn(IsPrimaryKey = true)]
|
||||
[Key]
|
||||
[Display(Name ="设备分组(数据字典)")]
|
||||
[MaxLength(20)]
|
||||
[Column(TypeName="nvarchar(20)")]
|
||||
@@ -85,23 +79,11 @@ namespace VolPro.Entity.DomainModels
|
||||
/// <summary>
|
||||
///所属点位ID
|
||||
/// </summary>
|
||||
[SugarColumn(IsPrimaryKey = true, IsIdentity = true)]
|
||||
[Key]
|
||||
[Display(Name ="所属点位ID")]
|
||||
[Column(TypeName="int")]
|
||||
[Editable(true)]
|
||||
public int? PointId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///所属网关节点ID
|
||||
/// </summary>
|
||||
[SugarColumn(IsPrimaryKey = true, IsIdentity = true)]
|
||||
[Key]
|
||||
[Display(Name ="所属网关节点ID")]
|
||||
[Column(TypeName="int")]
|
||||
[Editable(true)]
|
||||
public int? GatewayNodeId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///是否父设备(数据字典)
|
||||
/// </summary>
|
||||
@@ -115,8 +97,6 @@ namespace VolPro.Entity.DomainModels
|
||||
/// <summary>
|
||||
///父设备ID(自引用,子设备挂父设备下)
|
||||
/// </summary>
|
||||
[SugarColumn(IsPrimaryKey = true, IsIdentity = true)]
|
||||
[Key]
|
||||
[Display(Name ="父设备ID(自引用,子设备挂父设备下)")]
|
||||
[Column(TypeName="int")]
|
||||
[Editable(true)]
|
||||
@@ -129,7 +109,6 @@ namespace VolPro.Entity.DomainModels
|
||||
[MaxLength(20)]
|
||||
[Column(TypeName="nvarchar(20)")]
|
||||
[Editable(true)]
|
||||
[Required(AllowEmptyStrings=false)]
|
||||
public string IsOnline { get; set; }
|
||||
|
||||
/// <summary>
|
||||
@@ -284,6 +263,16 @@ namespace VolPro.Entity.DomainModels
|
||||
[Editable(true)]
|
||||
public DateTime? ModifyDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///所属网关节点ID
|
||||
/// </summary>
|
||||
[SugarColumn(IsPrimaryKey = true, IsIdentity = true)]
|
||||
[Key]
|
||||
[Display(Name ="所属网关节点ID")]
|
||||
[Column(TypeName="int")]
|
||||
[Editable(true)]
|
||||
public int? NodeId { get; set; }
|
||||
|
||||
[Display(Name ="视频通道")]
|
||||
[ForeignKey("DeviceId")][Navigate(NavigateType.OneToMany,nameof(DeviceId),nameof(DeviceId))]
|
||||
public List<video_channel> video_channel { get; set; }
|
||||
|
||||
@@ -14,7 +14,7 @@ using VolPro.Entity.SystemModels;
|
||||
|
||||
namespace VolPro.Entity.DomainModels
|
||||
{
|
||||
[Entity(TableCnName = "设备管理_网关节点",TableName = "gateway_nodes",DBServer = "ServiceDbContext")]
|
||||
[Entity(TableCnName = "设备管理_网关节点",TableName = "gateway_nodes",DetailTable = new Type[] { typeof(base_device)},DetailTableCnName = "设备管理",DBServer = "ServiceDbContext")]
|
||||
public partial class gateway_nodes:ServiceEntity
|
||||
{
|
||||
/// <summary>
|
||||
@@ -165,6 +165,11 @@ namespace VolPro.Entity.DomainModels
|
||||
[Editable(true)]
|
||||
public DateTime? ModifyDate { get; set; }
|
||||
|
||||
[Display(Name ="设备管理")]
|
||||
[ForeignKey("NodeId")][Navigate(NavigateType.OneToMany,nameof(NodeId),nameof(NodeId))]
|
||||
public List<base_device> base_device { get; set; }
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -11,17 +11,17 @@ namespace VolPro.Entity.DomainModels
|
||||
{
|
||||
public partial class base_device
|
||||
{
|
||||
/// <summary>导航属性:关联视频通道扩展记录(一对一)</summary>
|
||||
[Navigate(NavigateType.OneToOne, nameof(DeviceId), nameof(video_channel.DeviceId))]
|
||||
public video_channel? VideoChannel { get; set; }
|
||||
/////// <summary>导航属性:关联视频通道扩展记录(一对一)</summary>
|
||||
////[Navigate(NavigateType.OneToOne, nameof(DeviceId), nameof(video_channel.DeviceId))]
|
||||
////public video_channel? VideoChannel { get; set; }
|
||||
|
||||
/// <summary>导航属性:关联告警记录(一对多)</summary>
|
||||
[Navigate(NavigateType.OneToMany, nameof(DeviceId), nameof(iot_alarm.DeviceId))]
|
||||
public List<iot_alarm>? Alarms { get; set; }
|
||||
/////// <summary>导航属性:关联告警记录(一对多)</summary>
|
||||
////[Navigate(NavigateType.OneToMany, nameof(DeviceId), nameof(iot_alarm.DeviceId))]
|
||||
////public List<iot_alarm>? Alarms { get; set; }
|
||||
|
||||
/// <summary>导航属性:关联数据归档(一对多)</summary>
|
||||
[Navigate(NavigateType.OneToMany, nameof(DeviceId), nameof(iot_devicedata.DeviceId))]
|
||||
public List<iot_devicedata>? DeviceData { get; set; }
|
||||
/////// <summary>导航属性:关联数据归档(一对多)</summary>
|
||||
////[Navigate(NavigateType.OneToMany, nameof(DeviceId), nameof(iot_devicedata.DeviceId))]
|
||||
////public List<iot_devicedata>? DeviceData { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 网关字段白名单。网关同步时,只有此集合中的字段会被覆盖,
|
||||
|
||||
@@ -17,6 +17,26 @@ namespace Warehouse.Controllers
|
||||
{
|
||||
public partial class base_deviceController
|
||||
{
|
||||
private readonly Ibase_deviceService _service;//访问业务代码
|
||||
private readonly Iwarehouse_regionsService _regionsService;
|
||||
private readonly Iwarehouse_devicepointService _pointService;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
|
||||
[ActivatorUtilitiesConstructor]
|
||||
public base_deviceController(
|
||||
Ibase_deviceService service,
|
||||
Iwarehouse_regionsService regionsService,
|
||||
Iwarehouse_devicepointService pointService,
|
||||
IHttpContextAccessor httpContextAccessor
|
||||
)
|
||||
: base(service)
|
||||
{
|
||||
_service = service;
|
||||
_regionsService = regionsService;
|
||||
_pointService = pointService;
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取区域→点位→设备树。
|
||||
/// 用于管理端左侧树形控件展示层级结构。
|
||||
@@ -26,32 +46,27 @@ namespace Warehouse.Controllers
|
||||
[Route("/api/DeviceManager/GetRegionTree")]
|
||||
public async Task<IActionResult> GetRegionTree()
|
||||
{
|
||||
// 获取区域和点位服务
|
||||
var regionSvcType = _service.GetType().Assembly.GetType("Warehouse.Services.warehouse_regionsService");
|
||||
var pointSvcType = _service.GetType().Assembly.GetType("Warehouse.Services.warehouse_devicepointService");
|
||||
|
||||
var regionSvc = regionSvcType?.GetProperty("Instance")?.GetValue(null) as IService<warehouse_regions>;
|
||||
var pointSvc = pointSvcType?.GetProperty("Instance")?.GetValue(null) as IService<warehouse_devicepoint>;
|
||||
|
||||
// 查所有区域
|
||||
var regions = regionSvc != null
|
||||
? await regionSvc.FindAsIQueryable(x => true).ToListAsync()
|
||||
: new List<warehouse_regions>();
|
||||
var regions = await _regionsService.FindAsIQueryable(x => true).ToListAsync();
|
||||
|
||||
// 查所有点位
|
||||
var points = pointSvc != null
|
||||
? await pointSvc.FindAsIQueryable(x => true).ToListAsync()
|
||||
: new List<warehouse_devicepoint>();
|
||||
var points = await _pointService.FindAsIQueryable(x => true).ToListAsync();
|
||||
|
||||
// 统计每个点位下的设备数量
|
||||
//var deviceCounts = new Dictionary<int, int>();
|
||||
//var allDevices = await _service.FindAsIQueryable(x => true)
|
||||
// .Where(x => x.PointId != null)
|
||||
// .GroupBy(x => x.PointId!.Value)
|
||||
// .Select(g => new { PointId = g.Key, Count = g.Count() })
|
||||
// .ToListAsync();
|
||||
var deviceCounts = new Dictionary<int, int>();
|
||||
var allDevices = await _service.FindAsIQueryable(x => true)
|
||||
.Where(x => x.PointId != null)
|
||||
.GroupBy(x => x.PointId!.Value)
|
||||
.Select(g => new { PointId = g.Key, Count = g.Count() })
|
||||
var devices = await _service.FindAsIQueryable(x => x.PointId != null)
|
||||
.Select(x => new { x.PointId })
|
||||
.ToListAsync();
|
||||
foreach (var g in allDevices)
|
||||
deviceCounts[g.PointId] = g.Count;
|
||||
deviceCounts = devices
|
||||
.Where(x => x.PointId.HasValue)
|
||||
.GroupBy(x => x.PointId!.Value)
|
||||
.ToDictionary(g => g.Key, g => g.Count());
|
||||
|
||||
// 构建树形结构
|
||||
var tree = new List<object>();
|
||||
|
||||
@@ -3,21 +3,45 @@
|
||||
*A组接口使用 [AllowAnonymous] + NodeToken 二次认证
|
||||
*所有改动在 Partial 目录,不破坏框架可升级性
|
||||
*/
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using VolPro.Entity.DomainModels;
|
||||
using Warehouse.IServices;
|
||||
using System.Linq;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Threading.Tasks;
|
||||
using VolPro.Core.DBManager;
|
||||
using VolPro.Core.DbSqlSugar;
|
||||
using VolPro.Entity.DomainModels;
|
||||
using Warehouse.IRepositories;
|
||||
using Warehouse.IServices;
|
||||
using Warehouse.Services;
|
||||
|
||||
namespace Warehouse.Controllers
|
||||
{
|
||||
public partial class gateway_nodesController
|
||||
{
|
||||
private readonly Igateway_nodesService _service;//访问业务代码
|
||||
private readonly Ibase_deviceService _deviceService;
|
||||
private readonly Iiot_alarmService _iot_alarmService;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
|
||||
[ActivatorUtilitiesConstructor]
|
||||
public gateway_nodesController(
|
||||
Igateway_nodesService service,
|
||||
Ibase_deviceService deviceService,
|
||||
Iiot_alarmService iot_alarmService,
|
||||
IHttpContextAccessor httpContextAccessor
|
||||
)
|
||||
: base(service)
|
||||
{
|
||||
_service = service;
|
||||
_deviceService = deviceService;
|
||||
_iot_alarmService = iot_alarmService;
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
}
|
||||
|
||||
/// <summary>A1: 网关注册(Upsert)。认证方式: NodeToken</summary>
|
||||
[HttpPost]
|
||||
[Route("/api/gateway/register")]
|
||||
@@ -32,11 +56,7 @@ namespace Warehouse.Controllers
|
||||
var node = await _service.RegisterNodeAsync(req.NodeCode, req.Token, req.AdapterTypes, req.BaseUrl);
|
||||
|
||||
// 返回当前网关的顶层设备列表
|
||||
var deviceSvc = _service.GetType().Assembly.GetType("Warehouse.Services.base_deviceService")
|
||||
?.GetProperty("Instance")?.GetValue(null) as Ibase_deviceService;
|
||||
var devices = deviceSvc != null
|
||||
? await deviceSvc.GetDevicesByGatewayNodeAsync(node.NodeId)
|
||||
: new List<base_device>();
|
||||
var devices = await _deviceService.GetDevicesByGatewayNodeAsync(node.NodeId);
|
||||
|
||||
return Ok(new { nodeId = node.NodeId, devices = devices.Select(d => new {
|
||||
d.DeviceId, d.DeviceName, d.AdapterCode, d.SourceId,
|
||||
@@ -125,12 +145,10 @@ namespace Warehouse.Controllers
|
||||
if (node == null) return StatusCode(401, new { message = "认证失败" });
|
||||
|
||||
// 获取告警服务
|
||||
var alarmSvcType = _service.GetType().Assembly.GetType("Warehouse.Services.iot_alarmService");
|
||||
var alarmSvc = alarmSvcType?.GetProperty("Instance")?.GetValue(null) as Iiot_alarmService;
|
||||
var alarmSvc = _iot_alarmService;
|
||||
|
||||
// 批量查询 DeviceSourceId → DeviceId 映射
|
||||
var deviceSvcType = _service.GetType().Assembly.GetType("Warehouse.Services.base_deviceService");
|
||||
var deviceSvc = deviceSvcType?.GetProperty("Instance")?.GetValue(null) as Ibase_deviceService;
|
||||
var deviceSvc = _deviceService;
|
||||
|
||||
int added = 0;
|
||||
foreach (var a in req.Alarms)
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
/*
|
||||
*所有关于base_device类的业务代码接口应在此处编写
|
||||
*/
|
||||
using VolPro.Core.BaseProvider;
|
||||
using VolPro.Entity.DomainModels;
|
||||
using VolPro.Core.Utilities;
|
||||
using System.Linq.Expressions;
|
||||
using VolPro.Core.BaseProvider;
|
||||
using VolPro.Core.Utilities;
|
||||
using VolPro.Entity.DomainModels;
|
||||
using Warehouse.Services;
|
||||
namespace Warehouse.IServices
|
||||
{
|
||||
public partial interface Ibase_deviceService
|
||||
{
|
||||
Task<List<base_device>> GetDevicesByGatewayNodeAsync(int gatewayNodeId);
|
||||
|
||||
Task UpsertDeviceAsync(SyncDeviceItem d, int gatewayNodeId, Dictionary<(string, string), int> existingIds);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
/*
|
||||
*所有关于gateway_nodes类的业务代码接口应在此处编写
|
||||
*/
|
||||
using VolPro.Core.BaseProvider;
|
||||
using VolPro.Entity.DomainModels;
|
||||
using VolPro.Core.Utilities;
|
||||
using System.Linq.Expressions;
|
||||
using VolPro.Core.BaseProvider;
|
||||
using VolPro.Core.Utilities;
|
||||
using VolPro.Entity.DomainModels;
|
||||
using Warehouse.Services;
|
||||
namespace Warehouse.IServices
|
||||
{
|
||||
public partial interface Igateway_nodesService
|
||||
{
|
||||
Task<gateway_nodes> RegisterNodeAsync(string nodeCode, string token, string adapterTypes, string baseUrl);
|
||||
|
||||
Task UpdateHeartbeatAsync(string nodeCode, string token);
|
||||
|
||||
Task<(int added, int updated)> SyncDevicesAsync(int gatewayNodeId, List<SyncDeviceItem> devices);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
/*
|
||||
*所有关于iot_alarm类的业务代码接口应在此处编写
|
||||
*/
|
||||
using VolPro.Core.BaseProvider;
|
||||
using VolPro.Entity.DomainModels;
|
||||
using VolPro.Core.Utilities;
|
||||
using System.Linq.Expressions;
|
||||
using VolPro.Core.BaseProvider;
|
||||
using VolPro.Core.Utilities;
|
||||
using VolPro.Entity.DomainModels;
|
||||
using Warehouse.Services;
|
||||
namespace Warehouse.IServices
|
||||
{
|
||||
public partial interface Iiot_alarmService
|
||||
{
|
||||
Task UpsertAlarmAsync(SyncAlarmItem a, int? deviceId);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ public class HeartbeatMonitorJob : IJob
|
||||
if (devSvc != null)
|
||||
{
|
||||
var devices = await devSvc.FindAsIQueryable(
|
||||
x => x.GatewayNodeId == node.NodeId && x.IsOnline == "在线")
|
||||
x => x.NodeId == node.NodeId && x.IsOnline == "在线")
|
||||
.ToListAsync();
|
||||
foreach (var dev in devices)
|
||||
{
|
||||
|
||||
@@ -48,7 +48,7 @@ namespace Warehouse.Services
|
||||
public async Task<List<base_device>> GetDevicesByGatewayNodeAsync(int gatewayNodeId)
|
||||
{
|
||||
return await _repository.DbContext.Queryable<base_device>()
|
||||
.Where(x => x.GatewayNodeId == gatewayNodeId && x.ParentDeviceId == null)
|
||||
.Where(x => x.NodeId == gatewayNodeId && x.ParentDeviceId == null)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ namespace Warehouse.Services
|
||||
SourceId = d.SourceId,
|
||||
DeviceCategory = d.Category,
|
||||
DeviceGroup = d.Group,
|
||||
GatewayNodeId = gatewayNodeId,
|
||||
NodeId = gatewayNodeId,
|
||||
IsParent = d.IsParent ? "是" : "否",
|
||||
ParentDeviceId = parentDeviceId,
|
||||
IsOnline = d.IsOnline ? "在线" : "离线",
|
||||
|
||||
@@ -113,7 +113,7 @@ namespace Warehouse.Services
|
||||
// 批量查询已有设备映射表(用于 parentSourceId → ParentDeviceId 解析)
|
||||
var adapterCodes = devices.Select(d => d.AdapterCode).Distinct().ToList();
|
||||
var existingIds = db.Queryable<base_device>()
|
||||
.Where(x => x.GatewayNodeId == gatewayNodeId && adapterCodes.Contains(x.AdapterCode))
|
||||
.Where(x => x.NodeId == gatewayNodeId && adapterCodes.Contains(x.AdapterCode))
|
||||
.ToList()
|
||||
.ToDictionary(x => (x.AdapterCode, x.SourceId), x => x.DeviceId);
|
||||
|
||||
@@ -142,7 +142,7 @@ namespace Warehouse.Services
|
||||
SourceId = d.SourceId,
|
||||
DeviceCategory = d.Category,
|
||||
DeviceGroup = d.Group,
|
||||
GatewayNodeId = gatewayNodeId,
|
||||
NodeId = gatewayNodeId,
|
||||
IsParent = d.IsParent ? "是" : "否",
|
||||
ParentDeviceId = parentDeviceId,
|
||||
IsOnline = d.IsOnline ? "在线" : "离线",
|
||||
|
||||
@@ -55,11 +55,11 @@ namespace Warehouse.Services
|
||||
var alarm = new iot_alarm
|
||||
{
|
||||
SourceAlarmId = a.SourceAlarmId,
|
||||
DeviceId = deviceId,
|
||||
DeviceId = (int)deviceId,
|
||||
AdapterCode = a.AdapterCode,
|
||||
AlarmLevel = a.Level,
|
||||
AlarmDesc = a.Desc,
|
||||
AlarmValue = a.Value,
|
||||
AlarmValue = (decimal?)a.Value,
|
||||
StartTime = DateTime.TryParse(a.StartTime, out var st) ? st : DateTime.Now,
|
||||
State = "未确认",
|
||||
CreateDate = DateTime.Now
|
||||
|
||||
@@ -13,15 +13,15 @@ DROP TABLE IF EXISTS base_device;
|
||||
CREATE TABLE base_device (
|
||||
DeviceId INT AUTO_INCREMENT COMMENT '设备ID',
|
||||
DeviceName NVARCHAR(100) NOT NULL COMMENT '设备名称',
|
||||
AdapterCode NVARCHAR(50) NOT NULL COMMENT '来源适配器(类型:实例)',
|
||||
SourceId NVARCHAR(100) NOT NULL COMMENT '源系统设备ID',
|
||||
AdapterCode NVARCHAR(50) COMMENT '来源适配器(类型:实例)',
|
||||
SourceId NVARCHAR(100) COMMENT '源系统设备ID',
|
||||
DeviceCategory NVARCHAR(50) NOT NULL COMMENT '设备种类(数据字典:门磁/空调/智能断路器/人行道闸/车辆道闸/485钥匙柜/网络钥匙柜/紧急报警按钮/红外报警器/门禁一体机/除湿_恒湿机/空调控制器/烟雾报警器/气体报警器/温湿度变送器/摄像机/硬盘录像机/动环采集器)',
|
||||
DeviceGroup NVARCHAR(20) NOT NULL COMMENT '设备分组(数据字典:视频设备/IoT设备/门禁设备/道闸设备/报警设备)',
|
||||
PointId INT NULL COMMENT '所属点位ID',
|
||||
GatewayNodeId INT NULL COMMENT '所属网关节点ID',
|
||||
NodeId INT NULL COMMENT '所属网关节点ID',
|
||||
IsParent NVARCHAR(20) NOT NULL DEFAULT '否' COMMENT '是否父设备(数据字典:是/否)',
|
||||
ParentDeviceId INT NULL COMMENT '父设备ID(自引用,子设备挂父设备下)',
|
||||
IsOnline NVARCHAR(20) NOT NULL DEFAULT '离线' COMMENT '在线状态(数据字典:在线/离线)',
|
||||
IsOnline NVARCHAR(20) DEFAULT '离线' COMMENT '在线状态(数据字典:在线/离线)',
|
||||
IpAddress NVARCHAR(50) COMMENT 'IP地址',
|
||||
Port INT COMMENT '端口',
|
||||
Location NVARCHAR(200) COMMENT '安装位置',
|
||||
|
||||
Reference in New Issue
Block a user