mirror of
https://github.com/azaion/ui.git
synced 2026-06-21 09:51:11 +00:00
[AZ-447] autodev Steps 1-4 baseline: docs, tests, refactor specs
Captures the full output of autodev existing-code Phase A through Step 4 (Code Testability Revision) for the Azaion UI workspace: - Step 1 Document: _docs/02_document/ (FINAL_report, architecture, glossary, components/, modules/, diagrams/, system-flows, module-layout) plus _docs/00_problem/ + _docs/01_solution/ + _docs/legacy/ + _docs/how_to_test + README. - Step 2 Architecture Baseline: architecture_compliance_baseline.md. - Step 3 Test Spec: _docs/02_document/tests/ (environment, test-data, blackbox/performance/resilience/security/ resource-limit tests, traceability-matrix), enum_spec_snapshot, expected_results/results_report.md (98 rows), plus the run-tests.sh + run-performance-tests.sh runners. - Step 4 Code Testability Revision: 01-testability-refactoring/ run dir (list-of-changes C01-C07, deferred_to_refactor, analysis/research_findings + refactoring_roadmap) and the 7 child task specs AZ-448..AZ-454 under _docs/02_tasks/todo/ plus _dependencies_table.md. - _docs/_autodev_state.md pins the cursor at Step 4 / refactor Phase 4 entry so /autodev resumes cleanly. Epic AZ-447 (UI testability gates) tracks the 7 child tasks that will land in subsequent commits. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,439 @@
|
||||
# Legacy: WPF Era of Azaion (`annotations` predecessor)
|
||||
|
||||
> **Source of truth for this doc:** `suite/annotations-research/` — a clone of
|
||||
> `suite/annotations` checked out at commit `22529c2 "Revert add MediaFile"`
|
||||
> (Mon Nov 17 2025), which is the LAST commit before the **big refactoring**
|
||||
> (`e7ea5a8`) that started decoupling the WPF UI from the backend in
|
||||
> preparation for the WPF→.NET API conversion (`9e7dc29` /
|
||||
> `fbbe556 refactor .net project to API`).
|
||||
>
|
||||
> This document captures the system as it existed when the **annotation tool,
|
||||
> the inference engine, the loader, and the UI were a single Windows desktop
|
||||
> application** with .NET WPF in front and Cython sidecar processes behind it,
|
||||
> all running on one machine.
|
||||
>
|
||||
> The current `azaion/ui` repo is the **React rewrite of the front-end half**
|
||||
> of that legacy stack. The Cython parts and the .NET service code became
|
||||
> separate suite submodules (`detections/`, `loader/`, `annotations/` (now
|
||||
> .NET API only), `flights/`, etc.). This file exists so future maintainers
|
||||
> can understand where features in the React UI come from, why some shapes
|
||||
> in the data look the way they do, and what is intentionally NOT being
|
||||
> ported.
|
||||
|
||||
---
|
||||
|
||||
## 1. Top-level layout (`Azaion.Suite.sln`)
|
||||
|
||||
The legacy repository was a single Visual Studio solution containing eight
|
||||
.NET projects plus two Cython projects:
|
||||
|
||||
| # | Project | Type | Role |
|
||||
|---|----------------------|---------------------|--------------------------------------------------------------|
|
||||
| 1 | `Azaion.Suite` | .NET 8 WPF (exe) | Application host. DI container, config, module registry, key handler. |
|
||||
| 2 | `Azaion.LoaderUI` | .NET 8 WPF (exe) | Login screen. Launches `Azaion.Suite.exe` with encrypted creds. |
|
||||
| 3 | `Azaion.Annotator` | .NET 8 WPF (lib) | Main annotation window: video/image canvas, bounding boxes, AI detect. |
|
||||
| 4 | `Azaion.Dataset` | .NET 8 WPF (lib) | Dataset Explorer window: thumbnail grid, class distribution, validation. |
|
||||
| 5 | `Azaion.Common` | .NET 8 (lib) | **Tangled core**: WPF user controls + LinqToDB models + RabbitMQ + HTTP + DTOs all in one assembly. |
|
||||
| 6 | `Azaion.CommonSecurity` | .NET 8 (lib) | AES helpers. Credentials persisted to disk encrypted. |
|
||||
| 7 | `Azaion.Test` | .NET 8 test project | Unit tests for utilities (intervals, throttle, parallel, tile processing). |
|
||||
| 8 | `Dummy` | placeholder dir | empty. |
|
||||
| C1 | `Azaion.Inference` | Cython (Python) | YOLO inference (ONNX / TensorRT). Separate process, ZeroMQ link to .NET. |
|
||||
| C2 | `Azaion.Loader` | Cython (Python) | Encrypted resource fetcher + hardware fingerprinting. Separate process, ZeroMQ link to .NET. |
|
||||
|
||||
There was no internet-facing API. Everything ran on **one Windows machine**
|
||||
(operator laptop / OrangePi / Jetson).
|
||||
|
||||
```
|
||||
+----------------------------- one Windows host -----------------------------+
|
||||
| |
|
||||
| Azaion.LoaderUI.exe |
|
||||
| | |
|
||||
| | encrypts creds -> spawns Azaion.Suite.exe -c <encrypted> |
|
||||
| v |
|
||||
| Azaion.Suite.exe (WPF) |
|
||||
| | |
|
||||
| | Microsoft.Extensions.Hosting + DI |
|
||||
| | - registers Azaion.Annotator, Azaion.Dataset windows |
|
||||
| | - registers Annotation/Gallery/Inference/GpsMatcher Services |
|
||||
| | - registers AzaionApi (HttpClient -> remote installer/aux APIs) |
|
||||
| | - registers LoaderClient + InferenceClient + GpsMatcherClient |
|
||||
| | (all are ZeroMQ DealerSocket clients) |
|
||||
| | |
|
||||
| |--- LinqToDB --------------> SQLite file (annotations.db) |
|
||||
| |--- ZeroMQ Dealer ---------> Azaion.Loader.exe (Cython) |
|
||||
| | | |
|
||||
| | +---> downloads encrypted resources |
|
||||
| | from remote API |
|
||||
| | |
|
||||
| |--- ZeroMQ Dealer ---------> Azaion.Inference.exe (Cython) |
|
||||
| | | |
|
||||
| | +---> ONNX / TensorRT inference |
|
||||
| | |
|
||||
| +--- RabbitMQ.Stream client -> remote RabbitMQ (annotation sync) |
|
||||
| |
|
||||
+----------------------------------------------------------------------------+
|
||||
```
|
||||
|
||||
## 2. Boot sequence
|
||||
|
||||
1. User runs `Azaion.LoaderUI.exe` (the launcher / login window).
|
||||
2. `Login.LoginClick` → calls `IAzaionApi.Login` (HTTP) for installer-version
|
||||
check, then spawns the **external** `Azaion.Loader` Cython process and
|
||||
talks to it over ZeroMQ (`tcp://127.0.0.1:<port>`):
|
||||
- `CommandType.Login` → loader stores credentials and a hardware-derived
|
||||
key (`hardware_service.pyx`).
|
||||
- `CommandType.CheckResource` → loader verifies it can decrypt the cached
|
||||
encrypted resource bundle.
|
||||
3. `Login` AES-encrypts `ApiCredentials` (`Azaion.CommonSecurity`) and starts
|
||||
`Azaion.Suite.exe -c <encrypted>` then closes itself.
|
||||
4. `Azaion.Suite.App.Start(creds)`:
|
||||
- Builds a Serilog logger.
|
||||
- Builds `IConfiguration` from three JSON streams: a local
|
||||
`config.json`, plus `config.system.json` and `config.secured.json`
|
||||
fetched from disk via `LoaderClient.LoadFile(...)` (the Cython loader
|
||||
decrypts them on the fly).
|
||||
- Configures the DI container (`Microsoft.Extensions.Hosting`):
|
||||
- `IConfigUpdater`, `Annotator`, `DatasetExplorer`, `HelpWindow`,
|
||||
`MainSuite`
|
||||
- `IDbFactory`, `IAnnotationService`, `FailsafeAnnotationsProducer`,
|
||||
`IGalleryService`
|
||||
- `IInferenceClient`/`IInferenceService` (ZMQ → Cython inference)
|
||||
- `IGpsMatcherClient`/`IGpsMatcherService` (ZMQ → GPS matcher service)
|
||||
- `ISatelliteDownloader`
|
||||
- `IAzaionApi` (HTTP client to remote API for installer + assets)
|
||||
- `IAzaionModule` registrations (`AnnotatorModule`, `DatasetExplorerModule`)
|
||||
- MediatR with assemblies from Annotator, DatasetExplorer, Common.
|
||||
- Calls `Annotation.Init(directoriesConfig, detectionClassesDict)` —
|
||||
populates **static** state on the `Annotation` entity so that LinqToDB
|
||||
hydrated rows know how to compute `ImagePath` / `LabelPath` / `ThumbPath`
|
||||
/ `Colors` / `ClassName`. (This static coupling is exactly what the
|
||||
`e7ea5a8` "big refactoring" set out to remove.)
|
||||
- Hooks a global preview-key handler that publishes a MediatR `KeyEvent`
|
||||
(with throttle) for any keyboard input that is not in a `TextBox`.
|
||||
- Shows `MainSuite` (the module switcher window).
|
||||
|
||||
## 3. Module system
|
||||
|
||||
`Azaion.Suite.MainSuite` is a small chrome window with a left-hand
|
||||
`ListView` of modules. Each module implements:
|
||||
|
||||
```csharp
|
||||
public interface IAzaionModule
|
||||
{
|
||||
string Name { get; } // localized display name
|
||||
string SvgIcon { get; } // inline SVG markup
|
||||
Type MainWindowType { get; } // WPF Window subclass
|
||||
WindowEnum WindowEnum { get; } // identifier
|
||||
}
|
||||
```
|
||||
|
||||
Two implementations existed at this commit:
|
||||
|
||||
- `AnnotatorModule` → `Azaion.Annotator.Annotator`
|
||||
- `DatasetExplorerModule` → `Azaion.Dataset.DatasetExplorer`
|
||||
|
||||
`MainSuite` shows the icon, opens the corresponding `Window` from the DI
|
||||
container, and tracks open windows in a `Dictionary<WindowEnum, Window>` so
|
||||
clicking the same module twice activates instead of recreating.
|
||||
|
||||
The `IAzaionModule` extension point is the seed of what became, in the
|
||||
post-refactor world, the **left-hand top-level navigation in the React SPA**:
|
||||
Flights, Annotations, Dataset, Admin, Settings.
|
||||
|
||||
## 4. The Annotator window (`Azaion.Annotator.Annotator`)
|
||||
|
||||
The annotation surface (`Annotator.xaml.cs`, ~600 lines) is the heaviest
|
||||
part of the legacy app. It owned, all in one window:
|
||||
|
||||
- **Video/image playback**: `LibVLCSharp` `MediaPlayer` for video, image
|
||||
decoding for stills.
|
||||
- **Canvas editor**: a custom WPF `Canvas` with `CanvasEditor` from
|
||||
`Azaion.Common.Controls` for click-and-drag bounding boxes, 8-handle
|
||||
resize, multi-select with Ctrl, zoom with Ctrl+wheel, pan with Ctrl+drag,
|
||||
crosshair cursor with active-class hint.
|
||||
- **Time-windowed annotation overlay** during video playback: an
|
||||
`IntervalTree<TimeSpan, Annotation>` keyed by
|
||||
`[Time - 50ms, Time + 150ms]`; on each VLC position update, all
|
||||
overlapping intervals render and the rest clear.
|
||||
- **Detection class strip** (`Azaion.Common.Controls.DetectionClasses`):
|
||||
data grid of class colour + number + name, with PhotoMode switcher
|
||||
(Regular=0, Winter=20, Night=40); class number pressed via keyboard (1–9);
|
||||
class colour mixed into the bounding-box label.
|
||||
- **Annotation list** (right sidebar): `DataGrid` over the in-process
|
||||
`IntervalTree`, gradient-coloured by detection class, double-click seeks
|
||||
the video and zooms.
|
||||
- **Frame-by-frame controls**: 1, 5, 10, 30, 60-frame stepping computed
|
||||
from the video's FPS; play/pause/stop; mute and volume.
|
||||
- **AI Detect** (`R` key or button): spawns the Cython inference process via
|
||||
`IInferenceClient` (ZMQ) and streams progress into a modal
|
||||
`AutodetectDialog`.
|
||||
- **Camera config side panel** (`Azaion.Common.Controls.CameraConfigControl`):
|
||||
altitude / focal length / sensor width — used to compute GSD-based bounds
|
||||
for valid detection sizes.
|
||||
- **GPS panel** (`Azaion.Annotator.Controls.MapMatcher`): toggleable below
|
||||
the canvas; ties into `IGpsMatcherClient` which talks to a separate
|
||||
GPS-denied positioning Cython process.
|
||||
- **Help window** (`HelpWindow.xaml` + `HelpTexts.cs`): annotation quality
|
||||
guidelines.
|
||||
- **Ukrainian / English localisation** via `translations.json`.
|
||||
|
||||
Everything in this list is owned by the same `Annotator` partial class. There
|
||||
is no view-model boundary; XAML code-behind directly:
|
||||
|
||||
- queries `IDbFactory` for `AnnotationsDb`, then runs LinqToDB queries
|
||||
against the `Annotation`, `Detection`, `MediaFile`, `AnnotationQueueRecord`
|
||||
tables;
|
||||
- mutates the canvas, the data grid, the VLC media player, and the GPS panel;
|
||||
- publishes MediatR notifications (`AnnotationCreatedEvent`,
|
||||
`AnnotationsDeletedEvent`, `KeyEvent`, `SetStatusTextEvent`,
|
||||
`AnnotatorControlEvent`, `LoadErrorEvent`) which downstream services like
|
||||
`AnnotationService.OnAnnotationCreated` react to (e.g. to enqueue a sync
|
||||
message into the local SQLite buffer table for later RabbitMQ publish).
|
||||
|
||||
This is the central tangle. The same class talks to the database, the
|
||||
network (RabbitMQ via mediator), the inference process, the file system,
|
||||
and the WPF visual tree.
|
||||
|
||||
## 5. The Dataset Explorer window (`Azaion.Dataset.DatasetExplorer`)
|
||||
|
||||
Mirrors the annotator, but for browsing:
|
||||
|
||||
- Thumbnail grid (virtualised) keyed by `Annotation.ThumbPath`, regenerated
|
||||
by `IGalleryService`.
|
||||
- Filter bar: date range, flight, status (`AnnotationStatus`: None / Created
|
||||
/ Edited / Validated).
|
||||
- Class distribution chart (`Controls/ClassDistribution.xaml`): horizontal
|
||||
bars, one per `DetectionClass`, coloured with the class colour.
|
||||
- Inline editor tab — same `CanvasEditor` from `Azaion.Common.Controls`,
|
||||
reused.
|
||||
- Bulk validation: select multiple thumbnails, press `V`, status becomes
|
||||
`Validated`.
|
||||
- Local keyboard handlers: `1–9` (class), `Enter` (save), `Del` (delete
|
||||
selected), `X` (delete all), `V` (validate), arrow keys + PageUp/PageDown
|
||||
for navigation, `Esc` to close the editor.
|
||||
|
||||
## 6. `Azaion.Common`: the everything-bag
|
||||
|
||||
This is the assembly that the post-refactor split tries hardest to undo. At
|
||||
commit `22529c2` it was a single .NET project containing:
|
||||
|
||||
| Folder | Concern |
|
||||
|---------------|------------------------------------------------------------------------------------------------------------------------|
|
||||
| `Controls/` | **WPF user controls**: `CanvasEditor`, `NumericUpDown`, `DetectionClasses`, `CameraConfigControl`, `DetectionLabelPanel`, `UpdatableProgressBar`, `DetectionControl`. |
|
||||
| `Database/` | LinqToDB models + `AnnotationsDb : DataConnection` + `DbFactory` + `SchemaMigrator` + `AnnotationsDbSchemaHolder`. |
|
||||
| `DTO/` | App config sections (`AppConfig`, `LoaderClientConfig`, `InferenceClientConfig`, `GpsDeniedConfig`, `MapConfig`, `QueueConfig`, `AIRecognitionConfig`, `ThumbnailConfig`, `UIConfig`, `AnnotationConfig`, `CameraConfig`, `DirectoriesConfig`), domain enums (`AffiliationEnum`, `RoleEnum`, `WindowEnum`, `Direction`, `PlaybackControlEnum`), and shared shapes (`ApiCredentials`, `BusinessExceptionDto`, `LoginResponse`, `RemoteCommand`, `User`, `DetectionClass`, `LabelInfo`, `AnnotationResult`, `AnnotationThumbnail`, `ClusterDistribution`, `Coordinates`, `SatTile`, `DownloadTilesResult`, `SelectionState`, `FormState`, `ExternalClientsConfig`). |
|
||||
| `Events/` | MediatR notifications (`AnnotationCreatedEvent`, `AnnotationsDeletedEvent`, `KeyEvent`, `SetStatusTextEvent`, `AnnotatorControlEvent`, `LoadErrorEvent`). |
|
||||
| `Exceptions/` | `BusinessException`. |
|
||||
| `Extensions/` | Helpers — `Geo`, `ParallelExt`, `ResilienceExt`, `ThrottleExtensions`, `IntervalTree`-related, `Bitmap`, `Color`, `Cancellation`, `Queryable`, `Graphics`, `Size`, `String`, `DirectoryInfo`, `DenseDateTimeConverter`, `EnumExtensions`, `ServiceCollectionExtensions`. |
|
||||
| `Services/` | `AnnotationService` (RabbitMQ.Stream + LinqToDB + MediatR), `FailsafeAnnotationsProducer`, `GalleryService` (thumbnails), `AuthProvider`, `TileProcessor`, `SatelliteDownloader`, `LoaderClient` (ZMQ), `GpsMatcher/*` (ZMQ + service + event handler + events), `Inference/InferenceClient` (ZMQ), `Inference/InferenceService` (orchestrates inference jobs), `Inference/InferenceServiceEventHandler`, `Inference/InferenceServiceEvents`, `Cache`, `HashExtensions`. |
|
||||
| `Constants.cs`| `CONFIG_PATH`, suffixes, file naming conventions, the `FailsafeAppConfig` builder. |
|
||||
| `Security.cs` | AES-256-CFB credentials encryption / decryption. Key is time-derived for local on-disk storage; symmetrical on both ends of the loader handoff. |
|
||||
|
||||
Concrete examples of the tangle, taken straight from this commit:
|
||||
|
||||
- `Azaion.Common.Database.Annotation` carries `[IgnoreMember] System.Windows.Media.Color`
|
||||
in its computed `Colors` projection. A "database model" that imports
|
||||
`System.Windows.Media`. The DTO assembly cannot exist outside WPF.
|
||||
- `Annotation.Init(DirectoriesConfig, Dictionary<int, DetectionClass>)` is a
|
||||
**static initializer** that the application calls once at startup.
|
||||
Hydrated entities then read the static `_labelsDir`, `_imagesDir`,
|
||||
`_thumbDir`, `DetectionClassesDict` to compute their own paths and class
|
||||
names. Two annotation databases or two configurations cannot coexist in
|
||||
the same process.
|
||||
- `AppConfig` aggregates ten config sections including `UIConfig`, but is
|
||||
also passed to the queue producer, the loader client, the inference
|
||||
client, the satellite downloader. There is no clear seam between
|
||||
"app-host concerns" and "business concerns".
|
||||
- `AnnotationService` is constructed with `IDbFactory`,
|
||||
`FailsafeAnnotationsProducer`, `QueueConfig`, `UIConfig`,
|
||||
`IGalleryService`, `IMediator`, `IAzaionApi`, `ILogger`. It runs a
|
||||
`RabbitMQ.Stream.Consumer` *inside its constructor* via `Task.Run(...).Wait()`,
|
||||
publishes MediatR events into the WPF dispatcher, and uses
|
||||
`_imageAccessSemaphore` and `_messageProcessingSemaphore` to serialize
|
||||
cross-thread SQLite writes. Lifecycle and threading model are baked in.
|
||||
|
||||
The follow-on commits (`e7ea5a8`, `9e7dc29`, `fbbe556`) split this into
|
||||
proper layers: repositories with interfaces, an `AnnotationPathResolver`
|
||||
service replacing the static fields on `Annotation`, a separate
|
||||
`Azaion.Common.Database.AnnotationRepository` + `IAnnotationRepository`,
|
||||
removal of `AnnotationsDbSchemaHolder`, and finally the move from a WPF
|
||||
client to a containerised .NET API exposing REST + SSE.
|
||||
|
||||
## 7. Cython sidecars
|
||||
|
||||
### `Azaion.Inference`
|
||||
|
||||
Standalone Python project compiled with Cython
|
||||
(`build_inference.cmd` → PyInstaller → `azaion-inference.spec`). Top-level
|
||||
modules at `22529c2`:
|
||||
|
||||
```
|
||||
ai_availability_status ai_config annotation
|
||||
classes.json constants_inf file_data
|
||||
inference inference_engine onnx_engine
|
||||
loader_client main_inference remote_command_handler_inf
|
||||
```
|
||||
|
||||
It exposes a ZeroMQ DealerSocket port. The .NET side (`InferenceClient` in
|
||||
`Azaion.Common.Services.Inference`) sends MessagePack-serialised
|
||||
`RemoteCommand` envelopes; the Cython side dispatches to either the ONNX
|
||||
or TensorRT engine, reads inputs from the local file system, and streams
|
||||
back `DetectionEvent`-shaped progress.
|
||||
|
||||
State the engine reports (mapped 1:1 to the React UI's
|
||||
`AIAvailabilityStatus`):
|
||||
|
||||
| Value | Name | Meaning |
|
||||
|-------|-------------|--------------------------------------------------------|
|
||||
| 0 | None | Initial. |
|
||||
| 10 | Downloading | Pulling weights from Admin API / CDN via `loader_client`. |
|
||||
| 20 | Converting | ONNX → TensorRT (TensorRT devices only). |
|
||||
| 30 | Uploading | Uploading converted engine back to API for caching. |
|
||||
| 200 | Enabled | Inference engine ready. |
|
||||
| 300 | Warning | Recoverable, the engine may come back. |
|
||||
| 500 | Error | Failed to initialize. |
|
||||
|
||||
### `Azaion.Loader`
|
||||
|
||||
Same shape — Cython, ZeroMQ DealerSocket, separate process. Modules:
|
||||
|
||||
```
|
||||
api_client cdn_manager constants
|
||||
credentials file_data hardware_service
|
||||
main_loader remote_command remote_command_handler
|
||||
security
|
||||
```
|
||||
|
||||
It is the only component in the legacy stack with internet access. It
|
||||
authenticates the user against the remote API, downloads encrypted
|
||||
resource bundles (model checkpoints, `config.system.json`,
|
||||
`config.secured.json`), and decrypts them on demand using a key derived
|
||||
from `email + password + hardware_id` (`security.pyx` + `hardware_service.pyx`).
|
||||
The .NET side never sees the raw resource files until the loader has
|
||||
already decrypted them.
|
||||
|
||||
This Loader is exactly the component documented in
|
||||
`suite/_docs/00_top_level_architecture.md` under **Binary Split Security**.
|
||||
The 3 KB key fragment, the encrypted on-device archive, and the
|
||||
`SHA384(fragment + hw_hash + creds)` derivation all originate here.
|
||||
|
||||
## 8. Data model (LinqToDB → SQLite)
|
||||
|
||||
| Table | Purpose |
|
||||
|-------------------------------|------------------------------------------------------------------------------------------|
|
||||
| `Annotations` | Per-frame label set: `Name`, `MediaHash`, `OriginalMediaName`, `Time`, `CreatedDate`, `CreatedEmail`, `CreatedRole`, `Source`, `AnnotationStatus`, `ValidateDate`, `ValidateEmail`, `Detections[]`, `Milliseconds`, `Lat`, `Lon`. |
|
||||
| `Detections` | Bounding box rows: `ClassNumber`, geometry, `Confidence`. |
|
||||
| `MediaFiles` | Files indexed by hash. Used to dedupe + drive the media list. |
|
||||
| `AnnotationQueueRecord` (`AnnotationsQueueRecords` table) | Local **failsafe outbox** for RabbitMQ publication. `FailsafeAnnotationsProducer` drains this every 10 s. |
|
||||
|
||||
Schema is created/migrated in process by `SchemaMigrator` against a
|
||||
SQLite file pointed to by `DirectoriesConfig`.
|
||||
|
||||
## 9. Annotation sync (edge → central)
|
||||
|
||||
The legacy code already had the eventual edge-to-central sync wired in:
|
||||
|
||||
```
|
||||
Annotator window // user creates annotation
|
||||
│ MediatR: AnnotationCreatedEvent
|
||||
▼
|
||||
AnnotationService // local SQLite write
|
||||
│
|
||||
├─► Annotations row
|
||||
├─► AnnotationQueueRecord row (unless SilentDetection)
|
||||
│
|
||||
▼
|
||||
FailsafeAnnotationsProducer // BackgroundService-style task
|
||||
│ MessagePack + Gzip, retry on failure
|
||||
▼
|
||||
RabbitMQ.Stream "azaion-annotations" // remote
|
||||
│
|
||||
└─► consumed by ai-queue-handler / Admin API in the remote tier
|
||||
```
|
||||
|
||||
The React UI inherits the *protocol* (RabbitMQ stream, MessagePack +
|
||||
Gzip, dedupe by `Annotation.Name`) but no longer owns it — it runs in
|
||||
the new `annotations/` .NET API submodule of the suite.
|
||||
|
||||
## 10. What survived into the new world
|
||||
|
||||
The following concepts are direct ports of the legacy WPF design and
|
||||
should be implemented in the React UI exactly the same way:
|
||||
|
||||
- **Module switcher** with localized name + SVG icon → top navigation bar
|
||||
(Flights, Annotations, Dataset, Admin, Settings).
|
||||
- **Detection-class strip** with class colour, number, name, and PhotoMode
|
||||
switcher (Regular / Winter / Night, offsets 0/20/40).
|
||||
`yoloId = classId + photoModeOffset`.
|
||||
- **Canvas editor**: bounding-box draw / 8-handle resize / Ctrl multi-select
|
||||
/ Ctrl+wheel zoom / Ctrl+drag pan / crosshair with active-class hint /
|
||||
normalized-coordinate clamping.
|
||||
- **Annotation row gradient** in the side list: a left-to-right gradient
|
||||
composed of each detection's class colour, opacity proportional to
|
||||
`Confidence`. Empty annotation → `#40DDDDDD` background.
|
||||
- **Affiliation icons** (Friendly / Hostile / Unknown / None) and
|
||||
**combat readiness** indicator (Ready / NotReady / Unknown) drawn next
|
||||
to the bounding-box label.
|
||||
- **Time-windowed annotation rendering** during video playback:
|
||||
`Before = 50 ms`, `After = 150 ms`, lookup via interval tree.
|
||||
- **Frame-by-frame stepping** in fixed counts (1, 5, 10, 30, 60), computed
|
||||
from `1 / fps`.
|
||||
- **Localized class names** (`DetectionClass.UIName` carried alongside the
|
||||
English `Name`).
|
||||
- **Camera config per session**: altitude / focal length / sensor width
|
||||
drives GSD-based detection-size validation.
|
||||
- **GPS-denied panel toggle** under the canvas (now implemented as the
|
||||
GPS-Denied mode of the Flights page in the React UI).
|
||||
- **Help window** with the six annotation quality rules.
|
||||
- **Color scheme**: dark navy/blue primary (`#343a40`), orange accents
|
||||
(`#fd7e14`), dark gray background (`#1e1e1e`), green success
|
||||
(`#40c057`), blue primary buttons (`#228be6`), red danger (`#fa5252`).
|
||||
- **Confirmation dialogs** for delete-media / delete-selected / delete-all
|
||||
/ deactivate-user.
|
||||
- **Resizable panel widths** persisted per user.
|
||||
|
||||
## 11. What is intentionally NOT being ported
|
||||
|
||||
- The DI host inside the UI process. The React app does not own a
|
||||
service container, RabbitMQ consumer, SQLite database, or background
|
||||
worker. All of that now lives in the per-service .NET / Python /
|
||||
Cython submodules.
|
||||
- LibVLCSharp. The browser's native `<video>` element with a
|
||||
frame-accurate seeking shim handles playback.
|
||||
- ZeroMQ DealerSockets. The browser only speaks HTTP and SSE. Inference,
|
||||
GPS-matching, satellite tile fetching, and loader requests are all
|
||||
exposed as REST endpoints by their respective suite services.
|
||||
- The static `Annotation.Init(...)` initializer. Path/colour computation
|
||||
becomes selector logic over the API DTOs, with no static state.
|
||||
- The `Azaion.Common` god-assembly. Each concern is now a separate suite
|
||||
submodule with its own repo, Dockerfile, and OpenAPI document.
|
||||
- The `Azaion.LoaderUI` external-process handoff with encrypted creds on
|
||||
the command line. The browser performs `POST /auth/login` against the
|
||||
Admin API and stores a JWT.
|
||||
- The Cython `Loader` and the binary-split key-fragment dance. That whole
|
||||
protocol is server-side now (`loader/` submodule) and the React UI is
|
||||
not involved beyond showing a progress screen.
|
||||
|
||||
## 12. How to read the research copy
|
||||
|
||||
```
|
||||
cd /Users/obezdienie001/dev/azaion/suite/annotations-research
|
||||
git status # detached at 22529c2 "Revert add MediaFile"
|
||||
git log --oneline -n 5 # see surrounding commits
|
||||
```
|
||||
|
||||
The folder is a plain clone of `suite/annotations` and is **not** wired
|
||||
into the suite's `.gitmodules`, so the parent repository ignores it.
|
||||
|
||||
If you want to compare the WPF-era code to the immediately following
|
||||
"big refactoring" commit, the comparison is:
|
||||
|
||||
```
|
||||
git log --oneline --reverse 22529c2..e7ea5a8 # there is only e7ea5a8 itself
|
||||
git diff 22529c2 e7ea5a8 -- Azaion.Annotator # what the cleanup changed
|
||||
git diff 22529c2 e7ea5a8 -- Azaion.Common # the big assembly split prep
|
||||
```
|
||||
|
||||
Two commits later (`fbbe556` / `9e7dc29`) the WPF projects disappear
|
||||
entirely and are replaced by a containerised .NET API — that is the
|
||||
state currently checked out in `suite/annotations`.
|
||||
Reference in New Issue
Block a user