179 lines
6.9 KiB
C#
179 lines
6.9 KiB
C#
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();
|
|
|
|
// 读取配置
|
|
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);
|