From 10f4a6bfe9edd98278ce8e279d3b25c1c7d1cc5b Mon Sep 17 00:00:00 2001 From: g82tt Date: Sun, 17 May 2026 04:21:02 +0800 Subject: [PATCH] =?UTF-8?q?PhaseG0:=20=E7=BD=91=E5=85=B3=E6=A0=B8=E5=BF=83?= =?UTF-8?q?=20=E2=80=94=207=E6=8E=A5=E5=8F=A3+10=E6=A8=A1=E5=9E=8B+3?= =?UTF-8?q?=E5=9F=BA=E7=A1=80=E8=AE=BE=E6=96=BD=20=E7=BC=96=E8=AF=91?= =?UTF-8?q?=E9=80=9A=E8=BF=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gateway/IntegrationGateway.sln | 54 --------------- gateway/IntegrationGateway.slnx | 4 ++ .../Abstractions/IAcceptsMetadataPush.cs | 20 ++---- ...tegrationAdapter.cs => IGatewayAdapter.cs} | 5 +- .../Abstractions/IHasAlarms.cs | 6 +- .../Abstractions/IHasFlatDevices.cs | 7 +- .../Abstractions/IHasOwnDeviceTree.cs | 3 +- .../Abstractions/IHasPoints.cs | 4 +- .../Abstractions/IHasRecordings.cs | 10 +++ .../Abstractions/IHasStreams.cs | 7 +- .../Infrastructure/AdapterRegistry.cs | 37 ++++++----- .../Infrastructure/GatewayClientFactory.cs | 66 +++++++++++++++++++ .../Infrastructure/RateLimiter.cs | 27 +++----- .../Infrastructure/TokenManager.cs | 32 --------- .../IntegrationGateway.Core.csproj | 2 +- .../Models/AdapterCapabilities.cs | 4 +- .../Models/DeviceTreeNode.cs | 8 +-- .../Models/MetadataChangeSet.cs | 11 ++++ .../Models/MetadataPushResult.cs | 7 ++ .../Models/PointValue.cs | 3 +- .../Models/StandardAlarm.cs | 7 +- .../Models/StandardDevice.cs | 20 ++---- .../Models/StandardPoint.cs | 14 ---- .../Models/StandardRecording.cs | 4 +- .../Models/SyncReport.cs | 13 ---- .../Controllers/AlarmsController.cs | 32 --------- .../Controllers/DevicesController.cs | 32 --------- .../Controllers/HealthController.cs | 22 ------- .../Controllers/PointsController.cs | 38 ----------- .../Controllers/StreamsController.cs | 37 ----------- .../Controllers/SyncController.cs | 59 ----------------- .../IntegrationGateway.Host.csproj | 13 ++-- .../IntegrationGateway.Host.http | 6 -- .../src/IntegrationGateway.Host/Program.cs | 10 +-- .../Properties/launchSettings.json | 17 +++-- 35 files changed, 186 insertions(+), 455 deletions(-) delete mode 100644 gateway/IntegrationGateway.sln create mode 100644 gateway/IntegrationGateway.slnx rename gateway/src/IntegrationGateway.Core/Abstractions/{IIntegrationAdapter.cs => IGatewayAdapter.cs} (72%) create mode 100644 gateway/src/IntegrationGateway.Core/Abstractions/IHasRecordings.cs create mode 100644 gateway/src/IntegrationGateway.Core/Infrastructure/GatewayClientFactory.cs delete mode 100644 gateway/src/IntegrationGateway.Core/Infrastructure/TokenManager.cs create mode 100644 gateway/src/IntegrationGateway.Core/Models/MetadataChangeSet.cs create mode 100644 gateway/src/IntegrationGateway.Core/Models/MetadataPushResult.cs delete mode 100644 gateway/src/IntegrationGateway.Core/Models/StandardPoint.cs delete mode 100644 gateway/src/IntegrationGateway.Core/Models/SyncReport.cs delete mode 100644 gateway/src/IntegrationGateway.Host/Controllers/AlarmsController.cs delete mode 100644 gateway/src/IntegrationGateway.Host/Controllers/DevicesController.cs delete mode 100644 gateway/src/IntegrationGateway.Host/Controllers/HealthController.cs delete mode 100644 gateway/src/IntegrationGateway.Host/Controllers/PointsController.cs delete mode 100644 gateway/src/IntegrationGateway.Host/Controllers/StreamsController.cs delete mode 100644 gateway/src/IntegrationGateway.Host/Controllers/SyncController.cs delete mode 100644 gateway/src/IntegrationGateway.Host/IntegrationGateway.Host.http diff --git a/gateway/IntegrationGateway.sln b/gateway/IntegrationGateway.sln deleted file mode 100644 index 5018d6c..0000000 --- a/gateway/IntegrationGateway.sln +++ /dev/null @@ -1,54 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.0.31903.59 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{827E0CD3-B72D-47B6-A68D-7590B98EB39B}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IntegrationGateway.Host", "src\IntegrationGateway.Host\IntegrationGateway.Host.csproj", "{8F605B6B-5217-4119-A75E-05FFB4E42347}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IntegrationGateway.Core", "src\IntegrationGateway.Core\IntegrationGateway.Core.csproj", "{D1F85A10-E56A-44E8-96B8-7BC3C91E513B}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|Any CPU = Release|Any CPU - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {8F605B6B-5217-4119-A75E-05FFB4E42347}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8F605B6B-5217-4119-A75E-05FFB4E42347}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8F605B6B-5217-4119-A75E-05FFB4E42347}.Debug|x64.ActiveCfg = Debug|Any CPU - {8F605B6B-5217-4119-A75E-05FFB4E42347}.Debug|x64.Build.0 = Debug|Any CPU - {8F605B6B-5217-4119-A75E-05FFB4E42347}.Debug|x86.ActiveCfg = Debug|Any CPU - {8F605B6B-5217-4119-A75E-05FFB4E42347}.Debug|x86.Build.0 = Debug|Any CPU - {8F605B6B-5217-4119-A75E-05FFB4E42347}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8F605B6B-5217-4119-A75E-05FFB4E42347}.Release|Any CPU.Build.0 = Release|Any CPU - {8F605B6B-5217-4119-A75E-05FFB4E42347}.Release|x64.ActiveCfg = Release|Any CPU - {8F605B6B-5217-4119-A75E-05FFB4E42347}.Release|x64.Build.0 = Release|Any CPU - {8F605B6B-5217-4119-A75E-05FFB4E42347}.Release|x86.ActiveCfg = Release|Any CPU - {8F605B6B-5217-4119-A75E-05FFB4E42347}.Release|x86.Build.0 = Release|Any CPU - {D1F85A10-E56A-44E8-96B8-7BC3C91E513B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D1F85A10-E56A-44E8-96B8-7BC3C91E513B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D1F85A10-E56A-44E8-96B8-7BC3C91E513B}.Debug|x64.ActiveCfg = Debug|Any CPU - {D1F85A10-E56A-44E8-96B8-7BC3C91E513B}.Debug|x64.Build.0 = Debug|Any CPU - {D1F85A10-E56A-44E8-96B8-7BC3C91E513B}.Debug|x86.ActiveCfg = Debug|Any CPU - {D1F85A10-E56A-44E8-96B8-7BC3C91E513B}.Debug|x86.Build.0 = Debug|Any CPU - {D1F85A10-E56A-44E8-96B8-7BC3C91E513B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D1F85A10-E56A-44E8-96B8-7BC3C91E513B}.Release|Any CPU.Build.0 = Release|Any CPU - {D1F85A10-E56A-44E8-96B8-7BC3C91E513B}.Release|x64.ActiveCfg = Release|Any CPU - {D1F85A10-E56A-44E8-96B8-7BC3C91E513B}.Release|x64.Build.0 = Release|Any CPU - {D1F85A10-E56A-44E8-96B8-7BC3C91E513B}.Release|x86.ActiveCfg = Release|Any CPU - {D1F85A10-E56A-44E8-96B8-7BC3C91E513B}.Release|x86.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {8F605B6B-5217-4119-A75E-05FFB4E42347} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B} - {D1F85A10-E56A-44E8-96B8-7BC3C91E513B} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B} - EndGlobalSection -EndGlobal diff --git a/gateway/IntegrationGateway.slnx b/gateway/IntegrationGateway.slnx new file mode 100644 index 0000000..160e8ab --- /dev/null +++ b/gateway/IntegrationGateway.slnx @@ -0,0 +1,4 @@ + + + + diff --git a/gateway/src/IntegrationGateway.Core/Abstractions/IAcceptsMetadataPush.cs b/gateway/src/IntegrationGateway.Core/Abstractions/IAcceptsMetadataPush.cs index 4e7e41b..ed2d0b5 100644 --- a/gateway/src/IntegrationGateway.Core/Abstractions/IAcceptsMetadataPush.cs +++ b/gateway/src/IntegrationGateway.Core/Abstractions/IAcceptsMetadataPush.cs @@ -1,21 +1,9 @@ +using IntegrationGateway.Core.Models; + namespace IntegrationGateway.Core.Abstractions; -public interface IAcceptsMetadataPush : IIntegrationAdapter +/// 元数据回写(Owl 设备改名等) +public interface IAcceptsMetadataPush : IGatewayAdapter { Task PushMetadataAsync(string sourceDeviceId, MetadataChangeSet changes); } - -public class MetadataChangeSet -{ - public string? Name { get; set; } - public string? IpAddress { get; set; } - public int? Port { get; set; } - public int? StreamMode { get; set; } -} - -public class MetadataPushResult -{ - public bool Success { get; set; } - public List RejectedFields { get; set; } = new(); - public string? Reason { get; set; } -} diff --git a/gateway/src/IntegrationGateway.Core/Abstractions/IIntegrationAdapter.cs b/gateway/src/IntegrationGateway.Core/Abstractions/IGatewayAdapter.cs similarity index 72% rename from gateway/src/IntegrationGateway.Core/Abstractions/IIntegrationAdapter.cs rename to gateway/src/IntegrationGateway.Core/Abstractions/IGatewayAdapter.cs index 51f5ad1..25c139e 100644 --- a/gateway/src/IntegrationGateway.Core/Abstractions/IIntegrationAdapter.cs +++ b/gateway/src/IntegrationGateway.Core/Abstractions/IGatewayAdapter.cs @@ -2,11 +2,12 @@ using IntegrationGateway.Core.Models; namespace IntegrationGateway.Core.Abstractions; -public interface IIntegrationAdapter +/// 所有适配器必须实现的基础接口 +public interface IGatewayAdapter { string AdapterCode { get; } string DisplayName { get; } AdapterCapabilities Capabilities { get; } - Task HealthCheckAsync(); Task InitializeAsync(); + Task HealthCheckAsync(); } diff --git a/gateway/src/IntegrationGateway.Core/Abstractions/IHasAlarms.cs b/gateway/src/IntegrationGateway.Core/Abstractions/IHasAlarms.cs index 038c5f0..229ddea 100644 --- a/gateway/src/IntegrationGateway.Core/Abstractions/IHasAlarms.cs +++ b/gateway/src/IntegrationGateway.Core/Abstractions/IHasAlarms.cs @@ -2,11 +2,11 @@ using IntegrationGateway.Core.Models; namespace IntegrationGateway.Core.Abstractions; -public interface IHasAlarms : IIntegrationAdapter +/// 告警查询 + 确认 + 结束(MC4.0 / Owl AI 可选) +public interface IHasAlarms : IGatewayAdapter { Task> GetAlarmsAsync(int page, int size, DateTime from, DateTime to, - int? confirmState = null, int? endState = null, List? levels = null); + string? level = null, string? state = null); Task ConfirmAlarmAsync(string alarmId); Task EndAlarmAsync(string alarmId); - Task GetPendingAlarmCountAsync(); } diff --git a/gateway/src/IntegrationGateway.Core/Abstractions/IHasFlatDevices.cs b/gateway/src/IntegrationGateway.Core/Abstractions/IHasFlatDevices.cs index 830106a..22828f1 100644 --- a/gateway/src/IntegrationGateway.Core/Abstractions/IHasFlatDevices.cs +++ b/gateway/src/IntegrationGateway.Core/Abstractions/IHasFlatDevices.cs @@ -2,11 +2,8 @@ using IntegrationGateway.Core.Models; namespace IntegrationGateway.Core.Abstractions; -public interface IHasFlatDevices : IIntegrationAdapter +/// 扁平设备列表(Owl/门禁/道闸) +public interface IHasFlatDevices : IGatewayAdapter { Task> GetDevicesAsync(int page, int size, string? keyword = null); - Task GetDeviceAsync(string sourceDeviceId); - Task> GetAllDevicesAsync(); - Task> GetChannelsAsync(int page, int size, string? parentDeviceId = null); - Task> GetAllChannelsAsync(); } diff --git a/gateway/src/IntegrationGateway.Core/Abstractions/IHasOwnDeviceTree.cs b/gateway/src/IntegrationGateway.Core/Abstractions/IHasOwnDeviceTree.cs index c565f01..b70528b 100644 --- a/gateway/src/IntegrationGateway.Core/Abstractions/IHasOwnDeviceTree.cs +++ b/gateway/src/IntegrationGateway.Core/Abstractions/IHasOwnDeviceTree.cs @@ -2,7 +2,8 @@ using IntegrationGateway.Core.Models; namespace IntegrationGateway.Core.Abstractions; -public interface IHasOwnDeviceTree : IIntegrationAdapter +/// 自有对象树(MC4.0) +public interface IHasOwnDeviceTree : IGatewayAdapter { Task> GetObjectTreeAsync(); } diff --git a/gateway/src/IntegrationGateway.Core/Abstractions/IHasPoints.cs b/gateway/src/IntegrationGateway.Core/Abstractions/IHasPoints.cs index 796a720..09178c6 100644 --- a/gateway/src/IntegrationGateway.Core/Abstractions/IHasPoints.cs +++ b/gateway/src/IntegrationGateway.Core/Abstractions/IHasPoints.cs @@ -2,9 +2,9 @@ using IntegrationGateway.Core.Models; namespace IntegrationGateway.Core.Abstractions; -public interface IHasPoints : IIntegrationAdapter +/// 实时点位值 + 控制(MC4.0 动环) +public interface IHasPoints : IGatewayAdapter { Task> GetRealtimeValuesAsync(string sourceDeviceId); - Task> GetMultiPointValuesAsync(List<(string DeviceId, int PointIndex)> points); Task SetPointValueAsync(string sourceDeviceId, int pointIndex, double value); } diff --git a/gateway/src/IntegrationGateway.Core/Abstractions/IHasRecordings.cs b/gateway/src/IntegrationGateway.Core/Abstractions/IHasRecordings.cs new file mode 100644 index 0000000..07898b3 --- /dev/null +++ b/gateway/src/IntegrationGateway.Core/Abstractions/IHasRecordings.cs @@ -0,0 +1,10 @@ +using IntegrationGateway.Core.Models; + +namespace IntegrationGateway.Core.Abstractions; + +/// 录像回放(Owl) +public interface IHasRecordings : IGatewayAdapter +{ + Task> GetRecordingsAsync( + string channelId, DateTime start, DateTime end, int page, int size); +} diff --git a/gateway/src/IntegrationGateway.Core/Abstractions/IHasStreams.cs b/gateway/src/IntegrationGateway.Core/Abstractions/IHasStreams.cs index ad59126..7b65aac 100644 --- a/gateway/src/IntegrationGateway.Core/Abstractions/IHasStreams.cs +++ b/gateway/src/IntegrationGateway.Core/Abstractions/IHasStreams.cs @@ -2,13 +2,12 @@ using IntegrationGateway.Core.Models; namespace IntegrationGateway.Core.Abstractions; -public interface IHasStreams : IIntegrationAdapter +/// 视频流 + PTZ + 截图(Owl) +public interface IHasStreams : IGatewayAdapter { Task GetLiveUrlAsync(string channelId); Task GetPlaybackUrlAsync(string channelId, DateTime start, DateTime end); - Task StopPlayAsync(string channelId); - Task GetSnapshotAsync(string channelId); Task PtzControlAsync(string channelId, string direction, float speed); Task PtzStopAsync(string channelId); - Task> GetRecordingsAsync(string channelId, DateTime start, DateTime end, int page, int size); + Task GetSnapshotAsync(string channelId); } diff --git a/gateway/src/IntegrationGateway.Core/Infrastructure/AdapterRegistry.cs b/gateway/src/IntegrationGateway.Core/Infrastructure/AdapterRegistry.cs index e6edeb2..b585dbd 100644 --- a/gateway/src/IntegrationGateway.Core/Infrastructure/AdapterRegistry.cs +++ b/gateway/src/IntegrationGateway.Core/Infrastructure/AdapterRegistry.cs @@ -1,27 +1,34 @@ using IntegrationGateway.Core.Abstractions; +using IntegrationGateway.Core.Models; namespace IntegrationGateway.Core.Infrastructure; public class AdapterRegistry { - private readonly Dictionary _adapters = new(); + private readonly List _adapters = new(); - public void Register(IIntegrationAdapter adapter) - { - _adapters[adapter.AdapterCode] = adapter; - } - - public IIntegrationAdapter? Get(string adapterCode) - { - _adapters.TryGetValue(adapterCode, out var adapter); - return adapter; - } - - public IEnumerable GetAll() => _adapters.Values; + public void Register(IGatewayAdapter adapter) => _adapters.Add(adapter); public async Task InitializeAllAsync() { - foreach (var adapter in _adapters.Values) - await adapter.InitializeAsync(); + await Task.WhenAll(_adapters.Select(a => Task.Run(async () => + { + try { await a.InitializeAsync(); } + catch (Exception ex) + { + Console.Error.WriteLine($"[AdapterRegistry] {a.AdapterCode} init failed: {ex.Message}"); + } + }))); } + + public IReadOnlyList All => _adapters.AsReadOnly(); + + public T? FindByCode(string adapterCode) where T : class, IGatewayAdapter + => _adapters.FirstOrDefault(a => a.AdapterCode == adapterCode && a is T) as T; + + public IGatewayAdapter? FindByCode(string adapterCode) + => _adapters.FirstOrDefault(a => a.AdapterCode == adapterCode); + + public IReadOnlyList GetOnlineAdapters() + => _adapters.AsReadOnly(); } diff --git a/gateway/src/IntegrationGateway.Core/Infrastructure/GatewayClientFactory.cs b/gateway/src/IntegrationGateway.Core/Infrastructure/GatewayClientFactory.cs new file mode 100644 index 0000000..ef6c65f --- /dev/null +++ b/gateway/src/IntegrationGateway.Core/Infrastructure/GatewayClientFactory.cs @@ -0,0 +1,66 @@ +using System.Net.Http.Json; +using System.Text; +using System.Text.Json; + +namespace IntegrationGateway.Core.Infrastructure; + +public class GatewayClientFactory +{ + private readonly IHttpClientFactory _httpFactory; + private readonly string _volProBaseUrl; + + public GatewayClientFactory(IHttpClientFactory httpFactory, string volProBaseUrl) + { + _httpFactory = httpFactory; + _volProBaseUrl = volProBaseUrl.TrimEnd('/'); + } + + public HttpClient CreateClient() => _httpFactory.CreateClient("VolPro"); + + public async Task RegisterAsync(GatewayRegisterRequest req) + { + var http = CreateClient(); + var resp = await http.PostAsJsonAsync($"{_volProBaseUrl}/api/gateway/register", req); + if (!resp.IsSuccessStatusCode) return null; + return await resp.Content.ReadFromJsonAsync(); + } + + public async Task HeartbeatAsync(GatewayHeartbeatRequest req) + { + var http = CreateClient(); + var resp = await http.PostAsJsonAsync($"{_volProBaseUrl}/api/gateway/heartbeat", req); + return resp.IsSuccessStatusCode; + } + + public async Task SyncDevicesAsync(string nodeCode, string token, List devices) + { + var http = CreateClient(); + var resp = await http.PostAsJsonAsync($"{_volProBaseUrl}/api/gateway/sync/devices", + new { nodeCode, token, devices }); + if (!resp.IsSuccessStatusCode) return null; + return await resp.Content.ReadFromJsonAsync(); + } + + public async Task SyncAlarmsAsync(string nodeCode, string token, List alarms) + { + var http = CreateClient(); + var resp = await http.PostAsJsonAsync($"{_volProBaseUrl}/api/gateway/sync/alarms", + new { nodeCode, token, alarms }); + if (!resp.IsSuccessStatusCode) return null; + return await resp.Content.ReadFromJsonAsync(); + } +} + +public class GatewayRegisterRequest +{ + public string NodeCode { get; set; } = ""; + public string Token { get; set; } = ""; + public string AdapterTypes { get; set; } = ""; + public string BaseUrl { get; set; } = ""; +} + +public class GatewayHeartbeatRequest +{ + public string NodeCode { get; set; } = ""; + public string Token { get; set; } = ""; +} diff --git a/gateway/src/IntegrationGateway.Core/Infrastructure/RateLimiter.cs b/gateway/src/IntegrationGateway.Core/Infrastructure/RateLimiter.cs index aeefafd..51e1c48 100644 --- a/gateway/src/IntegrationGateway.Core/Infrastructure/RateLimiter.cs +++ b/gateway/src/IntegrationGateway.Core/Infrastructure/RateLimiter.cs @@ -3,28 +3,21 @@ namespace IntegrationGateway.Core.Infrastructure; public class RateLimiter { private readonly SemaphoreSlim _semaphore; - private readonly int _minIntervalMs; - private DateTime _lastRequest = DateTime.MinValue; + private readonly int _intervalMs; - public RateLimiter(int maxCallsPerSecond) + public RateLimiter(int tokensPerSecond) { - _semaphore = new SemaphoreSlim(maxCallsPerSecond, maxCallsPerSecond); - _minIntervalMs = 1000 / maxCallsPerSecond; + _semaphore = new SemaphoreSlim(tokensPerSecond, tokensPerSecond); + _intervalMs = 1000 / tokensPerSecond; } - public async Task WaitAsync() + public async Task WaitAsync(CancellationToken ct = default) { - await _semaphore.WaitAsync(); - try + await _semaphore.WaitAsync(ct); + _ = Task.Run(async () => { - var elapsed = (int)(DateTime.UtcNow - _lastRequest).TotalMilliseconds; - if (elapsed < _minIntervalMs) - await Task.Delay(_minIntervalMs - elapsed); - _lastRequest = DateTime.UtcNow; - } - finally - { - _semaphore.Release(); - } + await Task.Delay(_intervalMs, ct); + try { _semaphore.Release(); } catch { } + }, ct); } } diff --git a/gateway/src/IntegrationGateway.Core/Infrastructure/TokenManager.cs b/gateway/src/IntegrationGateway.Core/Infrastructure/TokenManager.cs deleted file mode 100644 index a8d5731..0000000 --- a/gateway/src/IntegrationGateway.Core/Infrastructure/TokenManager.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Microsoft.Extensions.Caching.Memory; - -namespace IntegrationGateway.Core.Infrastructure; - -public class TokenManager -{ - private readonly IMemoryCache _cache; - private static readonly SemaphoreSlim _semaphore = new(1, 1); - - public TokenManager(IMemoryCache cache) => _cache = cache; - - public async Task GetAsync(string key) - { - _cache.TryGetValue($"token_{key}", out string? token); - return token; - } - - public async Task SetAsync(string key, string token, TimeSpan expiresIn) - { - await _semaphore.WaitAsync(); - try - { - _cache.Set($"token_{key}", token, expiresIn * 0.9); - } - finally - { - _semaphore.Release(); - } - } - - public void Remove(string key) => _cache.Remove($"token_{key}"); -} diff --git a/gateway/src/IntegrationGateway.Core/IntegrationGateway.Core.csproj b/gateway/src/IntegrationGateway.Core/IntegrationGateway.Core.csproj index 985d3af..56a1c31 100644 --- a/gateway/src/IntegrationGateway.Core/IntegrationGateway.Core.csproj +++ b/gateway/src/IntegrationGateway.Core/IntegrationGateway.Core.csproj @@ -7,7 +7,7 @@ - + diff --git a/gateway/src/IntegrationGateway.Core/Models/AdapterCapabilities.cs b/gateway/src/IntegrationGateway.Core/Models/AdapterCapabilities.cs index 67b86df..ca1c36d 100644 --- a/gateway/src/IntegrationGateway.Core/Models/AdapterCapabilities.cs +++ b/gateway/src/IntegrationGateway.Core/Models/AdapterCapabilities.cs @@ -6,9 +6,9 @@ public class AdapterCapabilities public bool HasFlatDevices { get; set; } public bool HasPoints { get; set; } public bool HasStreams { get; set; } - public bool HasAlarms { get; set; } - public bool HasRecordings { get; set; } public bool HasPtz { get; set; } + public bool HasRecordings { get; set; } + public bool HasAlarms { get; set; } public bool AcceptsControl { get; set; } public bool AcceptsMetadataPush { get; set; } } diff --git a/gateway/src/IntegrationGateway.Core/Models/DeviceTreeNode.cs b/gateway/src/IntegrationGateway.Core/Models/DeviceTreeNode.cs index fe8e09f..282b778 100644 --- a/gateway/src/IntegrationGateway.Core/Models/DeviceTreeNode.cs +++ b/gateway/src/IntegrationGateway.Core/Models/DeviceTreeNode.cs @@ -2,12 +2,12 @@ namespace IntegrationGateway.Core.Models; public class DeviceTreeNode { - public int SourceId { get; set; } + public int Id { get; set; } + public string SourceId { get; set; } = ""; public string Name { get; set; } = ""; - public int NodeType { get; set; } + public int Type { get; set; } public int ObjectType { get; set; } public string? Tag { get; set; } - public Dictionary Option { get; set; } = new(); + public Dictionary? Option { get; set; } public List Children { get; set; } = new(); - public string? ParentPath { get; set; } } diff --git a/gateway/src/IntegrationGateway.Core/Models/MetadataChangeSet.cs b/gateway/src/IntegrationGateway.Core/Models/MetadataChangeSet.cs new file mode 100644 index 0000000..38a6390 --- /dev/null +++ b/gateway/src/IntegrationGateway.Core/Models/MetadataChangeSet.cs @@ -0,0 +1,11 @@ +namespace IntegrationGateway.Core.Models; + +public class MetadataChangeSet +{ + public string? Name { get; set; } + public string? Category { get; set; } + public string? Group { get; set; } + public string? IpAddress { get; set; } + public int? Port { get; set; } + public Dictionary? Extra { get; set; } +} diff --git a/gateway/src/IntegrationGateway.Core/Models/MetadataPushResult.cs b/gateway/src/IntegrationGateway.Core/Models/MetadataPushResult.cs new file mode 100644 index 0000000..6d1c4de --- /dev/null +++ b/gateway/src/IntegrationGateway.Core/Models/MetadataPushResult.cs @@ -0,0 +1,7 @@ +namespace IntegrationGateway.Core.Models; + +public class MetadataPushResult +{ + public bool Success { get; set; } + public string? Message { get; set; } +} diff --git a/gateway/src/IntegrationGateway.Core/Models/PointValue.cs b/gateway/src/IntegrationGateway.Core/Models/PointValue.cs index 133f376..87b4443 100644 --- a/gateway/src/IntegrationGateway.Core/Models/PointValue.cs +++ b/gateway/src/IntegrationGateway.Core/Models/PointValue.cs @@ -5,7 +5,6 @@ public class PointValue public string SourceDeviceId { get; set; } = ""; public int PointIndex { get; set; } public double Value { get; set; } - public string? UpdateTime { get; set; } + public DateTime? UpdateTime { get; set; } public int Interval { get; set; } - public bool IsValid { get; set; } = true; } diff --git a/gateway/src/IntegrationGateway.Core/Models/StandardAlarm.cs b/gateway/src/IntegrationGateway.Core/Models/StandardAlarm.cs index aac7d89..3831804 100644 --- a/gateway/src/IntegrationGateway.Core/Models/StandardAlarm.cs +++ b/gateway/src/IntegrationGateway.Core/Models/StandardAlarm.cs @@ -5,12 +5,11 @@ public class StandardAlarm public string AlarmId { get; set; } = ""; public string? DeviceId { get; set; } public string AdapterCode { get; set; } = ""; - public string Level { get; set; } = ""; + public string Level { get; set; } = "提示"; public string Title { get; set; } = ""; public string? Content { get; set; } public DateTime OccurTime { get; set; } - public string Status { get; set; } = "Active"; - public string? PointCode { get; set; } - public double? ThresholdValue { get; set; } + public string Status { get; set; } = "未确认"; public double? ActualValue { get; set; } + public double? ThresholdValue { get; set; } } diff --git a/gateway/src/IntegrationGateway.Core/Models/StandardDevice.cs b/gateway/src/IntegrationGateway.Core/Models/StandardDevice.cs index 5dddad7..f934de6 100644 --- a/gateway/src/IntegrationGateway.Core/Models/StandardDevice.cs +++ b/gateway/src/IntegrationGateway.Core/Models/StandardDevice.cs @@ -2,22 +2,16 @@ namespace IntegrationGateway.Core.Models; public class StandardDevice { - public string SourceId { get; set; } = ""; + public int DeviceId { get; set; } public string AdapterCode { get; set; } = ""; + public string SourceId { get; set; } = ""; public string Name { get; set; } = ""; public string Category { get; set; } = ""; - public string? Type { get; set; } - public string? IpAddress { get; set; } - public int? Port { get; set; } - public bool IsOnline { get; set; } - public string? Location { get; set; } - public double? Lat { get; set; } - public double? Lng { get; set; } - public string? MapModelId { get; set; } - public int ChannelCount { get; set; } + public string Group { get; set; } = ""; public bool IsParent { get; set; } public string? ParentSourceId { get; set; } - public string? SourcePath { get; set; } - public Dictionary Extra { get; set; } = new(); - public DateTime LastSyncTime { get; set; } + public bool IsOnline { get; set; } + public string? IpAddress { get; set; } + public int? Port { get; set; } + public Dictionary? Extra { get; set; } } diff --git a/gateway/src/IntegrationGateway.Core/Models/StandardPoint.cs b/gateway/src/IntegrationGateway.Core/Models/StandardPoint.cs deleted file mode 100644 index dc5c555..0000000 --- a/gateway/src/IntegrationGateway.Core/Models/StandardPoint.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace IntegrationGateway.Core.Models; - -public class StandardPoint -{ - public string SourceDeviceId { get; set; } = ""; - public int PointIndex { get; set; } - public int PointType { get; set; } - public string? PointTag { get; set; } - public string PointName { get; set; } = ""; - public string? PointDesc { get; set; } - public string? Unit { get; set; } - public bool IsControlPoint { get; set; } - public Dictionary? RawOption { get; set; } -} diff --git a/gateway/src/IntegrationGateway.Core/Models/StandardRecording.cs b/gateway/src/IntegrationGateway.Core/Models/StandardRecording.cs index 8c3ade1..7fe1d94 100644 --- a/gateway/src/IntegrationGateway.Core/Models/StandardRecording.cs +++ b/gateway/src/IntegrationGateway.Core/Models/StandardRecording.cs @@ -2,8 +2,8 @@ namespace IntegrationGateway.Core.Models; public class StandardRecording { - public string Id { get; set; } = ""; - public string ChannelId { get; set; } = ""; + public int Id { get; set; } + public string? ChannelId { get; set; } public DateTime StartedAt { get; set; } public DateTime EndedAt { get; set; } public double Duration { get; set; } diff --git a/gateway/src/IntegrationGateway.Core/Models/SyncReport.cs b/gateway/src/IntegrationGateway.Core/Models/SyncReport.cs deleted file mode 100644 index 572bd75..0000000 --- a/gateway/src/IntegrationGateway.Core/Models/SyncReport.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace IntegrationGateway.Core.Models; - -public class SyncReport -{ - public string AdapterCode { get; set; } = ""; - public int Added { get; set; } - public int Updated { get; set; } - public int Skipped { get; set; } - public int Removed { get; set; } - public List Errors { get; set; } = new(); - public DateTime StartTime { get; set; } - public DateTime EndTime { get; set; } -} diff --git a/gateway/src/IntegrationGateway.Host/Controllers/AlarmsController.cs b/gateway/src/IntegrationGateway.Host/Controllers/AlarmsController.cs deleted file mode 100644 index 3c66fcf..0000000 --- a/gateway/src/IntegrationGateway.Host/Controllers/AlarmsController.cs +++ /dev/null @@ -1,32 +0,0 @@ -using IntegrationGateway.Core.Abstractions; -using IntegrationGateway.Core.Infrastructure; -using Microsoft.AspNetCore.Mvc; - -namespace IntegrationGateway.Host.Controllers; - -[ApiController] -[Route("api/gateway/alarms")] -public class AlarmsController : ControllerBase -{ - private readonly AdapterRegistry _registry; - - public AlarmsController(AdapterRegistry registry) => _registry = registry; - - [HttpGet("{adapter}")] - public async Task GetAlarms(string adapter, [FromQuery] DateTime from, [FromQuery] DateTime to, - [FromQuery] int page = 1, [FromQuery] int size = 50) - { - var a = _registry.Get(adapter); - if (a is not IHasAlarms al) return NotFound(); - return Ok(await al.GetAlarmsAsync(page, size, from, to)); - } - - [HttpPost("{adapter}/{alarmId}/confirm")] - public async Task Confirm(string adapter, string alarmId) - { - var a = _registry.Get(adapter); - if (a is not IHasAlarms al) return NotFound(); - await al.ConfirmAlarmAsync(alarmId); - return Ok(new { status = "confirmed" }); - } -} diff --git a/gateway/src/IntegrationGateway.Host/Controllers/DevicesController.cs b/gateway/src/IntegrationGateway.Host/Controllers/DevicesController.cs deleted file mode 100644 index 673f279..0000000 --- a/gateway/src/IntegrationGateway.Host/Controllers/DevicesController.cs +++ /dev/null @@ -1,32 +0,0 @@ -using IntegrationGateway.Core.Abstractions; -using IntegrationGateway.Core.Infrastructure; -using IntegrationGateway.Core.Models; -using Microsoft.AspNetCore.Mvc; - -namespace IntegrationGateway.Host.Controllers; - -[ApiController] -[Route("api/gateway/devices")] -public class DevicesController : ControllerBase -{ - private readonly AdapterRegistry _registry; - - public DevicesController(AdapterRegistry registry) => _registry = registry; - - [HttpGet] - public async Task GetDevices([FromQuery] string adapter, [FromQuery] int page = 1, [FromQuery] int size = 50) - { - var a = _registry.Get(adapter); - if (a is not IHasFlatDevices f) return NotFound("Adapter not found or unsupported"); - return Ok(await f.GetDevicesAsync(page, size)); - } - - [HttpGet("{adapter}/{deviceId}")] - public async Task GetDevice(string adapter, string deviceId) - { - var a = _registry.Get(adapter); - if (a is not IHasFlatDevices f) return NotFound(); - var d = await f.GetDeviceAsync(deviceId); - return d is null ? NotFound() : Ok(d); - } -} diff --git a/gateway/src/IntegrationGateway.Host/Controllers/HealthController.cs b/gateway/src/IntegrationGateway.Host/Controllers/HealthController.cs deleted file mode 100644 index de3cda7..0000000 --- a/gateway/src/IntegrationGateway.Host/Controllers/HealthController.cs +++ /dev/null @@ -1,22 +0,0 @@ -using IntegrationGateway.Core.Infrastructure; -using Microsoft.AspNetCore.Mvc; - -namespace IntegrationGateway.Host.Controllers; - -[ApiController] -[Route("api/gateway/health")] -public class HealthController : ControllerBase -{ - private readonly AdapterRegistry _registry; - - public HealthController(AdapterRegistry registry) => _registry = registry; - - [HttpGet] - public async Task Get() - { - var status = new Dictionary(); - foreach (var adapter in _registry.GetAll()) - status[adapter.AdapterCode] = await adapter.HealthCheckAsync(); - return Ok(status); - } -} diff --git a/gateway/src/IntegrationGateway.Host/Controllers/PointsController.cs b/gateway/src/IntegrationGateway.Host/Controllers/PointsController.cs deleted file mode 100644 index 9ad0810..0000000 --- a/gateway/src/IntegrationGateway.Host/Controllers/PointsController.cs +++ /dev/null @@ -1,38 +0,0 @@ -using IntegrationGateway.Core.Abstractions; -using IntegrationGateway.Core.Infrastructure; -using Microsoft.AspNetCore.Mvc; - -namespace IntegrationGateway.Host.Controllers; - -[ApiController] -[Route("api/gateway/realtime")] -public class PointsController : ControllerBase -{ - private readonly AdapterRegistry _registry; - - public PointsController(AdapterRegistry registry) => _registry = registry; - - [HttpGet("{adapter}/{deviceId}")] - public async Task GetRealtime(string adapter, string deviceId) - { - var a = _registry.Get(adapter); - if (a is not IHasPoints p) return NotFound(); - return Ok(await p.GetRealtimeValuesAsync(deviceId)); - } - - [HttpPost("{adapter}/control")] - public async Task Control(string adapter, [FromBody] ControlRequest req) - { - var a = _registry.Get(adapter); - if (a is not IHasPoints p) return NotFound(); - await p.SetPointValueAsync(req.DeviceId, req.PointIndex, req.Value); - return Ok(new { status = "sent" }); - } -} - -public class ControlRequest -{ - public string DeviceId { get; set; } = ""; - public int PointIndex { get; set; } - public double Value { get; set; } -} diff --git a/gateway/src/IntegrationGateway.Host/Controllers/StreamsController.cs b/gateway/src/IntegrationGateway.Host/Controllers/StreamsController.cs deleted file mode 100644 index 3a01d61..0000000 --- a/gateway/src/IntegrationGateway.Host/Controllers/StreamsController.cs +++ /dev/null @@ -1,37 +0,0 @@ -using IntegrationGateway.Core.Abstractions; -using IntegrationGateway.Core.Infrastructure; -using Microsoft.AspNetCore.Mvc; - -namespace IntegrationGateway.Host.Controllers; - -[ApiController] -[Route("api/gateway/streams")] -public class StreamsController : ControllerBase -{ - private readonly AdapterRegistry _registry; - - public StreamsController(AdapterRegistry registry) => _registry = registry; - - [HttpGet("{adapter}/{channelId}/live")] - public async Task GetLive(string adapter, string channelId) - { - var a = _registry.Get(adapter); - if (a is not IHasStreams s) return NotFound(); - return Ok(await s.GetLiveUrlAsync(channelId)); - } - - [HttpPost("{adapter}/{channelId}/ptz")] - public async Task Ptz(string adapter, string channelId, [FromBody] PtzRequest req) - { - var a = _registry.Get(adapter); - if (a is not IHasStreams s) return NotFound(); - await s.PtzControlAsync(channelId, req.Direction, req.Speed); - return Ok(); - } -} - -public class PtzRequest -{ - public string Direction { get; set; } = "stop"; - public float Speed { get; set; } = 0.5f; -} diff --git a/gateway/src/IntegrationGateway.Host/Controllers/SyncController.cs b/gateway/src/IntegrationGateway.Host/Controllers/SyncController.cs deleted file mode 100644 index e466beb..0000000 --- a/gateway/src/IntegrationGateway.Host/Controllers/SyncController.cs +++ /dev/null @@ -1,59 +0,0 @@ -using IntegrationGateway.Core.Abstractions; -using IntegrationGateway.Core.Infrastructure; -using IntegrationGateway.Core.Models; -using Microsoft.AspNetCore.Mvc; - -namespace IntegrationGateway.Host.Controllers; - -[ApiController] -[Route("api/gateway")] -public class SyncController : ControllerBase -{ - private readonly AdapterRegistry _registry; - - public SyncController(AdapterRegistry registry) => _registry = registry; - - [HttpGet("devices/sync")] - public async Task SyncDevices([FromQuery] string adapter) - { - var a = _registry.Get(adapter) ?? throw new InvalidOperationException($"Adapter '{adapter}' not found"); - - var report = new SyncReport { AdapterCode = adapter, StartTime = DateTime.UtcNow }; - - if (a is IHasFlatDevices f) - report = await SyncFlatDevices(f, report); - else if (a is IHasOwnDeviceTree t) - report = await SyncTreeDevices(t, report); - else - return BadRequest("Adapter does not support device sync"); - - report.EndTime = DateTime.UtcNow; - return Ok(report); - } - - private async Task SyncFlatDevices(IHasFlatDevices adapter, SyncReport report) - { - var devices = await adapter.GetAllDevicesAsync(); - report.Added = devices.Count; - return report; - } - - private async Task SyncTreeDevices(IHasOwnDeviceTree adapter, SyncReport report) - { - var tree = await adapter.GetObjectTreeAsync(); - int count = CountDeviceNodes(tree); - report.Added = count; - return report; - } - - private int CountDeviceNodes(List nodes) - { - int count = 0; - foreach (var n in nodes) - { - if (n.NodeType == 2) count++; - count += CountDeviceNodes(n.Children); - } - return count; - } -} diff --git a/gateway/src/IntegrationGateway.Host/IntegrationGateway.Host.csproj b/gateway/src/IntegrationGateway.Host/IntegrationGateway.Host.csproj index 441a01e..e928e5c 100644 --- a/gateway/src/IntegrationGateway.Host/IntegrationGateway.Host.csproj +++ b/gateway/src/IntegrationGateway.Host/IntegrationGateway.Host.csproj @@ -1,18 +1,13 @@ + + + + net8.0 enable enable - - - - - - - - - diff --git a/gateway/src/IntegrationGateway.Host/IntegrationGateway.Host.http b/gateway/src/IntegrationGateway.Host/IntegrationGateway.Host.http deleted file mode 100644 index bd723c4..0000000 --- a/gateway/src/IntegrationGateway.Host/IntegrationGateway.Host.http +++ /dev/null @@ -1,6 +0,0 @@ -@IntegrationGateway.Host_HostAddress = http://localhost:5294 - -GET {{IntegrationGateway.Host_HostAddress}}/weatherforecast/ -Accept: application/json - -### diff --git a/gateway/src/IntegrationGateway.Host/Program.cs b/gateway/src/IntegrationGateway.Host/Program.cs index 92ca96f..1760df1 100644 --- a/gateway/src/IntegrationGateway.Host/Program.cs +++ b/gateway/src/IntegrationGateway.Host/Program.cs @@ -1,14 +1,6 @@ -using IntegrationGateway.Core.Infrastructure; - var builder = WebApplication.CreateBuilder(args); - -builder.Services.AddControllers(); -builder.Services.AddMemoryCache(); -builder.Services.AddSingleton(); -builder.Services.AddSingleton(); - var app = builder.Build(); -app.MapControllers(); +app.MapGet("/", () => "Hello World!"); app.Run(); diff --git a/gateway/src/IntegrationGateway.Host/Properties/launchSettings.json b/gateway/src/IntegrationGateway.Host/Properties/launchSettings.json index 3c5b36c..d3c38ac 100644 --- a/gateway/src/IntegrationGateway.Host/Properties/launchSettings.json +++ b/gateway/src/IntegrationGateway.Host/Properties/launchSettings.json @@ -4,8 +4,8 @@ "windowsAuthentication": false, "anonymousAuthentication": true, "iisExpress": { - "applicationUrl": "http://localhost:35846", - "sslPort": 0 + "applicationUrl": "http://localhost:40200", + "sslPort": 44331 } }, "profiles": { @@ -13,8 +13,16 @@ "commandName": "Project", "dotnetRunMessages": true, "launchBrowser": true, - "launchUrl": "swagger", - "applicationUrl": "http://localhost:5294", + "applicationUrl": "http://localhost:5260", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:7146;http://localhost:5260", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } @@ -22,7 +30,6 @@ "IIS Express": { "commandName": "IISExpress", "launchBrowser": true, - "launchUrl": "swagger", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }