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

92 lines
3.8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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`
- `RouteService``RequestRegionAsync` (for geofence regions)
- `RouteProcessingService``RequestRegionAsync` (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.