Application Services
The Alert.CA.Frontend.Application project implements business logic using the CQRS pattern with MediatR. Controllers dispatch commands and queries through the mediator pipeline, which routes them to dedicated handlers that coordinate with repositories and services.
CQRS Pattern with MediatR
Commands modify state (e.g., PanTiltCameraCommand, CreateSubscriptionCommand). Queries read state (e.g., GetStaticMetadataByCameraIdsQuery, GetTimelapseForCameraQuery). MediatR handlers are registered automatically via assembly scanning in ConfigureServices.cs:
services.AddMediatR(cfg => {
cfg.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly());
});
Services
CameraControlService
The core service for all camera movement and control operations. Orchestrates PTZ movements, focus, brightness, guard tour toggling, and camera leasing.
Key Operations:
| Method | Description |
|---|---|
PanTiltZoomCamera() | Validates camera access for the user, checks the device is an active RealCam, then sends a MoveCameraTrigger to the Image Acquisition API via HTTP POST |
SetCameraFocus() | Sends a FocusCameraTrigger with manual focus value and auto-focus toggle |
SetCameraBrightness() | Sends a CameraBrightnessTrigger. Reset to default sets brightness to 5000 |
MoveCameraHome() | Sends a home position trigger. Returns lease data from callback |
GuardTourToggle() | Validates device supports autopan (IsAutopanEnabled), then sends a ToggleGuardTourTrigger to the Image Acquisition API |
CloseLease() | Sets the lease end time and calculates actual duration |
LeaseCameraAfterMove() | Called by the callback endpoint. Validates the camera response, patches PTZ/focus/brightness state in Cosmos DB, creates a lease + movement record in SQL Server |
Validation Rules:
- Camera Access: The user's Entra ID is checked against the
UserCamerasSQL table. Users must either haveAllCamerasaccess or have the specific camera in their allowed list (excluding any excluded cameras). - Device Type: Only devices with
CameraType == "RealCam"andIsAlertCaActive == truecan be controlled. - Guard Tour: Additionally requires
IsAutopanEnabled == trueon the device.
Integration with Image Acquisition API:
- All movement triggers are sent as HTTP POST requests to the Image Acquisition API
- Requests include an
x-api-keyheader fromFunctionApiKeyOptions - The Image Acquisition API processes the movement and calls back to
/functionendpoints/leaseCameraAfterMove
Lease & Movement Defaults:
- Default lease duration: 60 seconds
- Default movement expiration: 60 minutes from the movement time
- If the current expiration is already further in the future, it is not shortened
ESRI Sync:
After each movement, the camera's position is synced to ESRI Feature Layers by upserting a CameraLocationEntity with updated pan, tilt, zoom, movement expiration time, and the latest image timestamp.
ClipGenerationService
Generates animated GIFs from sequences of camera images stored in Azure Blob Storage.
Process:
- Receives a list of image metadata (single or panorama)
- Extracts blob URLs and downloads images from Azure Blob Storage
- Uses ImageMagick (
Magick.NET) to create an animated GIF with 100ms frame delay per frame - Uploads the generated GIF back to blob storage at
{cameraId}/clips/{date}/{timestamp}.gif - Returns the GIF bytes to the client
Supports both SingleImageMetadata and PanoImageMetadata inputs.
ValidatorService
Provides environment-based camera visibility validation by wrapping camera repository queries. Enforces rules about which cameras are visible in a given deployment environment.
AlertService
Handles alert message creation including map image upload. Registered from the Alert.CA.Infrastructure package and coordinated with ESRI-based and point-based subscription services.
For the full list of commands and queries, see the Command & Query Catalog.
Data Access
Repositories are provided by the AlertCAInfrastructure NuGet package and registered during startup:
| Repository Type | Storage | Key Repositories |
|---|---|---|
| Cosmos DB | Camera state, leases, users, acquisition devices | ICameraStaticRepository, ICameraOperationalRepository, IAcquisitionDeviceRepository, ICameraLeaseRepository |
| SQL Server | Activity logs, leases, camera access | ICameraSqlRepository |
| Table Storage | Image metadata | ISingleImageMetadataRepository |
| Blob Storage | Camera images | ISingleImageStorageRepository |
| Microsoft Graph | User and group data | Graph service from infrastructure package |
Custom Exceptions
| Exception | When Thrown |
|---|---|
CameraConfigNotFoundException | Camera configuration not found in any data store |
CameraMovementInvalidResponseException | Image Acquisition API returns errors or null position for a home move |
InvalidCameraException | Camera ID doesn't exist or isn't valid |
InvalidClaimsException | Required Entra ID claim (oid, name, preferred_username) is missing from the JWT |
InvalidMovementExtensionException | Movement expiration extension request is invalid |
InvalidTimeRangeException | Start time is after end time |
MissingCameraIdException | Camera ID was not provided in the request |
TimelapseExceedsRangeException | Timelapse request exceeds the 6-hour maximum range |
Validators
TimelapseValidator
Validates timelapse and panorama requests:
- Camera ID must not be empty
startTimemust be beforeendTime- Time range must not exceed 6 hours
CameraValidator
Validates camera-specific business rules for operations.
Utilities
| Utility | Purpose |
|---|---|
CoordinateExtensions | Pan/tilt/zoom coordinate math and conversions |
RequestUtility | Generates overlay text for camera movement triggers |
ImageUtilityMethods | Maps table storage entities to image metadata DTOs |
CameraUtilityMethods | Composes camera metadata from multiple data sources (Cosmos, SQL, Table Storage) |
CodeGenerator | Generates unique identifiers for shared views and other entities |
CommonExtensions | General-purpose extension methods |
ExpressionExtensions | Expression tree utilities for dynamic queries |