From f9eeeed843e013636d82eb18c1c909fed9d42048 Mon Sep 17 00:00:00 2001 From: g82tt Date: Sun, 17 May 2026 14:22:46 +0800 Subject: [PATCH] =?UTF-8?q?V4=E5=AE=8C=E6=88=90:=20=E9=A2=84=E8=A7=88+?= =?UTF-8?q?=E4=BA=91=E5=8F=B0=E6=95=B4=E5=90=88=20=E6=93=8D=E4=BD=9C?= =?UTF-8?q?=E5=88=97JSX=E6=8C=89=E9=92=AE=20=E5=85=A8=E9=93=BE=E8=B7=AF?= =?UTF-8?q?=E8=B0=83=E9=80=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../device_manager/base_device.cs | 31 ++++------- .../device_manager/gateway_nodes.cs | 7 ++- .../device_manager/partial/base_device.cs | 18 +++---- .../Partial/base_deviceController.cs | 53 ++++++++++++------- .../Partial/gateway_nodesController.cs | 48 +++++++++++------ .../Partial/Ibase_deviceService.cs | 12 +++-- .../Partial/Igateway_nodesService.cs | 12 +++-- .../Partial/Iiot_alarmService.cs | 10 ++-- .../Warehouse/Services/HeartbeatMonitorJob.cs | 2 +- .../Partial/base_deviceService.cs | 4 +- .../Partial/gateway_nodesService.cs | 4 +- .../Partial/iot_alarmService.cs | 4 +- doc/db_init.sql | 8 +-- 13 files changed, 128 insertions(+), 85 deletions(-) diff --git a/api_sqlsugar/VolPro.Entity/DomainModels/device_manager/base_device.cs b/api_sqlsugar/VolPro.Entity/DomainModels/device_manager/base_device.cs index 242f69b..9ad1f02 100644 --- a/api_sqlsugar/VolPro.Entity/DomainModels/device_manager/base_device.cs +++ b/api_sqlsugar/VolPro.Entity/DomainModels/device_manager/base_device.cs @@ -41,13 +41,10 @@ namespace VolPro.Entity.DomainModels /// ///来源适配器(类型:实例) /// - [SugarColumn(IsPrimaryKey = true)] - [Key] [Display(Name ="来源适配器(类型:实例)")] [MaxLength(50)] [Column(TypeName="nvarchar(50)")] [Editable(true)] - [Required(AllowEmptyStrings=false)] public string AdapterCode { get; set; } /// @@ -57,7 +54,6 @@ namespace VolPro.Entity.DomainModels [MaxLength(100)] [Column(TypeName="nvarchar(100)")] [Editable(true)] - [Required(AllowEmptyStrings=false)] public string SourceId { get; set; } /// @@ -73,8 +69,6 @@ namespace VolPro.Entity.DomainModels /// ///设备分组(数据字典) /// - [SugarColumn(IsPrimaryKey = true)] - [Key] [Display(Name ="设备分组(数据字典)")] [MaxLength(20)] [Column(TypeName="nvarchar(20)")] @@ -85,23 +79,11 @@ namespace VolPro.Entity.DomainModels /// ///所属点位ID /// - [SugarColumn(IsPrimaryKey = true, IsIdentity = true)] - [Key] [Display(Name ="所属点位ID")] [Column(TypeName="int")] [Editable(true)] public int? PointId { get; set; } - /// - ///所属网关节点ID - /// - [SugarColumn(IsPrimaryKey = true, IsIdentity = true)] - [Key] - [Display(Name ="所属网关节点ID")] - [Column(TypeName="int")] - [Editable(true)] - public int? GatewayNodeId { get; set; } - /// ///是否父设备(数据字典) /// @@ -115,8 +97,6 @@ namespace VolPro.Entity.DomainModels /// ///父设备ID(自引用,子设备挂父设备下) /// - [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; } /// @@ -284,6 +263,16 @@ namespace VolPro.Entity.DomainModels [Editable(true)] public DateTime? ModifyDate { get; set; } + /// + ///所属网关节点ID + /// + [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 { get; set; } diff --git a/api_sqlsugar/VolPro.Entity/DomainModels/device_manager/gateway_nodes.cs b/api_sqlsugar/VolPro.Entity/DomainModels/device_manager/gateway_nodes.cs index d390516..5ed35a9 100644 --- a/api_sqlsugar/VolPro.Entity/DomainModels/device_manager/gateway_nodes.cs +++ b/api_sqlsugar/VolPro.Entity/DomainModels/device_manager/gateway_nodes.cs @@ -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 { /// @@ -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 { get; set; } + + } } \ No newline at end of file diff --git a/api_sqlsugar/VolPro.Entity/DomainModels/device_manager/partial/base_device.cs b/api_sqlsugar/VolPro.Entity/DomainModels/device_manager/partial/base_device.cs index 02be855..370b052 100644 --- a/api_sqlsugar/VolPro.Entity/DomainModels/device_manager/partial/base_device.cs +++ b/api_sqlsugar/VolPro.Entity/DomainModels/device_manager/partial/base_device.cs @@ -11,17 +11,17 @@ namespace VolPro.Entity.DomainModels { public partial class base_device { - /// 导航属性:关联视频通道扩展记录(一对一) - [Navigate(NavigateType.OneToOne, nameof(DeviceId), nameof(video_channel.DeviceId))] - public video_channel? VideoChannel { get; set; } + /////// 导航属性:关联视频通道扩展记录(一对一) + ////[Navigate(NavigateType.OneToOne, nameof(DeviceId), nameof(video_channel.DeviceId))] + ////public video_channel? VideoChannel { get; set; } - /// 导航属性:关联告警记录(一对多) - [Navigate(NavigateType.OneToMany, nameof(DeviceId), nameof(iot_alarm.DeviceId))] - public List? Alarms { get; set; } + /////// 导航属性:关联告警记录(一对多) + ////[Navigate(NavigateType.OneToMany, nameof(DeviceId), nameof(iot_alarm.DeviceId))] + ////public List? Alarms { get; set; } - /// 导航属性:关联数据归档(一对多) - [Navigate(NavigateType.OneToMany, nameof(DeviceId), nameof(iot_devicedata.DeviceId))] - public List? DeviceData { get; set; } + /////// 导航属性:关联数据归档(一对多) + ////[Navigate(NavigateType.OneToMany, nameof(DeviceId), nameof(iot_devicedata.DeviceId))] + ////public List? DeviceData { get; set; } /// /// 网关字段白名单。网关同步时,只有此集合中的字段会被覆盖, diff --git a/api_sqlsugar/VolPro.WebApi/Controllers/Warehouse/Partial/base_deviceController.cs b/api_sqlsugar/VolPro.WebApi/Controllers/Warehouse/Partial/base_deviceController.cs index be1d362..801ed2b 100644 --- a/api_sqlsugar/VolPro.WebApi/Controllers/Warehouse/Partial/base_deviceController.cs +++ b/api_sqlsugar/VolPro.WebApi/Controllers/Warehouse/Partial/base_deviceController.cs @@ -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; + } + /// /// 获取区域→点位→设备树。 /// 用于管理端左侧树形控件展示层级结构。 @@ -26,32 +46,27 @@ namespace Warehouse.Controllers [Route("/api/DeviceManager/GetRegionTree")] public async Task 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; - var pointSvc = pointSvcType?.GetProperty("Instance")?.GetValue(null) as IService; - // 查所有区域 - var regions = regionSvc != null - ? await regionSvc.FindAsIQueryable(x => true).ToListAsync() - : new List(); + var regions = await _regionsService.FindAsIQueryable(x => true).ToListAsync(); // 查所有点位 - var points = pointSvc != null - ? await pointSvc.FindAsIQueryable(x => true).ToListAsync() - : new List(); + var points = await _pointService.FindAsIQueryable(x => true).ToListAsync(); // 统计每个点位下的设备数量 + //var deviceCounts = new Dictionary(); + //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(); - 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(); diff --git a/api_sqlsugar/VolPro.WebApi/Controllers/Warehouse/Partial/gateway_nodesController.cs b/api_sqlsugar/VolPro.WebApi/Controllers/Warehouse/Partial/gateway_nodesController.cs index 8bc9355..6496cfc 100644 --- a/api_sqlsugar/VolPro.WebApi/Controllers/Warehouse/Partial/gateway_nodesController.cs +++ b/api_sqlsugar/VolPro.WebApi/Controllers/Warehouse/Partial/gateway_nodesController.cs @@ -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; + } + /// A1: 网关注册(Upsert)。认证方式: NodeToken [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(); + 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) diff --git a/api_sqlsugar/Warehouse/IServices/device_manager/Partial/Ibase_deviceService.cs b/api_sqlsugar/Warehouse/IServices/device_manager/Partial/Ibase_deviceService.cs index 9783000..431bb4d 100644 --- a/api_sqlsugar/Warehouse/IServices/device_manager/Partial/Ibase_deviceService.cs +++ b/api_sqlsugar/Warehouse/IServices/device_manager/Partial/Ibase_deviceService.cs @@ -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> GetDevicesByGatewayNodeAsync(int gatewayNodeId); + + Task UpsertDeviceAsync(SyncDeviceItem d, int gatewayNodeId, Dictionary<(string, string), int> existingIds); + + } } diff --git a/api_sqlsugar/Warehouse/IServices/device_manager/Partial/Igateway_nodesService.cs b/api_sqlsugar/Warehouse/IServices/device_manager/Partial/Igateway_nodesService.cs index b0d6ea9..a38c80b 100644 --- a/api_sqlsugar/Warehouse/IServices/device_manager/Partial/Igateway_nodesService.cs +++ b/api_sqlsugar/Warehouse/IServices/device_manager/Partial/Igateway_nodesService.cs @@ -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 RegisterNodeAsync(string nodeCode, string token, string adapterTypes, string baseUrl); + + Task UpdateHeartbeatAsync(string nodeCode, string token); + + Task<(int added, int updated)> SyncDevicesAsync(int gatewayNodeId, List devices); } } diff --git a/api_sqlsugar/Warehouse/IServices/device_manager/Partial/Iiot_alarmService.cs b/api_sqlsugar/Warehouse/IServices/device_manager/Partial/Iiot_alarmService.cs index c9ae1ee..3943e5e 100644 --- a/api_sqlsugar/Warehouse/IServices/device_manager/Partial/Iiot_alarmService.cs +++ b/api_sqlsugar/Warehouse/IServices/device_manager/Partial/Iiot_alarmService.cs @@ -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); + + } } diff --git a/api_sqlsugar/Warehouse/Services/HeartbeatMonitorJob.cs b/api_sqlsugar/Warehouse/Services/HeartbeatMonitorJob.cs index a836fb9..28e2a08 100644 --- a/api_sqlsugar/Warehouse/Services/HeartbeatMonitorJob.cs +++ b/api_sqlsugar/Warehouse/Services/HeartbeatMonitorJob.cs @@ -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) { diff --git a/api_sqlsugar/Warehouse/Services/device_manager/Partial/base_deviceService.cs b/api_sqlsugar/Warehouse/Services/device_manager/Partial/base_deviceService.cs index 17e2fce..fc7a46b 100644 --- a/api_sqlsugar/Warehouse/Services/device_manager/Partial/base_deviceService.cs +++ b/api_sqlsugar/Warehouse/Services/device_manager/Partial/base_deviceService.cs @@ -48,7 +48,7 @@ namespace Warehouse.Services public async Task> GetDevicesByGatewayNodeAsync(int gatewayNodeId) { return await _repository.DbContext.Queryable() - .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 ? "在线" : "离线", diff --git a/api_sqlsugar/Warehouse/Services/device_manager/Partial/gateway_nodesService.cs b/api_sqlsugar/Warehouse/Services/device_manager/Partial/gateway_nodesService.cs index 6f60bea..4147785 100644 --- a/api_sqlsugar/Warehouse/Services/device_manager/Partial/gateway_nodesService.cs +++ b/api_sqlsugar/Warehouse/Services/device_manager/Partial/gateway_nodesService.cs @@ -113,7 +113,7 @@ namespace Warehouse.Services // 批量查询已有设备映射表(用于 parentSourceId → ParentDeviceId 解析) var adapterCodes = devices.Select(d => d.AdapterCode).Distinct().ToList(); var existingIds = db.Queryable() - .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 ? "在线" : "离线", diff --git a/api_sqlsugar/Warehouse/Services/device_manager/Partial/iot_alarmService.cs b/api_sqlsugar/Warehouse/Services/device_manager/Partial/iot_alarmService.cs index b1b1855..bed494a 100644 --- a/api_sqlsugar/Warehouse/Services/device_manager/Partial/iot_alarmService.cs +++ b/api_sqlsugar/Warehouse/Services/device_manager/Partial/iot_alarmService.cs @@ -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 diff --git a/doc/db_init.sql b/doc/db_init.sql index b116667..584489e 100644 --- a/doc/db_init.sql +++ b/doc/db_init.sql @@ -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 '安装位置',