PhaseG1: Host宿主 — 14路由+配置+错误处理 编译通过

This commit is contained in:
2026-05-17 04:23:47 +08:00
parent 10f4a6bfe9
commit af469228d6
2 changed files with 189 additions and 3 deletions

View File

@@ -1,6 +1,178 @@
using IntegrationGateway.Core.Abstractions;
using IntegrationGateway.Core.Infrastructure;
using IntegrationGateway.Core.Models;
var builder = WebApplication.CreateBuilder(args);
// 注册 IHttpClientFactory
builder.Services.AddHttpClient("VolPro", c =>
{
c.Timeout = TimeSpan.FromSeconds(30);
c.DefaultRequestHeaders.Add("Accept", "application/json");
}).ConfigurePrimaryHttpMessageHandler(() => new SocketsHttpHandler
{
PooledConnectionLifetime = TimeSpan.FromMinutes(5),
MaxConnectionsPerServer = 10
});
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
// 读取配置
var gwCfg = app.Configuration.GetSection("Gateway");
var owlCfg = app.Configuration.GetSection("Owl");
var mc4Cfg = app.Configuration.GetSection("MC4");
// 创建适配器注册中心
var registry = new AdapterRegistry();
// 创建 VolPro 客户端工厂
var volProUrl = gwCfg["VolProBaseUrl"] ?? "http://localhost:9100";
var httpFactory = app.Services.GetRequiredService<IHttpClientFactory>();
var clientFactory = new GatewayClientFactory(httpFactory, volProUrl);
// 适配器将在 Phase G2/G3 注册,此处预留
Console.WriteLine("[Gateway] AdapterRegistry initialized, awaiting adapters...");
// ═══ B 组路由 ═══
// B1: 健康检查
app.MapGet("/api/gateway/health", async () =>
{
var results = new List<object>();
foreach (var a in registry.All)
{
bool healthy = false;
try { healthy = await a.HealthCheckAsync(); } catch { }
results.Add(new { a.AdapterCode, a.DisplayName, Healthy = healthy, a.Capabilities });
}
return Results.Ok(results);
});
// B2: 设备列表
app.MapGet("/api/gateway/devices", async (string adapter, int page, int size, string? keyword) =>
{
var a = registry.FindByCode<IHasFlatDevices>(adapter);
if (a == null) return Results.NotFound(new { error = "ADAPTER_NOT_FOUND", message = $"Adapter '{adapter}' not found or does not support flat devices" });
return Results.Ok(await a.GetDevicesAsync(page, size, keyword));
});
// B3: 对象树
app.MapGet("/api/gateway/tree", async (string adapter) =>
{
var a = registry.FindByCode<IHasOwnDeviceTree>(adapter);
if (a == null) return Results.NotFound(new { error = "CAPABILITY_NOT_SUPPORTED", message = $"Tree not supported by '{adapter}'" });
return Results.Ok(await a.GetObjectTreeAsync());
});
// B6a: 实时流
app.MapGet("/api/gateway/streams/{adapter}/{deviceId}/live", async (string adapter, string deviceId) =>
{
var a = registry.FindByCode<IHasStreams>(adapter);
if (a == null) return Results.NotFound(new { error = "CAPABILITY_NOT_SUPPORTED", message = $"Streams not supported by '{adapter}'" });
var result = await a.GetLiveUrlAsync(deviceId);
return result.WsFlv == null && result.Hls == null
? Results.Problem("No stream URL returned", statusCode: 502)
: Results.Ok(result);
});
// B6b: 回放
app.MapGet("/api/gateway/streams/{adapter}/{deviceId}/playback", async (string adapter, string deviceId, DateTime start, DateTime end) =>
{
var a = registry.FindByCode<IHasStreams>(adapter);
if (a == null) return Results.NotFound(new { error = "CAPABILITY_NOT_SUPPORTED" });
return Results.Ok(await a.GetPlaybackUrlAsync(deviceId, start, end));
});
// 截图
app.MapPost("/api/gateway/streams/{adapter}/{deviceId}/snapshot", async (string adapter, string deviceId) =>
{
var a = registry.FindByCode<IHasStreams>(adapter);
if (a == null) return Results.NotFound(new { error = "CAPABILITY_NOT_SUPPORTED" });
return Results.Ok(await a.GetSnapshotAsync(deviceId));
});
// B7: PTZ
app.MapPost("/api/gateway/streams/{adapter}/{deviceId}/ptz", async (string adapter, string deviceId, PtzRequest req) =>
{
var a = registry.FindByCode<IHasStreams>(adapter);
if (a == null) return Results.NotFound(new { error = "CAPABILITY_NOT_SUPPORTED" });
if (req.Action == "stop") await a.PtzStopAsync(deviceId);
else await a.PtzControlAsync(deviceId, req.Direction ?? "stop", req.Speed);
return Results.Ok();
});
// B4: 实时点值
app.MapGet("/api/gateway/realtime/{adapter}/{deviceId}", async (string adapter, string deviceId) =>
{
var a = registry.FindByCode<IHasPoints>(adapter);
if (a == null) return Results.NotFound(new { error = "CAPABILITY_NOT_SUPPORTED" });
return Results.Ok(await a.GetRealtimeValuesAsync(deviceId));
});
// B5: 控制
app.MapPost("/api/gateway/realtime/{adapter}/control", async (string adapter, ControlRequest req) =>
{
var a = registry.FindByCode<IHasPoints>(adapter);
if (a == null) return Results.NotFound(new { error = "CAPABILITY_NOT_SUPPORTED" });
await a.SetPointValueAsync(req.DeviceId ?? "", req.PointIndex, req.Value);
return Results.Ok();
});
// B8: 告警查询
app.MapGet("/api/gateway/alarms/{adapter}", async (string adapter, int page, int size, DateTime from, DateTime to, string? level, string? state) =>
{
var a = registry.FindByCode<IHasAlarms>(adapter);
if (a == null) return Results.NotFound(new { error = "CAPABILITY_NOT_SUPPORTED" });
return Results.Ok(await a.GetAlarmsAsync(page, size, from, to, level, state));
});
// B9: 告警确认
app.MapPost("/api/gateway/alarms/{adapter}/{alarmId}/confirm", async (string adapter, string alarmId) =>
{
var a = registry.FindByCode<IHasAlarms>(adapter);
if (a == null) return Results.NotFound(new { error = "CAPABILITY_NOT_SUPPORTED" });
await a.ConfirmAlarmAsync(alarmId);
return Results.Ok();
});
// 告警结束
app.MapPost("/api/gateway/alarms/{adapter}/{alarmId}/end", async (string adapter, string alarmId) =>
{
var a = registry.FindByCode<IHasAlarms>(adapter);
if (a == null) return Results.NotFound(new { error = "CAPABILITY_NOT_SUPPORTED" });
await a.EndAlarmAsync(alarmId);
return Results.Ok();
});
// 录像
app.MapGet("/api/gateway/recordings/{adapter}/{deviceId}", async (string adapter, string deviceId, DateTime start, DateTime end, int page, int size) =>
{
var a = registry.FindByCode<IHasRecordings>(adapter);
if (a == null) return Results.NotFound(new { error = "CAPABILITY_NOT_SUPPORTED" });
return Results.Ok(await a.GetRecordingsAsync(deviceId, start, end, page, size));
});
// B3: 手动同步
app.MapPost("/api/gateway/devices/sync", async (string adapter) =>
{
var a = registry.FindByCode<IGatewayAdapter>(adapter);
if (a == null) return Results.NotFound(new { error = "ADAPTER_NOT_FOUND" });
// 根据适配器能力触发对应同步
if (a is IHasOwnDeviceTree tree)
{
var obj = await tree.GetObjectTreeAsync();
return Results.Ok(new { nodeCount = obj.Count, message = "tree synced" });
}
if (a is IHasFlatDevices flat)
{
var dev = await flat.GetDevicesAsync(1, 1000);
return Results.Ok(new { deviceCount = dev.Total, message = "devices synced" });
}
return Results.Ok(new { message = "no sync needed" });
});
app.Run();
// 请求 DTO
record PtzRequest(string? Direction, string Action, float Speed);
record ControlRequest(string? DeviceId, int PointIndex, double Value);

View File

@@ -5,5 +5,19 @@
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
"Owl": {
"BaseUrl": "http://localhost:15123",
"Username": "admin",
"Password": "your_owl_password"
},
"MC4": {
"BaseUrl": "http://localhost:3000"
},
"Gateway": {
"VolProBaseUrl": "http://localhost:9100",
"NodeCode": "gw-31ku",
"NodeToken": "changeme",
"HeartbeatIntervalSec": 15,
"AdapterInitTimeoutSec": 30
}
}