PhaseG0: 网关核心 — 7接口+10模型+3基础设施 编译通过
This commit is contained in:
@@ -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
|
|
||||||
4
gateway/IntegrationGateway.slnx
Normal file
4
gateway/IntegrationGateway.slnx
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<Solution>
|
||||||
|
<Project Path="src/IntegrationGateway.Core/IntegrationGateway.Core.csproj" />
|
||||||
|
<Project Path="src/IntegrationGateway.Host/IntegrationGateway.Host.csproj" />
|
||||||
|
</Solution>
|
||||||
@@ -1,21 +1,9 @@
|
|||||||
|
using IntegrationGateway.Core.Models;
|
||||||
|
|
||||||
namespace IntegrationGateway.Core.Abstractions;
|
namespace IntegrationGateway.Core.Abstractions;
|
||||||
|
|
||||||
public interface IAcceptsMetadataPush : IIntegrationAdapter
|
/// <summary>元数据回写(Owl 设备改名等)</summary>
|
||||||
|
public interface IAcceptsMetadataPush : IGatewayAdapter
|
||||||
{
|
{
|
||||||
Task<MetadataPushResult> PushMetadataAsync(string sourceDeviceId, MetadataChangeSet changes);
|
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; }
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -2,11 +2,12 @@ using IntegrationGateway.Core.Models;
|
|||||||
|
|
||||||
namespace IntegrationGateway.Core.Abstractions;
|
namespace IntegrationGateway.Core.Abstractions;
|
||||||
|
|
||||||
public interface IIntegrationAdapter
|
/// <summary>所有适配器必须实现的基础接口</summary>
|
||||||
|
public interface IGatewayAdapter
|
||||||
{
|
{
|
||||||
string AdapterCode { get; }
|
string AdapterCode { get; }
|
||||||
string DisplayName { get; }
|
string DisplayName { get; }
|
||||||
AdapterCapabilities Capabilities { get; }
|
AdapterCapabilities Capabilities { get; }
|
||||||
Task<bool> HealthCheckAsync();
|
|
||||||
Task InitializeAsync();
|
Task InitializeAsync();
|
||||||
|
Task<bool> HealthCheckAsync();
|
||||||
}
|
}
|
||||||
@@ -2,11 +2,11 @@ using IntegrationGateway.Core.Models;
|
|||||||
|
|
||||||
namespace IntegrationGateway.Core.Abstractions;
|
namespace IntegrationGateway.Core.Abstractions;
|
||||||
|
|
||||||
public interface IHasAlarms : IIntegrationAdapter
|
/// <summary>告警查询 + 确认 + 结束(MC4.0 / Owl AI 可选)</summary>
|
||||||
|
public interface IHasAlarms : IGatewayAdapter
|
||||||
{
|
{
|
||||||
Task<PagedResult<StandardAlarm>> GetAlarmsAsync(int page, int size, DateTime from, DateTime to,
|
Task<PagedResult<StandardAlarm>> GetAlarmsAsync(int page, int size, DateTime from, DateTime to,
|
||||||
int? confirmState = null, int? endState = null, List<int>? levels = null);
|
string? level = null, string? state = null);
|
||||||
Task ConfirmAlarmAsync(string alarmId);
|
Task ConfirmAlarmAsync(string alarmId);
|
||||||
Task EndAlarmAsync(string alarmId);
|
Task EndAlarmAsync(string alarmId);
|
||||||
Task<int> GetPendingAlarmCountAsync();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,8 @@ using IntegrationGateway.Core.Models;
|
|||||||
|
|
||||||
namespace IntegrationGateway.Core.Abstractions;
|
namespace IntegrationGateway.Core.Abstractions;
|
||||||
|
|
||||||
public interface IHasFlatDevices : IIntegrationAdapter
|
/// <summary>扁平设备列表(Owl/门禁/道闸)</summary>
|
||||||
|
public interface IHasFlatDevices : IGatewayAdapter
|
||||||
{
|
{
|
||||||
Task<PagedResult<StandardDevice>> GetDevicesAsync(int page, int size, string? keyword = null);
|
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();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,8 @@ using IntegrationGateway.Core.Models;
|
|||||||
|
|
||||||
namespace IntegrationGateway.Core.Abstractions;
|
namespace IntegrationGateway.Core.Abstractions;
|
||||||
|
|
||||||
public interface IHasOwnDeviceTree : IIntegrationAdapter
|
/// <summary>自有对象树(MC4.0)</summary>
|
||||||
|
public interface IHasOwnDeviceTree : IGatewayAdapter
|
||||||
{
|
{
|
||||||
Task<List<DeviceTreeNode>> GetObjectTreeAsync();
|
Task<List<DeviceTreeNode>> GetObjectTreeAsync();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ using IntegrationGateway.Core.Models;
|
|||||||
|
|
||||||
namespace IntegrationGateway.Core.Abstractions;
|
namespace IntegrationGateway.Core.Abstractions;
|
||||||
|
|
||||||
public interface IHasPoints : IIntegrationAdapter
|
/// <summary>实时点位值 + 控制(MC4.0 动环)</summary>
|
||||||
|
public interface IHasPoints : IGatewayAdapter
|
||||||
{
|
{
|
||||||
Task<List<PointValue>> GetRealtimeValuesAsync(string sourceDeviceId);
|
Task<List<PointValue>> GetRealtimeValuesAsync(string sourceDeviceId);
|
||||||
Task<List<PointValue>> GetMultiPointValuesAsync(List<(string DeviceId, int PointIndex)> points);
|
|
||||||
Task SetPointValueAsync(string sourceDeviceId, int pointIndex, double value);
|
Task SetPointValueAsync(string sourceDeviceId, int pointIndex, double value);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
using IntegrationGateway.Core.Models;
|
||||||
|
|
||||||
|
namespace IntegrationGateway.Core.Abstractions;
|
||||||
|
|
||||||
|
/// <summary>录像回放(Owl)</summary>
|
||||||
|
public interface IHasRecordings : IGatewayAdapter
|
||||||
|
{
|
||||||
|
Task<PagedResult<StandardRecording>> GetRecordingsAsync(
|
||||||
|
string channelId, DateTime start, DateTime end, int page, int size);
|
||||||
|
}
|
||||||
@@ -2,13 +2,12 @@ using IntegrationGateway.Core.Models;
|
|||||||
|
|
||||||
namespace IntegrationGateway.Core.Abstractions;
|
namespace IntegrationGateway.Core.Abstractions;
|
||||||
|
|
||||||
public interface IHasStreams : IIntegrationAdapter
|
/// <summary>视频流 + PTZ + 截图(Owl)</summary>
|
||||||
|
public interface IHasStreams : IGatewayAdapter
|
||||||
{
|
{
|
||||||
Task<StreamUrls> GetLiveUrlAsync(string channelId);
|
Task<StreamUrls> GetLiveUrlAsync(string channelId);
|
||||||
Task<StreamUrls> GetPlaybackUrlAsync(string channelId, DateTime start, DateTime end);
|
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 PtzControlAsync(string channelId, string direction, float speed);
|
||||||
Task PtzStopAsync(string channelId);
|
Task PtzStopAsync(string channelId);
|
||||||
Task<PagedResult<StandardRecording>> GetRecordingsAsync(string channelId, DateTime start, DateTime end, int page, int size);
|
Task<StreamUrls> GetSnapshotAsync(string channelId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,27 +1,34 @@
|
|||||||
using IntegrationGateway.Core.Abstractions;
|
using IntegrationGateway.Core.Abstractions;
|
||||||
|
using IntegrationGateway.Core.Models;
|
||||||
|
|
||||||
namespace IntegrationGateway.Core.Infrastructure;
|
namespace IntegrationGateway.Core.Infrastructure;
|
||||||
|
|
||||||
public class AdapterRegistry
|
public class AdapterRegistry
|
||||||
{
|
{
|
||||||
private readonly Dictionary<string, IIntegrationAdapter> _adapters = new();
|
private readonly List<IGatewayAdapter> _adapters = new();
|
||||||
|
|
||||||
public void Register(IIntegrationAdapter adapter)
|
public void Register(IGatewayAdapter adapter) => _adapters.Add(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()
|
public async Task InitializeAllAsync()
|
||||||
{
|
{
|
||||||
foreach (var adapter in _adapters.Values)
|
await Task.WhenAll(_adapters.Select(a => Task.Run(async () =>
|
||||||
await adapter.InitializeAsync();
|
{
|
||||||
|
try { await a.InitializeAsync(); }
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine($"[AdapterRegistry] {a.AdapterCode} init failed: {ex.Message}");
|
||||||
}
|
}
|
||||||
|
})));
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyList<IGatewayAdapter> All => _adapters.AsReadOnly();
|
||||||
|
|
||||||
|
public T? FindByCode<T>(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<IGatewayAdapter> GetOnlineAdapters()
|
||||||
|
=> _adapters.AsReadOnly();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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<JsonDocument?> 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<JsonDocument>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> HeartbeatAsync(GatewayHeartbeatRequest req)
|
||||||
|
{
|
||||||
|
var http = CreateClient();
|
||||||
|
var resp = await http.PostAsJsonAsync($"{_volProBaseUrl}/api/gateway/heartbeat", req);
|
||||||
|
return resp.IsSuccessStatusCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<JsonDocument?> SyncDevicesAsync(string nodeCode, string token, List<object> 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<JsonDocument>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<JsonDocument?> SyncAlarmsAsync(string nodeCode, string token, List<object> 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<JsonDocument>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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; } = "";
|
||||||
|
}
|
||||||
@@ -3,28 +3,21 @@ namespace IntegrationGateway.Core.Infrastructure;
|
|||||||
public class RateLimiter
|
public class RateLimiter
|
||||||
{
|
{
|
||||||
private readonly SemaphoreSlim _semaphore;
|
private readonly SemaphoreSlim _semaphore;
|
||||||
private readonly int _minIntervalMs;
|
private readonly int _intervalMs;
|
||||||
private DateTime _lastRequest = DateTime.MinValue;
|
|
||||||
|
|
||||||
public RateLimiter(int maxCallsPerSecond)
|
public RateLimiter(int tokensPerSecond)
|
||||||
{
|
{
|
||||||
_semaphore = new SemaphoreSlim(maxCallsPerSecond, maxCallsPerSecond);
|
_semaphore = new SemaphoreSlim(tokensPerSecond, tokensPerSecond);
|
||||||
_minIntervalMs = 1000 / maxCallsPerSecond;
|
_intervalMs = 1000 / tokensPerSecond;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task WaitAsync()
|
public async Task WaitAsync(CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
await _semaphore.WaitAsync();
|
await _semaphore.WaitAsync(ct);
|
||||||
try
|
_ = Task.Run(async () =>
|
||||||
{
|
{
|
||||||
var elapsed = (int)(DateTime.UtcNow - _lastRequest).TotalMilliseconds;
|
await Task.Delay(_intervalMs, ct);
|
||||||
if (elapsed < _minIntervalMs)
|
try { _semaphore.Release(); } catch { }
|
||||||
await Task.Delay(_minIntervalMs - elapsed);
|
}, ct);
|
||||||
_lastRequest = DateTime.UtcNow;
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
_semaphore.Release();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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<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}");
|
|
||||||
}
|
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="10.0.8" />
|
<PackageReference Include="Microsoft.Extensions.Http" Version="10.0.8" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ public class AdapterCapabilities
|
|||||||
public bool HasFlatDevices { get; set; }
|
public bool HasFlatDevices { get; set; }
|
||||||
public bool HasPoints { get; set; }
|
public bool HasPoints { get; set; }
|
||||||
public bool HasStreams { get; set; }
|
public bool HasStreams { get; set; }
|
||||||
public bool HasAlarms { get; set; }
|
|
||||||
public bool HasRecordings { get; set; }
|
|
||||||
public bool HasPtz { get; set; }
|
public bool HasPtz { get; set; }
|
||||||
|
public bool HasRecordings { get; set; }
|
||||||
|
public bool HasAlarms { get; set; }
|
||||||
public bool AcceptsControl { get; set; }
|
public bool AcceptsControl { get; set; }
|
||||||
public bool AcceptsMetadataPush { get; set; }
|
public bool AcceptsMetadataPush { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,12 +2,12 @@ namespace IntegrationGateway.Core.Models;
|
|||||||
|
|
||||||
public class DeviceTreeNode
|
public class DeviceTreeNode
|
||||||
{
|
{
|
||||||
public int SourceId { get; set; }
|
public int Id { get; set; }
|
||||||
|
public string SourceId { get; set; } = "";
|
||||||
public string Name { get; set; } = "";
|
public string Name { get; set; } = "";
|
||||||
public int NodeType { get; set; }
|
public int Type { get; set; }
|
||||||
public int ObjectType { get; set; }
|
public int ObjectType { get; set; }
|
||||||
public string? Tag { get; set; }
|
public string? Tag { get; set; }
|
||||||
public Dictionary<string, object?> Option { get; set; } = new();
|
public Dictionary<string, object?>? Option { get; set; }
|
||||||
public List<DeviceTreeNode> Children { get; set; } = new();
|
public List<DeviceTreeNode> Children { get; set; } = new();
|
||||||
public string? ParentPath { get; set; }
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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<string, object?>? Extra { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
namespace IntegrationGateway.Core.Models;
|
||||||
|
|
||||||
|
public class MetadataPushResult
|
||||||
|
{
|
||||||
|
public bool Success { get; set; }
|
||||||
|
public string? Message { get; set; }
|
||||||
|
}
|
||||||
@@ -5,7 +5,6 @@ public class PointValue
|
|||||||
public string SourceDeviceId { get; set; } = "";
|
public string SourceDeviceId { get; set; } = "";
|
||||||
public int PointIndex { get; set; }
|
public int PointIndex { get; set; }
|
||||||
public double Value { get; set; }
|
public double Value { get; set; }
|
||||||
public string? UpdateTime { get; set; }
|
public DateTime? UpdateTime { get; set; }
|
||||||
public int Interval { get; set; }
|
public int Interval { get; set; }
|
||||||
public bool IsValid { get; set; } = true;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,12 +5,11 @@ public class StandardAlarm
|
|||||||
public string AlarmId { get; set; } = "";
|
public string AlarmId { get; set; } = "";
|
||||||
public string? DeviceId { get; set; }
|
public string? DeviceId { get; set; }
|
||||||
public string AdapterCode { get; set; } = "";
|
public string AdapterCode { get; set; } = "";
|
||||||
public string Level { get; set; } = "";
|
public string Level { get; set; } = "提示";
|
||||||
public string Title { get; set; } = "";
|
public string Title { get; set; } = "";
|
||||||
public string? Content { get; set; }
|
public string? Content { get; set; }
|
||||||
public DateTime OccurTime { get; set; }
|
public DateTime OccurTime { get; set; }
|
||||||
public string Status { get; set; } = "Active";
|
public string Status { get; set; } = "未确认";
|
||||||
public string? PointCode { get; set; }
|
|
||||||
public double? ThresholdValue { get; set; }
|
|
||||||
public double? ActualValue { get; set; }
|
public double? ActualValue { get; set; }
|
||||||
|
public double? ThresholdValue { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,22 +2,16 @@ namespace IntegrationGateway.Core.Models;
|
|||||||
|
|
||||||
public class StandardDevice
|
public class StandardDevice
|
||||||
{
|
{
|
||||||
public string SourceId { get; set; } = "";
|
public int DeviceId { get; set; }
|
||||||
public string AdapterCode { get; set; } = "";
|
public string AdapterCode { get; set; } = "";
|
||||||
|
public string SourceId { get; set; } = "";
|
||||||
public string Name { get; set; } = "";
|
public string Name { get; set; } = "";
|
||||||
public string Category { get; set; } = "";
|
public string Category { get; set; } = "";
|
||||||
public string? Type { get; set; }
|
public string Group { 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 bool IsParent { get; set; }
|
||||||
public string? ParentSourceId { get; set; }
|
public string? ParentSourceId { get; set; }
|
||||||
public string? SourcePath { get; set; }
|
public bool IsOnline { get; set; }
|
||||||
public Dictionary<string, object?> Extra { get; set; } = new();
|
public string? IpAddress { get; set; }
|
||||||
public DateTime LastSyncTime { get; set; }
|
public int? Port { get; set; }
|
||||||
|
public Dictionary<string, object?>? Extra { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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<string, object?>? RawOption { get; set; }
|
|
||||||
}
|
|
||||||
@@ -2,8 +2,8 @@ namespace IntegrationGateway.Core.Models;
|
|||||||
|
|
||||||
public class StandardRecording
|
public class StandardRecording
|
||||||
{
|
{
|
||||||
public string Id { get; set; } = "";
|
public int Id { get; set; }
|
||||||
public string ChannelId { get; set; } = "";
|
public string? ChannelId { get; set; }
|
||||||
public DateTime StartedAt { get; set; }
|
public DateTime StartedAt { get; set; }
|
||||||
public DateTime EndedAt { get; set; }
|
public DateTime EndedAt { get; set; }
|
||||||
public double Duration { get; set; }
|
public double Duration { get; set; }
|
||||||
|
|||||||
@@ -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<string> Errors { get; set; } = new();
|
|
||||||
public DateTime StartTime { get; set; }
|
|
||||||
public DateTime EndTime { get; set; }
|
|
||||||
}
|
|
||||||
@@ -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<IActionResult> 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<IActionResult> 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" });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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<IActionResult> 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<IActionResult> 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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<IActionResult> Get()
|
|
||||||
{
|
|
||||||
var status = new Dictionary<string, bool>();
|
|
||||||
foreach (var adapter in _registry.GetAll())
|
|
||||||
status[adapter.AdapterCode] = await adapter.HealthCheckAsync();
|
|
||||||
return Ok(status);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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<IActionResult> 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<IActionResult> 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; }
|
|
||||||
}
|
|
||||||
@@ -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<IActionResult> 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<IActionResult> 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;
|
|
||||||
}
|
|
||||||
@@ -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<IActionResult> 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<SyncReport> SyncFlatDevices(IHasFlatDevices adapter, SyncReport report)
|
|
||||||
{
|
|
||||||
var devices = await adapter.GetAllDevicesAsync();
|
|
||||||
report.Added = devices.Count;
|
|
||||||
return report;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<SyncReport> SyncTreeDevices(IHasOwnDeviceTree adapter, SyncReport report)
|
|
||||||
{
|
|
||||||
var tree = await adapter.GetObjectTreeAsync();
|
|
||||||
int count = CountDeviceNodes(tree);
|
|
||||||
report.Added = count;
|
|
||||||
return report;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int CountDeviceNodes(List<DeviceTreeNode> nodes)
|
|
||||||
{
|
|
||||||
int count = 0;
|
|
||||||
foreach (var n in nodes)
|
|
||||||
{
|
|
||||||
if (n.NodeType == 2) count++;
|
|
||||||
count += CountDeviceNodes(n.Children);
|
|
||||||
}
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,18 +1,13 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\IntegrationGateway.Core\IntegrationGateway.Core.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.27" />
|
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\IntegrationGateway.Core\IntegrationGateway.Core.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
@IntegrationGateway.Host_HostAddress = http://localhost:5294
|
|
||||||
|
|
||||||
GET {{IntegrationGateway.Host_HostAddress}}/weatherforecast/
|
|
||||||
Accept: application/json
|
|
||||||
|
|
||||||
###
|
|
||||||
@@ -1,14 +1,6 @@
|
|||||||
using IntegrationGateway.Core.Infrastructure;
|
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
builder.Services.AddControllers();
|
|
||||||
builder.Services.AddMemoryCache();
|
|
||||||
builder.Services.AddSingleton<AdapterRegistry>();
|
|
||||||
builder.Services.AddSingleton<TokenManager>();
|
|
||||||
|
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
|
||||||
app.MapControllers();
|
app.MapGet("/", () => "Hello World!");
|
||||||
|
|
||||||
app.Run();
|
app.Run();
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
"windowsAuthentication": false,
|
"windowsAuthentication": false,
|
||||||
"anonymousAuthentication": true,
|
"anonymousAuthentication": true,
|
||||||
"iisExpress": {
|
"iisExpress": {
|
||||||
"applicationUrl": "http://localhost:35846",
|
"applicationUrl": "http://localhost:40200",
|
||||||
"sslPort": 0
|
"sslPort": 44331
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"profiles": {
|
"profiles": {
|
||||||
@@ -13,8 +13,16 @@
|
|||||||
"commandName": "Project",
|
"commandName": "Project",
|
||||||
"dotnetRunMessages": true,
|
"dotnetRunMessages": true,
|
||||||
"launchBrowser": true,
|
"launchBrowser": true,
|
||||||
"launchUrl": "swagger",
|
"applicationUrl": "http://localhost:5260",
|
||||||
"applicationUrl": "http://localhost:5294",
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"https": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"dotnetRunMessages": true,
|
||||||
|
"launchBrowser": true,
|
||||||
|
"applicationUrl": "https://localhost:7146;http://localhost:5260",
|
||||||
"environmentVariables": {
|
"environmentVariables": {
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
}
|
}
|
||||||
@@ -22,7 +30,6 @@
|
|||||||
"IIS Express": {
|
"IIS Express": {
|
||||||
"commandName": "IISExpress",
|
"commandName": "IISExpress",
|
||||||
"launchBrowser": true,
|
"launchBrowser": true,
|
||||||
"launchUrl": "swagger",
|
|
||||||
"environmentVariables": {
|
"environmentVariables": {
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user