Initial_commit_SecMPS_v2

This commit is contained in:
2026-05-15 23:22:48 +08:00
commit 23ea4fe05f
13830 changed files with 298675 additions and 0 deletions

View File

@@ -0,0 +1,21 @@
namespace IntegrationGateway.Core.Abstractions;
public interface IAcceptsMetadataPush : IIntegrationAdapter
{
Task<MetadataPushResult> 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<string> RejectedFields { get; set; } = new();
public string? Reason { get; set; }
}

View File

@@ -0,0 +1,12 @@
using IntegrationGateway.Core.Models;
namespace IntegrationGateway.Core.Abstractions;
public interface IHasAlarms : IIntegrationAdapter
{
Task<PagedResult<StandardAlarm>> GetAlarmsAsync(int page, int size, DateTime from, DateTime to,
int? confirmState = null, int? endState = null, List<int>? levels = null);
Task ConfirmAlarmAsync(string alarmId);
Task EndAlarmAsync(string alarmId);
Task<int> GetPendingAlarmCountAsync();
}

View File

@@ -0,0 +1,12 @@
using IntegrationGateway.Core.Models;
namespace IntegrationGateway.Core.Abstractions;
public interface IHasFlatDevices : IIntegrationAdapter
{
Task<PagedResult<StandardDevice>> GetDevicesAsync(int page, int size, string? keyword = null);
Task<StandardDevice?> GetDeviceAsync(string sourceDeviceId);
Task<List<StandardDevice>> GetAllDevicesAsync();
Task<PagedResult<StandardDevice>> GetChannelsAsync(int page, int size, string? parentDeviceId = null);
Task<List<StandardDevice>> GetAllChannelsAsync();
}

View File

@@ -0,0 +1,8 @@
using IntegrationGateway.Core.Models;
namespace IntegrationGateway.Core.Abstractions;
public interface IHasOwnDeviceTree : IIntegrationAdapter
{
Task<List<DeviceTreeNode>> GetObjectTreeAsync();
}

View File

@@ -0,0 +1,10 @@
using IntegrationGateway.Core.Models;
namespace IntegrationGateway.Core.Abstractions;
public interface IHasPoints : IIntegrationAdapter
{
Task<List<PointValue>> GetRealtimeValuesAsync(string sourceDeviceId);
Task<List<PointValue>> GetMultiPointValuesAsync(List<(string DeviceId, int PointIndex)> points);
Task SetPointValueAsync(string sourceDeviceId, int pointIndex, double value);
}

View File

@@ -0,0 +1,14 @@
using IntegrationGateway.Core.Models;
namespace IntegrationGateway.Core.Abstractions;
public interface IHasStreams : IIntegrationAdapter
{
Task<StreamUrls> GetLiveUrlAsync(string channelId);
Task<StreamUrls> GetPlaybackUrlAsync(string channelId, DateTime start, DateTime end);
Task StopPlayAsync(string channelId);
Task<StreamUrls> GetSnapshotAsync(string channelId);
Task PtzControlAsync(string channelId, string direction, float speed);
Task PtzStopAsync(string channelId);
Task<PagedResult<StandardRecording>> GetRecordingsAsync(string channelId, DateTime start, DateTime end, int page, int size);
}

View File

@@ -0,0 +1,12 @@
using IntegrationGateway.Core.Models;
namespace IntegrationGateway.Core.Abstractions;
public interface IIntegrationAdapter
{
string AdapterCode { get; }
string DisplayName { get; }
AdapterCapabilities Capabilities { get; }
Task<bool> HealthCheckAsync();
Task InitializeAsync();
}

View File

@@ -0,0 +1,27 @@
using IntegrationGateway.Core.Abstractions;
namespace IntegrationGateway.Core.Infrastructure;
public class AdapterRegistry
{
private readonly Dictionary<string, IIntegrationAdapter> _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<IIntegrationAdapter> GetAll() => _adapters.Values;
public async Task InitializeAllAsync()
{
foreach (var adapter in _adapters.Values)
await adapter.InitializeAsync();
}
}

