PhaseG1: Host宿主 — 14路由+配置+错误处理 编译通过
This commit is contained in:
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user