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