Fix-F1: RealtimePollJob实现+A1自注册+B组认证中间件
This commit is contained in:
@@ -1,20 +1,87 @@
|
||||
using Quartz;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using VolPro.Entity.DomainModels;
|
||||
using Warehouse.IRepositories;
|
||||
using Warehouse.IServices;
|
||||
|
||||
namespace VolPro.Warehouse.Services;
|
||||
|
||||
/// <summary>
|
||||
/// 实时数据轮询任务(Phase 2 完善)。
|
||||
/// 定时轮询 MC4.0 IoT 设备实时值 → 更新 iot_devicedata。
|
||||
/// 实时数据轮询任务。
|
||||
/// 定时轮询在线 MC4 IoT 设备的实时值 → 写入 iot_devicedata 表。
|
||||
/// Cron 建议: 每 10 秒 ("0/10 * * * * ?")
|
||||
///
|
||||
/// 设备与网关的关联通过 AdapterCode 前缀匹配(如设备 AdapterCode="MC4:31ku" 匹配网关 AdapterTypes="MC4:31ku")。
|
||||
/// </summary>
|
||||
public class RealtimePollJob : IJob
|
||||
{
|
||||
public Task Execute(IJobExecutionContext context)
|
||||
public async Task Execute(IJobExecutionContext context)
|
||||
{
|
||||
// TODO: Phase 2 — 遍历在线 MC4 网关,轮询实时值写入 iot_devicedata
|
||||
return Task.CompletedTask;
|
||||
var sp = (IServiceProvider)context.JobDetail.JobDataMap["ServiceProvider"];
|
||||
if (sp == null) return;
|
||||
|
||||
var gwSvc = sp.GetService<Igateway_nodesService>();
|
||||
var devRepo = sp.GetService<Ibase_deviceRepository>();
|
||||
var dataRepo = sp.GetService<Iiot_devicedataRepository>();
|
||||
var gatewayClient = sp.GetService<GatewayClient>();
|
||||
if (gwSvc == null || devRepo == null || dataRepo == null || gatewayClient == null) return;
|
||||
|
||||
// 1. 查在线 MC4 网关
|
||||
var onlineNodes = await gwSvc.FindAsIQueryable(x =>
|
||||
x.IsOnline == "在线" && x.AdapterTypes != null && x.AdapterTypes.Contains("MC4")).ToArrayAsync();
|
||||
|
||||
foreach (var node in onlineNodes)
|
||||
{
|
||||
try
|
||||
{
|
||||
var baseUrl = node.BaseUrl;
|
||||
if (string.IsNullOrEmpty(baseUrl)) continue;
|
||||
|
||||
// 2. 解析网关管理的适配器前缀列表
|
||||
var adapterPrefixes = (node.AdapterTypes ?? "")
|
||||
.Split(',', StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(t => t.Trim());
|
||||
|
||||
// 3. 查该网关下在线的 IoT 设备(AdapterCode 前缀匹配)
|
||||
var devices = await devRepo.FindAsIQueryable(d =>
|
||||
d.DeviceGroup == "IoT设备" && d.IsOnline == "在线").ToListAsync();
|
||||
var matchedDevices = devices.Where(d =>
|
||||
adapterPrefixes.Any(p => (d.AdapterCode ?? "").StartsWith(p))).ToList();
|
||||
|
||||
if (!matchedDevices.Any()) continue;
|
||||
|
||||
// 4. 逐设备调网关 B4 获取实时值
|
||||
foreach (var dev in matchedDevices)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await gatewayClient.GetRealtimeAsync(baseUrl, dev.AdapterCode, dev.SourceId);
|
||||
if (result == null) continue;
|
||||
var root = result.RootElement;
|
||||
var points = root.TryGetProperty("pointValues", out var pv) ? pv
|
||||
: root.TryGetProperty("rows", out var r) ? r
|
||||
: root;
|
||||
// 结果可能是 PointValue[] 数组,取第一个点位写入
|
||||
if (points.ValueKind == System.Text.Json.JsonValueKind.Array && points.GetArrayLength() > 0)
|
||||
{
|
||||
var first = points[0];
|
||||
var entry = new iot_devicedata
|
||||
{
|
||||
DeviceId = dev.DeviceId,
|
||||
PointValue = first.TryGetProperty("value", out var v) ? v.GetDecimal() : (decimal?)null,
|
||||
UpdateTime = DateTime.Now,
|
||||
Interval = first.TryGetProperty("interval", out var iv) ? iv.GetInt32() : 10
|
||||
};
|
||||
dataRepo.Add(entry);
|
||||
}
|
||||
}
|
||||
catch { /* 单设备失败不阻塞其他设备 */ }
|
||||
}
|
||||
}
|
||||
catch { /* 单网关失败不阻塞其他网关 */ }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,6 +83,22 @@ var adapterTypes = string.Join(",", registry.All.Select(a => a.AdapterCode));
|
||||
await registry.InitializeAllAsync();
|
||||
Console.WriteLine($"[Gateway] {registry.All.Count} 个适配器已注册: {adapterTypes}");
|
||||
|
||||
// ── A1: 向 Vol.Pro 注册当前网关节点 ──
|
||||
var nodeCode = gwCfg["NodeCode"] ?? "gw-default";
|
||||
var nodeToken = Environment.GetEnvironmentVariable("SECMPS_GATEWAY_TOKEN") ?? gwCfg["NodeToken"] ?? "";
|
||||
try
|
||||
{
|
||||
var registerReq = new GatewayRegisterRequest
|
||||
{
|
||||
NodeCode = nodeCode, Token = nodeToken,
|
||||
AdapterTypes = adapterTypes,
|
||||
BaseUrl = $"http://localhost:{app.Urls.FirstOrDefault()?.Split(':').LastOrDefault() ?? "5100"}"
|
||||
};
|
||||
var registerResult = await clientFactory.RegisterAsync(registerReq);
|
||||
Console.WriteLine($"[Gateway] A1 注册完成: nodeCode={nodeCode}, adapters={adapterTypes}");
|
||||
}
|
||||
catch (Exception ex) { Console.Error.WriteLine($"[Gateway] A1 注册失败: {ex.Message}"); }
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// B 组路由(管理端 / Vol.Pro → 网关)
|
||||
// 所有路由通过适配器编码查找对应适配器,按能力接口分发请求
|
||||
|
||||
@@ -24,7 +24,9 @@
|
||||
"NodeCode": "gw-31ku",
|
||||
"NodeToken": "changeme",
|
||||
"HeartbeatIntervalSec": 15,
|
||||
"AdapterInitTimeoutSec": 30
|
||||
"AdapterInitTimeoutSec": 30,
|
||||
"GatewayKey": null,
|
||||
"_comment_NodeToken": "生产环境由 SECMPS_GATEWAY_TOKEN 环境变量注入"
|
||||
},
|
||||
"KMS": [
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user