Flutter web app replacing legacy WPF CCTV surveillance keyboard controller. Includes wall overview, section view with monitor grid, camera input, PTZ control, alarm/lock/sequence BLoCs, and legacy-matching UI styling. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
269 lines
7.2 KiB
Markdown
269 lines
7.2 KiB
Markdown
---
|
|
title: "System Architecture"
|
|
description: "Component diagrams, dependency graph, and deployment model"
|
|
---
|
|
|
|
# System Architecture
|
|
|
|
## Component Dependency Graph
|
|
|
|
```mermaid
|
|
graph TB
|
|
subgraph "Copilot.App.exe (WPF Client)"
|
|
App["App.cs<br/>.NET Generic Host"]
|
|
MW["MainWindow<br/>Input Router"]
|
|
SVM["SegmentViewModel<br/>Main Screen Logic"]
|
|
PVM["PlaybackViewModel"]
|
|
CSVM["CameraSearchViewModel"]
|
|
PreVM["PrepositionsViewModel"]
|
|
SeqVM["SequenceCategoriesViewModel<br/>SequencesViewModel"]
|
|
SvcVM["ServiceMenuViewModel"]
|
|
|
|
subgraph "App Services"
|
|
CCS["CameraControllerService"]
|
|
CLS["CameraLockService"]
|
|
CAS["CameraAlarmService"]
|
|
SeqS["SequenceService"]
|
|
FBS["FunctionButtonsService"]
|
|
PBS["PlaybackStateService"]
|
|
CfgS["ConfigurationService"]
|
|
NAS["NetworkAvailabilityState"]
|
|
end
|
|
|
|
subgraph "Navigation"
|
|
NS["NavigationService"]
|
|
NStore["NavigationStore"]
|
|
end
|
|
end
|
|
|
|
subgraph "Copilot.Device.dll"
|
|
CD["CopilotDevice"]
|
|
SP["SerialPortDataProvider"]
|
|
HID["JoystickHidDataProvider"]
|
|
end
|
|
|
|
subgraph "Copilot.Common.dll"
|
|
Config["Configuration System"]
|
|
Hub["ICopilotHub<br/>ICopilotHubEvents"]
|
|
Prov["ICopilotInfoProvider"]
|
|
end
|
|
|
|
subgraph "Copilot.Common.Services.dll"
|
|
MCS["MediaChannelService"]
|
|
VSS["ViewerStateService"]
|
|
CSDP["CameraServerDriverProvider"]
|
|
CenDP["CentralServerDriverProvider"]
|
|
end
|
|
|
|
subgraph "Copilot.Drivers.Common.dll"
|
|
IMC["IMovementController"]
|
|
ICSD["ICameraServerDriver"]
|
|
ICenD["ICentralServerDriver"]
|
|
IVC["IViewerController"]
|
|
end
|
|
|
|
subgraph "Copilot.Drivers.GeViScope.dll"
|
|
GscD["GeViScopeDriver"]
|
|
GscMC["GeViScopeMovementController"]
|
|
GscVC["GeviScopeViewerController"]
|
|
end
|
|
|
|
subgraph "Copilot.Drivers.GCore.dll"
|
|
GcD["GCoreDriver"]
|
|
GcMC["GCoreMovementController"]
|
|
GcVC["GCoreViewerController"]
|
|
end
|
|
|
|
subgraph "Copilot.AppServer.Client.dll"
|
|
ASC["AppServerClient<br/>(SignalR)"]
|
|
Avail["AvailabilityState"]
|
|
end
|
|
|
|
App --> MW
|
|
MW --> CD
|
|
MW --> SVM
|
|
SVM --> CCS
|
|
SVM --> CLS
|
|
SVM --> FBS
|
|
SVM --> CAS
|
|
CLS --> Hub
|
|
CAS --> Hub
|
|
SeqS --> Hub
|
|
CfgS --> Hub
|
|
CCS --> CSDP
|
|
CCS --> MCS
|
|
CSDP --> GscD
|
|
CSDP --> GcD
|
|
CenDP --> GscD
|
|
GscD --> GscMC
|
|
GscD --> GscVC
|
|
GcD --> GcMC
|
|
GcD --> GcVC
|
|
GscMC -.->|implements| IMC
|
|
GcMC -.->|implements| IMC
|
|
GscD -.->|implements| ICSD
|
|
GcD -.->|implements| ICSD
|
|
Hub --> ASC
|
|
```
|
|
|
|
## Service Lifetime & Registration
|
|
|
|
All services are registered via `Microsoft.Extensions.DependencyInjection` in `App.cs`:
|
|
|
|
```mermaid
|
|
graph LR
|
|
subgraph "Singletons (one instance)"
|
|
MainWindow
|
|
SegmentViewModel
|
|
NavigationStore
|
|
NavigationService
|
|
CameraLockService
|
|
PlaybackStateService
|
|
ConfigurationService
|
|
CameraAlarmService
|
|
CameraControllerService
|
|
PrepositionService
|
|
SequenceService
|
|
NetworkAvailabilityState
|
|
end
|
|
|
|
subgraph "Transient (new per request)"
|
|
CameraSearchViewModel
|
|
PlaybackViewModel
|
|
PrepositionsViewModel
|
|
SequenceCategoriesViewModel
|
|
SequencesViewModel
|
|
ServiceMenuViewModel
|
|
ErrorViewModel
|
|
CameraAlarmHistoryViewModel
|
|
end
|
|
|
|
subgraph "Hosted Services (background workers)"
|
|
StartupConfigurationCheckWorker
|
|
ViewerStatesInitWorker
|
|
CameraAlarmsUpdateWorker
|
|
NetworkAvailabilityWorker
|
|
SequenceService_Hosted["SequenceService<br/>(also IHostedService)"]
|
|
ProcessMonitorServiceHost
|
|
end
|
|
```
|
|
|
|
## Startup Sequence
|
|
|
|
```mermaid
|
|
sequenceDiagram
|
|
participant App as App.cs
|
|
participant Host as .NET Host
|
|
participant MW as MainWindow
|
|
participant Nav as NavigationService
|
|
participant Device as CopilotDevice
|
|
participant Hub as SignalR Hub
|
|
participant Config as ConfigurationService
|
|
|
|
App->>Host: Build & ConfigureServices
|
|
Host->>Host: Register all DI services
|
|
App->>Host: StartAsync()
|
|
Host->>Host: Start background workers
|
|
|
|
Note over Host: StartupConfigurationCheckWorker starts
|
|
Note over Host: ViewerStatesInitWorker starts
|
|
Note over Host: CameraAlarmsUpdateWorker starts
|
|
Note over Host: NetworkAvailabilityWorker starts
|
|
|
|
App->>Nav: Navigate<SegmentsPage>(wallId)
|
|
App->>MW: Show()
|
|
MW->>Device: Subscribe JoystickMoved
|
|
MW->>Device: Subscribe VirtualKeyDown/Up
|
|
|
|
alt Firmware outdated
|
|
MW->>Nav: Navigate<UpdateFirmwarePage>
|
|
end
|
|
|
|
App->>Config: UpdateAllConfigurationsIfChanged()
|
|
Config->>Hub: GetConfigurationFile() for each manager
|
|
Config->>Config: Write updated configs to disk
|
|
|
|
Note over App: Application Ready
|
|
```
|
|
|
|
## Navigation System
|
|
|
|
The app uses a custom stack-based navigation system (not WPF Navigation):
|
|
|
|
```mermaid
|
|
stateDiagram-v2
|
|
[*] --> SegmentsPage: Initial (select segment)
|
|
SegmentsPage --> SegmentPage: Select segment
|
|
|
|
SegmentPage --> CameraSearchPage: Search button
|
|
SegmentPage --> PrepositionsPage: Prepositions button
|
|
SegmentPage --> PlaybackPage: Playback button
|
|
SegmentPage --> SequenceCategoriesPage: Sequence button
|
|
SegmentPage --> CameraAlarmHistoryPage: Alarm history
|
|
SegmentPage --> ServiceMenuPage: Hold Backspace 3s
|
|
SegmentPage --> CameraLockExpirationView: Lock expiring
|
|
|
|
CameraSearchPage --> SegmentPage: Back
|
|
PrepositionsPage --> PrepositionAddPage: Add preset
|
|
PrepositionAddPage --> PrepositionsPage: Back
|
|
PrepositionsPage --> SegmentPage: Back
|
|
PlaybackPage --> SegmentPage: Back
|
|
SequenceCategoriesPage --> SequencesPage: Select category
|
|
SequencesPage --> SegmentPage: Start sequence
|
|
|
|
ServiceMenuPage --> UpdateFirmwarePage: Update firmware
|
|
```
|
|
|
|
## Two Communication Paths
|
|
|
|
The app communicates with two distinct backends simultaneously:
|
|
|
|
### Path 1: Direct SDK (Camera Operations)
|
|
```
|
|
App → CameraServerDriverProvider → GeViScope/GCore Driver → Native SDK → Camera Server
|
|
```
|
|
- **Used for:** CrossSwitch, PTZ, Playback, Viewer control
|
|
- **Latency:** < 50ms (direct TCP connection to camera server)
|
|
- **Reconnection:** Automatic, 2-second retry interval
|
|
|
|
### Path 2: SignalR Hub (Coordination)
|
|
```
|
|
App → CopilotHub (SignalR) → AppServer → Database/Logic → Response
|
|
```
|
|
- **Used for:** Camera locks, sequences, config sync, alarm history, viewer state
|
|
- **Latency:** ~100-200ms (HTTPS + server processing)
|
|
- **Reconnection:** Automatic via SignalR reconnection policy
|
|
|
|
```mermaid
|
|
graph LR
|
|
subgraph "App"
|
|
SVM["SegmentViewModel"]
|
|
end
|
|
|
|
subgraph "Path 1: Direct (low latency)"
|
|
Driver["GeViScope/GCore Driver"]
|
|
SDK["Native SDK (TCP)"]
|
|
end
|
|
|
|
subgraph "Path 2: Coordinated"
|
|
HubC["SignalR Client"]
|
|
HTTPS["HTTPS/WSS"]
|
|
end
|
|
|
|
subgraph "Camera Server"
|
|
CS["GeViScope / G-Core"]
|
|
end
|
|
|
|
subgraph "AppServer"
|
|
HubS["SignalR Hub"]
|
|
DB["SQLite DB"]
|
|
end
|
|
|
|
SVM -->|"CrossSwitch, PTZ,<br/>Playback"| Driver
|
|
Driver --> SDK --> CS
|
|
|
|
SVM -->|"Locks, Sequences,<br/>Config, Alarms"| HubC
|
|
HubC --> HTTPS --> HubS
|
|
HubS --> DB
|
|
```
|