G1-G2: A1-A3自注册+BaseUrl修复+心跳重试+语法规范化+废弃标记
This commit is contained in:
@@ -59,6 +59,7 @@ namespace Warehouse.Services
|
|||||||
/// <param name="d">同步设备条目</param>
|
/// <param name="d">同步设备条目</param>
|
||||||
/// <param name="gatewayNodeId">网关节点ID</param>
|
/// <param name="gatewayNodeId">网关节点ID</param>
|
||||||
/// <param name="existingIds">已有设备映射表 (AdapterCode, SourceId) → DeviceId</param>
|
/// <param name="existingIds">已有设备映射表 (AdapterCode, SourceId) → DeviceId</param>
|
||||||
|
[Obsolete("已迁移至 gateway_nodesService.SyncDevicesAsync")]
|
||||||
public async Task UpsertDeviceAsync(SyncDeviceItem d, int gatewayNodeId, Dictionary<(string, string), int> existingIds)
|
public async Task UpsertDeviceAsync(SyncDeviceItem d, int gatewayNodeId, Dictionary<(string, string), int> existingIds)
|
||||||
{
|
{
|
||||||
var db = _repository.DbContext;
|
var db = _repository.DbContext;
|
||||||
|
|||||||
@@ -49,8 +49,8 @@ namespace Warehouse.Services
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public async Task<gateway_nodes> RegisterNodeAsync(string nodeCode, string token, string adapterTypes, string baseUrl)
|
public async Task<gateway_nodes> RegisterNodeAsync(string nodeCode, string token, string adapterTypes, string baseUrl)
|
||||||
{
|
{
|
||||||
var existing = _repository.DbContext.Queryable<gateway_nodes>()
|
var existing = await _repository.FindAsIQueryable<gateway_nodes>()
|
||||||
.First(x => x.NodeCode == nodeCode);
|
.FirstOrDefaultAsync(x => x.NodeCode == nodeCode);
|
||||||
|
|
||||||
gateway_nodes entity;
|
gateway_nodes entity;
|
||||||
if (existing != null)
|
if (existing != null)
|
||||||
@@ -91,8 +91,8 @@ namespace Warehouse.Services
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public async Task UpdateHeartbeatAsync(string nodeCode, string token)
|
public async Task UpdateHeartbeatAsync(string nodeCode, string token)
|
||||||
{
|
{
|
||||||
var entity = _repository.DbContext.Queryable<gateway_nodes>()
|
var entity = _repository.FindAsIQueryable<gateway_nodes>()
|
||||||
.First(x => x.NodeCode == nodeCode && x.NodeToken == token);
|
.FirstOrDefaultAsync(x => x.NodeCode == nodeCode && x.NodeToken == token);
|
||||||
if (entity == null)
|
if (entity == null)
|
||||||
throw new UnauthorizedAccessException("认证失败:NodeCode 或 Token 无效");
|
throw new UnauthorizedAccessException("认证失败:NodeCode 或 Token 无效");
|
||||||
|
|
||||||
|
|||||||
@@ -92,22 +92,99 @@ Console.WriteLine($"[Gateway] {registry.All.Count} 个适配器已注册: {adapt
|
|||||||
// ── A1: 向 Vol.Pro 注册当前网关节点 ──
|
// ── A1: 向 Vol.Pro 注册当前网关节点 ──
|
||||||
var nodeCode = gwCfg["NodeCode"] ?? "gw-default";
|
var nodeCode = gwCfg["NodeCode"] ?? "gw-default";
|
||||||
var nodeToken = Environment.GetEnvironmentVariable("SECMPS_GATEWAY_TOKEN") ?? gwCfg["NodeToken"] ?? "";
|
var nodeToken = Environment.GetEnvironmentVariable("SECMPS_GATEWAY_TOKEN") ?? gwCfg["NodeToken"] ?? "";
|
||||||
|
var port = app.Urls.FirstOrDefault()?.Split(':').LastOrDefault() ?? "5100";
|
||||||
|
var selfUrl = gwCfg["SelfUrl"] ?? $"http://localhost:{port}";
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var registerReq = new GatewayRegisterRequest
|
var registerReq = new GatewayRegisterRequest
|
||||||
{
|
{
|
||||||
NodeCode = nodeCode, Token = nodeToken,
|
NodeCode = nodeCode, Token = nodeToken,
|
||||||
AdapterTypes = adapterTypes,
|
AdapterTypes = adapterTypes,
|
||||||
BaseUrl = $"http://localhost:{app.Urls.FirstOrDefault()?.Split(':').LastOrDefault() ?? "5100"}"
|
BaseUrl = selfUrl
|
||||||
};
|
};
|
||||||
var registerResult = await clientFactory.RegisterAsync(registerReq);
|
var registerResult = await clientFactory.RegisterAsync(registerReq);
|
||||||
Console.WriteLine($"[Gateway] A1 注册完成: nodeCode={nodeCode}, adapters={adapterTypes}");
|
Console.WriteLine($"[Gateway] A1 注册完成: nodeCode={nodeCode}, adapters={adapterTypes}");
|
||||||
}
|
}
|
||||||
catch (Exception ex) { Console.Error.WriteLine($"[Gateway] A1 注册失败: {ex.Message}"); }
|
catch (Exception ex) { Console.Error.WriteLine($"[Gateway] A1 注册失败: {ex.Message}"); }
|
||||||
|
|
||||||
|
// ── A3: 同步所有适配器设备到 Vol.Pro ──
|
||||||
|
await SyncAllDevicesAsync(nodeCode, nodeToken, selfUrl);
|
||||||
|
Console.WriteLine("[Gateway] A3 设备同步完成");
|
||||||
|
|
||||||
|
// ── A2: 心跳 + 自动重注册 ──
|
||||||
|
var heartbeatInterval = int.TryParse(gwCfg["HeartbeatIntervalSec"], out var hs) ? hs : 15;
|
||||||
|
var failCount = 0; var maxFails = 3;
|
||||||
|
_ = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
using var timer = new PeriodicTimer(TimeSpan.FromSeconds(heartbeatInterval));
|
||||||
|
while (await timer.WaitForNextTickAsync())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await clientFactory.HeartbeatAsync(new GatewayHeartbeatRequest { NodeCode = nodeCode, Token = nodeToken });
|
||||||
|
failCount = 0;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
failCount++;
|
||||||
|
Console.Error.WriteLine($"[Gateway] A2 心跳失败 ({failCount}/{maxFails})");
|
||||||
|
if (failCount >= maxFails)
|
||||||
|
{
|
||||||
|
Console.WriteLine("[Gateway] 心跳连续失败, 尝试重新注册...");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await clientFactory.RegisterAsync(new GatewayRegisterRequest { NodeCode = nodeCode, Token = nodeToken, AdapterTypes = adapterTypes, BaseUrl = selfUrl });
|
||||||
|
await SyncAllDevicesAsync(nodeCode, nodeToken, selfUrl);
|
||||||
|
failCount = 0;
|
||||||
|
Console.WriteLine("[Gateway] 重新注册成功");
|
||||||
|
}
|
||||||
|
catch (Exception re) { Console.Error.WriteLine($"[Gateway] 重新注册失败: {re.Message}"); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Console.WriteLine($"[Gateway] A2 心跳已启动 ({heartbeatInterval}s)");
|
||||||
|
|
||||||
// ═══════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════
|
||||||
// B 组路由(管理端 / Vol.Pro → 网关)
|
// A 组辅助函数
|
||||||
// 所有路由通过适配器编码查找对应适配器,按能力接口分发请求
|
// ═══════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
async Task SyncAllDevicesAsync(string nc, string nt, string baseUrl)
|
||||||
|
{
|
||||||
|
var allDevices = new List<object>();
|
||||||
|
foreach (var adapter in registry.All)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (adapter is IHasFlatDevices flat)
|
||||||
|
{
|
||||||
|
var result = await flat.GetDevicesAsync(1, 1000);
|
||||||
|
foreach (var item in result.Items)
|
||||||
|
allDevices.Add(new { AdapterCode = item.AdapterCode, SourceId = item.SourceId, Name = item.Name, Category = item.Category, Group = item.Group, IsParent = item.IsParent, ParentSourceId = item.ParentSourceId, IsOnline = item.IsOnline, IpAddress = item.IpAddress, Port = item.Port, ExtraDataJson = item.Extra != null ? System.Text.Json.JsonSerializer.Serialize(item.Extra) : null });
|
||||||
|
}
|
||||||
|
else if (adapter is IHasOwnDeviceTree tree)
|
||||||
|
{
|
||||||
|
var nodes = await tree.GetObjectTreeAsync();
|
||||||
|
FlattenTree(allDevices, nodes, adapter.AdapterCode, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
if (allDevices.Any())
|
||||||
|
await clientFactory.SyncDevicesAsync(nc, nt, allDevices);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlattenTree(List<object> devices, List<DeviceTreeNode> nodes, string ac, string? parentSourceId)
|
||||||
|
{
|
||||||
|
foreach (var n in nodes)
|
||||||
|
{
|
||||||
|
devices.Add(new { AdapterCode = ac, SourceId = n.SourceId, Name = n.Name ?? n.SourceId, Category = n.Tag ?? "IoT设备", Group = "IoT设备", IsParent = n.Type == 1, ParentSourceId = parentSourceId, IsOnline = true, IpAddress = (string?)null, Port = (int?)null, ExtraDataJson = n.Option != null ? System.Text.Json.JsonSerializer.Serialize(n.Option) : null });
|
||||||
|
if (n.Children?.Count > 0) FlattenTree(devices, n.Children, ac, n.SourceId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════
|
||||||
|
// B 组路由(管理端/ Vol.Pro → 网关)
|
||||||
// ═══════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
// B1: 健康检查 — 返回所有适配器的健康状态和能力声明
|
// B1: 健康检查 — 返回所有适配器的健康状态和能力声明
|
||||||
@@ -291,50 +368,6 @@ app.MapPost("/api/gateway/devices/sync", async (string adapter) =>
|
|||||||
});
|
});
|
||||||
|
|
||||||
app.Run();
|
app.Run();
|
||||||
|
|
||||||
// ═══════════════════════════════════════════════
|
|
||||||
// 配置 POCO
|
|
||||||
// ═══════════════════════════════════════════════
|
|
||||||
|
|
||||||
/// <summary>Owl 适配器配置项</summary>
|
|
||||||
public class OwlConfig
|
|
||||||
{
|
|
||||||
public string? InstanceName { get; set; }
|
|
||||||
public string BaseUrl { get; set; } = "";
|
|
||||||
public string Username { get; set; } = "admin";
|
|
||||||
public string Password { get; set; } = "admin";
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>MC4.0 适配器配置项</summary>
|
|
||||||
public class Mc4Config
|
|
||||||
{
|
|
||||||
public string? InstanceName { get; set; }
|
|
||||||
public string BaseUrl { get; set; } = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>KMS 适配器配置项</summary>
|
|
||||||
public class KmsConfig
|
|
||||||
{
|
|
||||||
public string? InstanceName { get; set; }
|
|
||||||
public string BaseUrl { get; set; } = "";
|
|
||||||
public string ClientId { get; set; } = "";
|
|
||||||
public string ClientSecret { get; set; } = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
// ═══════════════════════════════════════════════
|
|
||||||
// B 组请求 DTO
|
|
||||||
// ═══════════════════════════════════════════════
|
|
||||||
|
|
||||||
/// <summary>云台控制请求</summary>
|
|
||||||
/// <param name="Direction">方向:up/down/left/right/zoom_in/zoom_out/stop</param>
|
|
||||||
/// <param name="Action">动作类型:continuous 或 stop</param>
|
|
||||||
/// <param name="Speed">速度 0.0-1.0</param>
|
|
||||||
record PtzRequest(string? Direction, string Action, float Speed);
|
|
||||||
|
|
||||||
/// <summary>设备控制请求</summary>
|
|
||||||
/// <param name="DeviceId">目标设备 SourceId</param>
|
|
||||||
/// <param name="PointIndex">点位索引</param>
|
|
||||||
/// <param name="Value">目标值</param>
|
|
||||||
record ControlRequest(string? DeviceId, int PointIndex, double Value);
|
record ControlRequest(string? DeviceId, int PointIndex, double Value);
|
||||||
record BatchRealtimeRequest(List<string>? DeviceIds);
|
record BatchRealtimeRequest(List<string>? DeviceIds);
|
||||||
record GatewayControlRequest(string? DeviceId, string? Command, Dictionary<string, object?>? Parameters);
|
record GatewayControlRequest(string? DeviceId, string? Command, Dictionary<string, object?>? Parameters);
|
||||||
|
|||||||
Reference in New Issue
Block a user