diff --git a/OpenAlprWebhookProcessor/ClientApp/src/app/settings/openalpr-agent/openalpr-agent.component.html b/OpenAlprWebhookProcessor/ClientApp/src/app/settings/openalpr-agent/openalpr-agent.component.html index 2369d67d..e5536902 100644 --- a/OpenAlprWebhookProcessor/ClientApp/src/app/settings/openalpr-agent/openalpr-agent.component.html +++ b/OpenAlprWebhookProcessor/ClientApp/src/app/settings/openalpr-agent/openalpr-agent.component.html @@ -67,5 +67,8 @@ style="cursor:default" matSuffix>help_center - - \ No newline at end of file + +
+ + +
\ No newline at end of file diff --git a/OpenAlprWebhookProcessor/ClientApp/src/app/settings/openalpr-agent/openalpr-agent.component.ts b/OpenAlprWebhookProcessor/ClientApp/src/app/settings/openalpr-agent/openalpr-agent.component.ts index fbbf8cf9..a19aa319 100644 --- a/OpenAlprWebhookProcessor/ClientApp/src/app/settings/openalpr-agent/openalpr-agent.component.ts +++ b/OpenAlprWebhookProcessor/ClientApp/src/app/settings/openalpr-agent/openalpr-agent.component.ts @@ -1,4 +1,6 @@ -import { Component, Input, OnInit } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; +import { SnackbarService } from '@app/snackbar/snackbar.service'; +import { SnackBarType } from '@app/snackbar/snackbartype'; import { SettingsService } from '../settings.service'; import { Agent } from './agent'; @@ -10,8 +12,10 @@ import { Agent } from './agent'; export class OpenalprAgentComponent implements OnInit { public agent: Agent; public isSaving: boolean = false; - - constructor(private settingsService: SettingsService) { } + public isHydrating: boolean = false; + constructor( + private settingsService: SettingsService, + private snackBarService: SnackbarService) { } ngOnInit(): void { this.getAgent(); @@ -26,6 +30,15 @@ export class OpenalprAgentComponent implements OnInit { }); } + public scrapeAgent() { + this.isHydrating = true; + + this.settingsService.startAgentScrape().subscribe(_ => { + this.isHydrating = false; + this.snackBarService.create("Agent Scraping has begun, check system logs for progress", SnackBarType.Info); + }); + } + private getAgent() { this.settingsService.getAgent().subscribe(result => { this.agent = result; diff --git a/OpenAlprWebhookProcessor/ClientApp/src/app/settings/settings.service.ts b/OpenAlprWebhookProcessor/ClientApp/src/app/settings/settings.service.ts index ebef9294..b51e5756 100644 --- a/OpenAlprWebhookProcessor/ClientApp/src/app/settings/settings.service.ts +++ b/OpenAlprWebhookProcessor/ClientApp/src/app/settings/settings.service.ts @@ -33,6 +33,10 @@ export class SettingsService { return this.http.get(`/settings/agent`); } + startAgentScrape(): Observable { + return this.http.post(`/settings/agent/scrape`, null); + } + getIgnores(): Observable { return this.http.get(`/settings/ignores`); } diff --git a/OpenAlprWebhookProcessor/ClientApp/src/app/signalr/signalr.service.ts b/OpenAlprWebhookProcessor/ClientApp/src/app/signalr/signalr.service.ts index 816ded03..d05822c5 100644 --- a/OpenAlprWebhookProcessor/ClientApp/src/app/signalr/signalr.service.ts +++ b/OpenAlprWebhookProcessor/ClientApp/src/app/signalr/signalr.service.ts @@ -69,6 +69,11 @@ export class SignalrService { this.snackbarService.create(`Connection lost`, SnackBarType.Disconnected); this.triggerConnectionStatusChange(false); }); + + this.hubConnection.on('ScrapeFinished', _ => { + console.log("Scrape finished"); + this.snackbarService.create(`Scrape finished!`, SnackBarType.Info); + }); } public stopConnection() { diff --git a/OpenAlprWebhookProcessor/Hydration/HydrationController.cs b/OpenAlprWebhookProcessor/Hydration/HydrationController.cs deleted file mode 100644 index 02122397..00000000 --- a/OpenAlprWebhookProcessor/Hydration/HydrationController.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; - -namespace OpenAlprWebhookProcessor.Hydrator -{ - [Authorize] - [ApiController] - [Route("[controller]")] - public class HydrationController : ControllerBase - { - private readonly HydrationService _hydrationService; - - public HydrationController(HydrationService hydrationService) - { - _hydrationService = hydrationService; - } - - [HttpPost("start")] - public IActionResult StartHydration() - { - _hydrationService.StartHydration(); - - return StatusCode(202); - } - } -} \ No newline at end of file diff --git a/OpenAlprWebhookProcessor/Hydration/HydrationService.cs b/OpenAlprWebhookProcessor/Hydration/HydrationService.cs index 98c305c0..d9d7a0cd 100644 --- a/OpenAlprWebhookProcessor/Hydration/HydrationService.cs +++ b/OpenAlprWebhookProcessor/Hydration/HydrationService.cs @@ -1,41 +1,44 @@ -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using OpenAlprWebhookProcessor.Data; -using OpenAlprWebhookProcessor.Hydrator.OpenAlprSearch; -using System; -using System.Collections.Generic; -using System.Net.Http; -using System.Text.Json; +using Microsoft.Extensions.Hosting; using System.Threading; using System.Threading.Tasks; -using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; -using OpenAlprWebhookProcessor.Utilities; -using System.Net; +using System.Collections.Concurrent; +using OpenAlprWebhookProcessor.WebhookProcessor.OpenAlprAgentScraper; +using System; +using Microsoft.Extensions.DependencyInjection; +using OpenAlprWebhookProcessor.Alerts; +using OpenAlprWebhookProcessor.ProcessorHub; +using Microsoft.AspNetCore.SignalR; namespace OpenAlprWebhookProcessor.Hydrator { public class HydrationService : IHostedService { + private readonly BlockingCollection _hydrationRequestToProcess; + private readonly CancellationTokenSource _cancellationTokenSource; - private Uri _openAlprServerUrl; + private readonly ILogger _logger; - private readonly IServiceScopeFactory _scopeFactory; + private readonly IServiceProvider _serviceProvider; - private readonly ILogger _logger; + private readonly IHubContext _processorHub; public HydrationService( - IServiceScopeFactory scopeFactory, - ILogger logger) + IServiceProvider serviceProvider, + ILogger logger, + IHubContext processorHub) { _cancellationTokenSource = new CancellationTokenSource(); - _scopeFactory = scopeFactory; + _serviceProvider = serviceProvider; _logger = logger; + _processorHub = processorHub; + _hydrationRequestToProcess = new BlockingCollection(); } public Task StartAsync(CancellationToken cancellationToken) { + Task.Run(() => StartHydrationAsync(), cancellationToken); return Task.CompletedTask; } @@ -45,202 +48,26 @@ public Task StopAsync(CancellationToken cancellationToken) return Task.CompletedTask; } - public void StartHydration() + public void StartHydration(string request) { - Task.Run(() => StartHydrationAsync()); + _hydrationRequestToProcess.Add(request); } private async Task StartHydrationAsync() { - var httpClient = new HttpClient(); - - var startDate = new DateTimeOffset(2019, 5, 5, 0, 0, 0, TimeSpan.Zero); - - var stopDate = DateTimeOffset.UtcNow; - - var firstRecordDate = await FindEarliestPlateGroupAsync( - startDate, - httpClient); - - if (firstRecordDate == null) - { - return; - } - - using (var scope = _scopeFactory.CreateScope()) - { - var processorContext = scope.ServiceProvider.GetRequiredService(); - - var agent = await processorContext.Agents.FirstOrDefaultAsync(); - - _openAlprServerUrl = new Uri( - Flurl.Url.Combine( - agent.OpenAlprWebServerUrl.ToString(), - "/api/search/plate", - $"?api_key={agent.OpenAlprWebServerApiKey}")); - } - - try - { - var responses = new List(); - - while (firstRecordDate <= stopDate) - { - var apiResults = await GetOpenAlprPlateGroupsFromApiAsync( - httpClient, - firstRecordDate.Value, - firstRecordDate.Value.AddDays(1)); - - responses.AddRange(apiResults); - - _logger.LogInformation($"pulling plates from: {firstRecordDate.Value:s} to {firstRecordDate.Value.AddDays(1):s}, found {apiResults.Count} plates"); - - firstRecordDate = firstRecordDate.Value.AddDays(1); - } - - var plateGroups = new List(); - - foreach (var response in responses) - { - plateGroups.Add(MapResponseToPlate(response)); - } - - using (var scope = _scopeFactory.CreateScope()) - { - var processorContext = scope.ServiceProvider.GetRequiredService(); - - _logger.LogInformation($"truncating plates table"); - - await processorContext.Database.ExecuteSqlRawAsync("DELETE FROM PlateGroups;"); - - _logger.LogInformation($"populating plates table"); - - processorContext.AddRange(plateGroups); - await processorContext.SaveChangesAsync(); - } - } - catch (Exception ex) - { - _logger.LogError(ex, "Unable to map hydration results"); - throw; - } - } - - private static PlateGroup MapResponseToPlate(Response apiResponse) - { - var fields = apiResponse.Fields; - - var plateGroup = new PlateGroup() - { - Confidence = double.Parse(fields.BestConfidence), - Direction = fields.DirectionOfTravelDegrees, - IsAlert = false, - BestNumber = fields.BestPlate, - OpenAlprCameraId = fields.CameraId, - OpenAlprProcessingTimeMs = double.Parse(fields.ProcessingTimeMs), - OpenAlprUuid = fields.BestUuid, - PlateCoordinates = VehicleUtilities.FormatLicensePlateImageCoordinates( - new List() - { - fields.PlateX1, - fields.PlateX2, - fields.PlateX3, - fields.PlateX4, - }, - new List() - { - fields.PlateY1, - fields.PlateY2, - fields.PlateY3, - fields.PlateY4, - }), - ReceivedOnEpoch = DateTimeOffset.Parse(fields.EpochTimeStart).ToUnixTimeMilliseconds(), - VehicleRegion = fields.Region, - VehicleColor = fields.VehicleColor, - VehicleMake = fields.VehicleMake, - VehicleMakeModel = fields.VehicleMakeModel, - VehicleType = fields.VehicleBodyType, - }; - - return plateGroup; - } - - private async Task> GetOpenAlprPlateGroupsFromApiAsync( - HttpClient httpClient, - DateTimeOffset dateRangeStart, - DateTimeOffset dateRangeEnd) - { - var requestUrl = Flurl.Url.Combine( - _openAlprServerUrl.ToString(), - $"start={dateRangeStart.ToString("s", System.Globalization.CultureInfo.InvariantCulture)}", - $"end={dateRangeEnd.ToString("s", System.Globalization.CultureInfo.InvariantCulture)}"); - - var result = await httpClient.GetAsync(requestUrl); - var response = await result.Content.ReadAsStringAsync(); - - if (!result.IsSuccessStatusCode) + foreach (var _ in _hydrationRequestToProcess.GetConsumingEnumerable(_cancellationTokenSource.Token)) { - _logger.LogError( - "failed to get plate data, request: {0}, response: {1}", - result.RequestMessage, - response); - } - - return JsonSerializer.Deserialize>(response); - } + _logger.LogInformation("Starting OpenALPR Agent scrape."); - private async Task FindEarliestPlateGroupAsync( - DateTimeOffset dateRangeStart, - HttpClient httpClient) - { - var numberOfResults = 0; - var currentRequestDate = dateRangeStart; - - _logger.LogInformation("searching for first license plate"); - - try - { - while (numberOfResults == 0) + using (var scope = _serviceProvider.CreateScope()) { - _logger.LogInformation($"searching from: {currentRequestDate:s} to {currentRequestDate.AddDays(1):s}"); - - var requestUrl = Flurl.Url.Combine( - _openAlprServerUrl.ToString(), - $"start={currentRequestDate.ToString("s", System.Globalization.CultureInfo.InvariantCulture)}", - $"end={currentRequestDate.AddDays(1).ToString("s", System.Globalization.CultureInfo.InvariantCulture)}"); - - var result = await httpClient.GetAsync(requestUrl); - var response = await result.Content.ReadAsStringAsync(); - - if (!result.IsSuccessStatusCode) - { - if(result.StatusCode == HttpStatusCode.Unauthorized) - { - _logger.LogError("unauthorized API call, do you have a commercial account?"); - } - else - { - _logger.LogError( - "failed to get earliest plate date request: {0} response: {1}", - result.RequestMessage, - response); - } - - break; - } - - numberOfResults = JsonSerializer.Deserialize>(response).Count; - - currentRequestDate = currentRequestDate.AddDays(1); + var scraper = scope.ServiceProvider.GetRequiredService(); + await scraper.ScrapeAgentAsync(_cancellationTokenSource.Token); } - return currentRequestDate; - } - catch (Exception ex) - { - _logger.LogError(ex, "failed to get earliest plate date"); + await _processorHub.Clients.All.ScrapeFinished(); - throw; + _logger.LogInformation("Finished OpenALPR Agent scrape."); } } } diff --git a/OpenAlprWebhookProcessor/Hydration/OpenAlprSearch/Fields.cs b/OpenAlprWebhookProcessor/Hydration/OpenAlprSearch/Fields.cs deleted file mode 100644 index e1e99b5c..00000000 --- a/OpenAlprWebhookProcessor/Hydration/OpenAlprSearch/Fields.cs +++ /dev/null @@ -1,132 +0,0 @@ -using System.Text.Json.Serialization; -using static OpenAlprWebhookProcessor.Hydration.StringToIntConverter; - -namespace OpenAlprWebhookProcessor.Hydrator.OpenAlprSearch -{ - public class Fields - { - [JsonPropertyName("agent_uid")] - public string AgentUid { get; set; } - - [JsonPropertyName("best_confidence")] - public string BestConfidence { get; set; } - - [JsonPropertyName("best_plate")] - public string BestPlate { get; set; } - - [JsonPropertyName("best_index")] - public int BestIndex { get; set; } - - [JsonPropertyName("best_uuid")] - public string BestUuid { get; set; } - - [JsonConverter(typeof(IntToStringConverter))] - [JsonPropertyName("camera")] - public string Camera { get; set; } - - [JsonPropertyName("camera_id")] - public int CameraId { get; set; } - - [JsonPropertyName("company")] - public int Company { get; set; } - - [JsonPropertyName("crop_location")] - public int CropLocation { get; set; } - - [JsonPropertyName("direction_of_travel_degrees")] - public double DirectionOfTravelDegrees { get; set; } - - [JsonPropertyName("direction_of_travel_id")] - public int DirectionOfTravelId { get; set; } - - [JsonPropertyName("epoch_time_start")] - public string EpochTimeStart { get; set; } - - [JsonPropertyName("epoch_time_end")] - public string EpochTimeEnd { get; set; } - - [JsonPropertyName("hit_count")] - public int HitCount { get; set; } - - [JsonPropertyName("img_width")] - public int ImgWidth { get; set; } - - [JsonPropertyName("img_height")] - public int ImgHeight { get; set; } - - [JsonPropertyName("plate_x1")] - public int PlateX1 { get; set; } - - [JsonPropertyName("plate_y1")] - public int PlateY1 { get; set; } - - [JsonPropertyName("plate_x2")] - public int PlateX2 { get; set; } - - [JsonPropertyName("plate_y2")] - public int PlateY2 { get; set; } - - [JsonPropertyName("plate_x3")] - public int PlateX3 { get; set; } - - [JsonPropertyName("plate_y3")] - public int PlateY3 { get; set; } - - [JsonPropertyName("plate_x4")] - public int PlateX4 { get; set; } - - [JsonPropertyName("plate_y4")] - public int PlateY4 { get; set; } - - [JsonPropertyName("processing_time_ms")] - public string ProcessingTimeMs { get; set; } - - [JsonPropertyName("region")] - public string Region { get; set; } - - [JsonPropertyName("region_confidence")] - public string RegionConfidence { get; set; } - - [JsonPropertyName("site")] - public string Site { get; set; } - - [JsonPropertyName("site_id")] - public int SiteId { get; set; } - - [JsonPropertyName("vehicle_body_type")] - public string VehicleBodyType { get; set; } - - [JsonPropertyName("vehicle_color")] - public string VehicleColor { get; set; } - - [JsonPropertyName("vehicle_make")] - public string VehicleMake { get; set; } - - [JsonPropertyName("vehicle_make_model")] - public string VehicleMakeModel { get; set; } - - [JsonPropertyName("vehicle_color_confidence")] - public string VehicleColorConfidence { get; set; } - - [JsonPropertyName("vehicle_make_confidence")] - public string VehicleMakeConfidence { get; set; } - - [JsonPropertyName("vehicle_make_model_confidence")] - public string VehicleMakeModelConfidence { get; set; } - - [JsonPropertyName("vehicle_body_type_confidence")] - public string VehicleBodyTypeConfidence { get; set; } - - [JsonPropertyName("vehicle_region_x")] - public int VehicleRegionX { get; set; } - - [JsonPropertyName("vehicle_region_y")] - public int VehicleRegionY { get; set; } - - [JsonPropertyName("vehicle_region_height")] - public int VehicleRegionHeight { get; set; } - - [JsonPropertyName("vehicle_region_width")] - public int VehicleRegionWidth { get; set; } - } -} diff --git a/OpenAlprWebhookProcessor/Hydration/OpenAlprSearch/Response.cs b/OpenAlprWebhookProcessor/Hydration/OpenAlprSearch/Response.cs deleted file mode 100644 index 1c7670a8..00000000 --- a/OpenAlprWebhookProcessor/Hydration/OpenAlprSearch/Response.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Text.Json.Serialization; - -namespace OpenAlprWebhookProcessor.Hydrator.OpenAlprSearch -{ - public class Response - { - [JsonPropertyName("pk")] - public int Pk { get; set; } - - [JsonPropertyName("model")] - public string Model { get; set; } - - [JsonPropertyName("fields")] - public Fields Fields { get; set; } - } -} diff --git a/OpenAlprWebhookProcessor/Hydration/StringToIntConverter.cs b/OpenAlprWebhookProcessor/Hydration/StringToIntConverter.cs deleted file mode 100644 index 6f346bfa..00000000 --- a/OpenAlprWebhookProcessor/Hydration/StringToIntConverter.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace OpenAlprWebhookProcessor.Hydration -{ - public class StringToIntConverter - { - public class IntToStringConverter : JsonConverter - { - public override string Read( - ref Utf8JsonReader reader, - Type typeToConvert, - JsonSerializerOptions options) - { - if (reader.TokenType == JsonTokenType.Number) - { - return reader.GetInt32().ToString(); - } - - return reader.GetString(); - } - - public override void Write( - Utf8JsonWriter writer, - string value, - JsonSerializerOptions options) - { - writer.WriteStringValue(value); - } - } - } -} diff --git a/OpenAlprWebhookProcessor/ProcessorHub/IProcessorHub.cs b/OpenAlprWebhookProcessor/ProcessorHub/IProcessorHub.cs index 778b77e4..76798d7a 100644 --- a/OpenAlprWebhookProcessor/ProcessorHub/IProcessorHub.cs +++ b/OpenAlprWebhookProcessor/ProcessorHub/IProcessorHub.cs @@ -9,5 +9,7 @@ public interface IProcessorHub Task LicensePlateAlerted(string plateNumber); Task ProcessInformationLogged(string log); + + Task ScrapeFinished(); } } diff --git a/OpenAlprWebhookProcessor/Settings/AgentHydration/AgentScrapeRequestHandler.cs b/OpenAlprWebhookProcessor/Settings/AgentHydration/AgentScrapeRequestHandler.cs new file mode 100644 index 00000000..d313b955 --- /dev/null +++ b/OpenAlprWebhookProcessor/Settings/AgentHydration/AgentScrapeRequestHandler.cs @@ -0,0 +1,19 @@ +using OpenAlprWebhookProcessor.Hydrator; + +namespace OpenAlprWebhookProcessor.Settings.AgentHydration +{ + public class AgentScrapeRequestHandler + { + private readonly HydrationService _hydrationService; + + public AgentScrapeRequestHandler(HydrationService hydrationService) + { + _hydrationService = hydrationService; + } + + public void Handle() + { + _hydrationService.StartHydration("hydration"); + } + } +} diff --git a/OpenAlprWebhookProcessor/Settings/GetAgent/GetAgentRequestHandler.cs b/OpenAlprWebhookProcessor/Settings/GetAgent/GetAgentRequestHandler.cs index e1266860..113b6e38 100644 --- a/OpenAlprWebhookProcessor/Settings/GetAgent/GetAgentRequestHandler.cs +++ b/OpenAlprWebhookProcessor/Settings/GetAgent/GetAgentRequestHandler.cs @@ -1,6 +1,5 @@ using Microsoft.EntityFrameworkCore; using OpenAlprWebhookProcessor.Data; -using OpenAlprWebhookProcessor.WebhookProcessor.OpenAlprAgentScraper; using System.Threading; using System.Threading.Tasks; @@ -9,19 +8,13 @@ namespace OpenAlprWebhookProcessor.Settings public class GetAgentRequestHandler { private readonly ProcessorContext _processorContext; - - private readonly OpenAlprAgentScraper _scraper; - public GetAgentRequestHandler( - ProcessorContext processorContext, - OpenAlprAgentScraper scraper) + public GetAgentRequestHandler(ProcessorContext processorContext) { _processorContext = processorContext; - _scraper = scraper; } public async Task HandleAsync(CancellationToken cancellationToken) { - await _scraper.ScrapeAgentAsync(cancellationToken); var agent = await _processorContext.Agents.FirstOrDefaultAsync(cancellationToken); if (agent == null) diff --git a/OpenAlprWebhookProcessor/Settings/SettingsController.cs b/OpenAlprWebhookProcessor/Settings/SettingsController.cs index 0c05f0b8..9c8c6167 100644 --- a/OpenAlprWebhookProcessor/Settings/SettingsController.cs +++ b/OpenAlprWebhookProcessor/Settings/SettingsController.cs @@ -1,5 +1,6 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using OpenAlprWebhookProcessor.Settings.AgentHydration; using OpenAlprWebhookProcessor.Settings.GetIgnores; using OpenAlprWebhookProcessor.Settings.UpdatedCameras; using OpenAlprWebhookProcessor.Settings.UpsertWebhookForwards; @@ -26,13 +27,16 @@ public class SettingsController : ControllerBase private readonly UpsertWebhookForwardsRequestHandler _upsertWebhookForwardsRequestHandler; + private readonly AgentScrapeRequestHandler _agentHydrationRequestHandler; + public SettingsController( GetAgentRequestHandler getAgentRequestHandler, UpsertAgentRequestHandler upsertAgentRequestHandler, GetIgnoresRequestHandler getIgnoresRequestHandler, UpsertIgnoresRequestHandler upsertIgnoresRequestHandler, GetWebhookForwardsRequestHandler getWebhookForwardsRequestHandler, - UpsertWebhookForwardsRequestHandler upsertWebhookForwardsRequestHandler) + UpsertWebhookForwardsRequestHandler upsertWebhookForwardsRequestHandler, + AgentScrapeRequestHandler agentHydrationRequestHandler) { _getAgentRequestHandler = getAgentRequestHandler; _upsertAgentRequestHandler = upsertAgentRequestHandler; @@ -40,6 +44,7 @@ public SettingsController( _upsertIgnoresRequestHandler = upsertIgnoresRequestHandler; _getWebhookForwardsRequestHandler = getWebhookForwardsRequestHandler; _upsertWebhookForwardsRequestHandler = upsertWebhookForwardsRequestHandler; + _agentHydrationRequestHandler = agentHydrationRequestHandler; } [HttpGet("agent")] @@ -54,6 +59,15 @@ public async Task UpsertAgent([FromBody] Agent agent) await _upsertAgentRequestHandler.HandleAsync(agent); } + + [HttpPost("agent/scrape")] + public IActionResult StartScrape() + { + _agentHydrationRequestHandler.Handle(); + + return StatusCode(202); + } + [HttpPost("ignores/add")] public async Task AddIgnore([FromBody] Ignore ignore) { diff --git a/OpenAlprWebhookProcessor/Startup.cs b/OpenAlprWebhookProcessor/Startup.cs index 3e6d4201..aa06979d 100644 --- a/OpenAlprWebhookProcessor/Startup.cs +++ b/OpenAlprWebhookProcessor/Startup.cs @@ -39,6 +39,7 @@ using Microsoft.Net.Http.Headers; using OpenAlprWebhookProcessor.WebhookProcessor.OpenAlprAgentScraper; using OpenAlprWebhookProcessor.LicensePlates.GetPlateFilters; +using OpenAlprWebhookProcessor.Settings.AgentHydration; namespace OpenAlprWebhookProcessor { @@ -161,6 +162,7 @@ public void ConfigureServices(IServiceCollection services) services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); services.AddSingleton(); services.AddSingleton(p => p.GetService());