View File

@@ -0,0 +1,30 @@
namespace IntegrationGateway.Core.Infrastructure;
public class RateLimiter
{
private readonly SemaphoreSlim _semaphore;
private readonly int _minIntervalMs;
private DateTime _lastRequest = DateTime.MinValue;
public RateLimiter(int maxCallsPerSecond)
{
_semaphore = new SemaphoreSlim(maxCallsPerSecond, maxCallsPerSecond);
_minIntervalMs = 1000 / maxCallsPerSecond;
}
public async Task WaitAsync()
{
await _semaphore.WaitAsync();
try
{
var elapsed = (int)(DateTime.UtcNow - _lastRequest).TotalMilliseconds;
if (elapsed < _minIntervalMs)
await Task.Delay(_minIntervalMs - elapsed);
_lastRequest = DateTime.UtcNow;
}
finally
{
_semaphore.Release();
}
}
}

View File

@@ -0,0 +1,32 @@
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<string?> 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}");
}

View File

@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="10.0.8" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,14 @@
namespace IntegrationGateway.Core.Models;
public class AdapterCapabilities
{
public bool HasObjectTree { get; set; }
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 AcceptsControl { get; set; }
public bool AcceptsMetadataPush { get; set; }
}

View File

@@ -0,0 +1,13 @@
namespace IntegrationGateway.Core.Models;
public class DeviceTreeNode
{
public int SourceId { get; set; }
public string Name { get; set; } = "";
public int NodeType { get; set; }
public int ObjectType { get; set; }
public string? Tag { get; set; }
public Dictionary<string, object?> Option { get; set; } = new();
public List<DeviceTreeNode> Children { get; set; } = new();
public string? ParentPath { get; set; }
}

View File

@@ -0,0 +1,7 @@
namespace IntegrationGateway.Core.Models;
public class PagedResult<T>
{
public List<T> Items { get; set; } = new();
public int Total { get; set; }
}

View File

@@ -0,0 +1,11 @@
namespace IntegrationGateway.Core.Models;
public class PointValue
{
public string SourceDeviceId { get; set; } = "";
public int PointIndex { get; set; }
public double Value { get; set; }
public string? UpdateTime { get; set; }
public int Interval { get; set; }
public bool IsValid { get; set; } = true;
}

View File

@@ -0,0 +1,16 @@
namespace IntegrationGateway.Core.Models;
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 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 double? ActualValue { get; set; }
}

View File

@@ -0,0 +1,23 @@
namespace IntegrationGateway.Core.Models;
public class StandardDevice
{
public string SourceId { get; set; } = "";
public string AdapterCode { 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 bool IsParent { get; set; }
public string? ParentSourceId { get; set; }
public string? SourcePath { get; set; }
public Dictionary<string, object?> Extra { get; set; } = new();
public DateTime LastSyncTime { get; set; }
}

View File

@@ -0,0 +1,14 @@
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<string, object?>? RawOption { get; set; }
}

View File

@@ -0,0 +1,12 @@
namespace IntegrationGateway.Core.Models;
public class StandardRecording
{
public string Id { get; set; } = "";
public string ChannelId { get; set; } = "";
public DateTime StartedAt { get; set; }
public DateTime EndedAt { get; set; }
public double Duration { get; set; }
public string? FilePath { get; set; }
public long Size { get; set; }
}

View File

@@ -0,0 +1,11 @@
namespace IntegrationGateway.Core.Models;
public class StreamUrls
{
public string? WsFlv { get; set; }
public string? HttpFlv { get; set; }
public string? Hls { get; set; }
public string? WebRtc { get; set; }
public string? Rtmp { get; set; }
public string? Rtsp { get; set; }
}

View File

@@ -0,0 +1,13 @@
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<string> Errors { get; set; } = new();
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
}