Files
Oleksandr Bezdieniezhnykh 23ab05766d [AZ-370] Refactor C17: status / point-type enums + AC RT2 update
Replaces bare strings with two enums in Common/Enums/:
  RegionStatus { Queued, Processing, Completed, Failed }
  RoutePointType { Start, End, Action, Intermediate }

Adds a Dapper EnumStringTypeHandler<T> (DataAccess/TypeHandlers/)
that round-trips enums to/from lowercase strings, registered once
at startup via DapperEnumTypeHandlers.RegisterAll(). DataAccess now
references Common (project ref) so entities can carry the enum types.

Sites converted: RegionService (5), RouteProcessingService (3),
RoutePointGraphBuilder (4), entity Status/PointType columns. Log
message and summary file format preserved via .ToLowerInvariant().

API JSON contract preserved by adding JsonStringEnumConverter with
JsonNamingPolicy.CamelCase to the http JSON options — single-word
enum members serialize to the same lowercase strings as before.

DTO renamed: Common.DTO.RegionStatus -> RegionStatusResponse to
free the RegionStatus name for the new enum (forced by the task's
explicit enum name); the renamed DTO has no public-API impact at
the JSON wire level. Stale doc references updated.

AC RT2 in _docs/00_problem/acceptance_criteria.md now lists all 4
point types (start/end/action/intermediate).

Tests: 171 / 171 unit + 5 / 5 smoke green (was 141 + 5; +30 new tests
covering type handler round-trip, set/parse, unknown-value rejection,
idempotent registration, and the AC RT2 doc check).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-11 03:55:22 +03:00

3.8 KiB
Raw Permalink Blame History

Module: Services/RegionService + RegionProcessingService + RegionRequestQueue

Purpose

End-to-end region processing pipeline: API request handling → queue → background worker → tile download → output file generation. Three closely coupled classes form the region processing subsystem.

Public Interface

RegionService (implements IRegionService)

  • RequestRegionAsync(...): creates RegionEntity with status RegionStatus.Queued, enqueues a RegionRequest, returns RegionStatusResponse
  • GetRegionStatusAsync(Guid id): reads region record and maps to RegionStatusResponse
  • ProcessRegionAsync(Guid id, CancellationToken): the main processing pipeline — see Internal Logic

RegionProcessingService (BackgroundService)

  • ExecuteAsync(CancellationToken): spawns MaxConcurrentRegions parallel worker tasks, each in an infinite dequeue loop

RegionRequestQueue (implements IRegionRequestQueue)

  • EnqueueAsync(RegionRequest, CancellationToken): writes to a bounded Channel<RegionRequest>
  • DequeueAsync(CancellationToken): reads from the channel (blocks until available)
  • Count: current queue depth

Internal Logic

RegionService.ProcessRegionAsync

  1. Sets region status to "processing"
  2. Creates a 5-minute timeout CancellationTokenSource
  3. Queries existing tiles in the region
  4. Calls TileService.DownloadAndStoreTilesAsync to fetch missing tiles
  5. Counts downloaded vs reused tiles
  6. Generates CSV file (region_{id}_ready.csv) listing tile coordinates + paths
  7. Optionally stitches tiles into a single JPEG image (if StitchTiles is true)
  8. Generates summary file (region_{id}_summary.txt)
  9. Updates region status to "completed"
  10. On any error: sets status to "failed", generates error summary

Tile Stitching

Uses ImageSharp to:

  1. Compute a tile grid from tile coordinates
  2. Create a new image of (gridWidth × 256) × (gridHeight × 256) pixels
  3. Place each tile image at its grid position
  4. Draw a red crosshair at the center coordinates
  5. Save as JPEG

Error Handling

Comprehensive catch blocks for:

  • TaskCanceledException (timeout vs external cancellation)
  • OperationCanceledException
  • RateLimitException (Google rate limiting)
  • HttpRequestException (with status code)
  • Generic Exception Each sets status to "failed" and writes an error summary file.

RegionProcessingService

  • Spawns MaxConcurrentRegions worker tasks with staggered startup (100500ms random delay)
  • Each worker loops: dequeue → ProcessRegionAsync → repeat
  • Graceful shutdown on cancellation

RegionRequestQueue

  • Uses System.Threading.Channels.Channel<T>.CreateBounded with BoundedChannelFullMode.Wait
  • Tracks _totalEnqueued and _totalDequeued counters

Dependencies

  • ITileService, IRegionRepository, IRegionRequestQueue
  • StorageConfig, ProcessingConfig
  • SixLabors.ImageSharp — tile stitching
  • SatelliteProvider.Common.Utils.GeoUtils — coordinate conversion for stitching

Consumers

  • Program.cs API endpoints — RequestRegionAsync, GetRegionStatusAsync
  • RouteServiceRequestRegionAsync (for geofence regions)
  • RouteProcessingServiceRequestRegionAsync (for route-point regions)

Data Models

  • Input: RegionRequest (queue message)
  • Output: RegionStatusResponse (API response), CSV files, summary files, stitched images
  • Persistence: RegionEntity

Configuration

  • StorageConfig.ReadyDirectory — output file location
  • ProcessingConfig.MaxConcurrentRegions — worker count
  • ProcessingConfig.QueueCapacity — bounded channel size

External Integrations

  • PostgreSQL (via repositories)
  • File system (CSV, summary, stitched images in ./ready/)
  • Google Maps (indirectly via TileService → GoogleMapsDownloaderV2)

Security

None.

Tests

Integration tests in RegionTests.cs cover the request → poll → complete flow.