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>
9.0 KiB
9.0 KiB
title, description
| title | description |
|---|---|
| Configuration | JSON configuration files, monitor wall topology, and function button mapping |
Configuration System
Configuration Files
All configuration is stored as JSON files, managed centrally on the AppServer and synced to each keyboard.
appsettings.json (Main)
{
"UseSoftwareRendering": true,
"AppServerConfiguration": {
"Uri": "https://copilot.test.d6.colsys.cz",
"Timeout": "00:00:01.5"
},
"ReconnectionConfiguration": {
"ReconnectionPeriod": "00:00:02"
},
"ViewerStates": {
"AppServerConnectionTimeout": "00:00:05"
}
}
| Setting | Value | Purpose |
|---|---|---|
UseSoftwareRendering |
true |
Forces WPF software rendering (no GPU) for LattePanda |
AppServerConfiguration.Uri |
HTTPS URL | Central AppServer for coordination |
AppServerConfiguration.Timeout |
1.5s | HTTP request timeout |
ReconnectionPeriod |
2s | Auto-reconnect interval to camera servers |
AppServerConnectionTimeout |
5s | SignalR connection timeout |
appsettings-copilot.json (Per-Keyboard)
{
"CopilotConfig": {
"WallId": 2,
"CameraLockOptions": {
"Priority": "Low",
"Timeout": "00:05:00"
},
"CameraNumberOptions": {
"MaxLength": 6,
"Prefixes": ["500", "501", "502"],
"CancelEditTimeout": "00:05"
},
"ViewerStateThrottleInterval": "00:00:00.150",
"CentralServer": {
"IpAddress": "192.168.102.186",
"UserName": "sysadmin",
"Password": "masterkey"
},
"AllowPrepositionsInterval": {
"Min": 10,
"Max": 99
},
"PlaybackOptions": {
"Allowed": true,
"Speeds": [1, 2, 5, 15, 30, 100, 250]
},
"AlarmHistoryOptions": {
"CameraIdExtractionRegex": "^\\D*(?<CameraId>\\d{6})\\D*$",
"DefaultSearchInterval": "30.00:00:00"
}
}
}
| Setting | Purpose |
|---|---|
WallId |
Which monitor wall this keyboard controls |
CameraLockOptions.Priority |
PTZ lock priority (Low / High) |
CameraLockOptions.Timeout |
Lock auto-expiration (5 min) |
CameraNumberOptions.MaxLength |
Max digits for camera number (6) |
CameraNumberOptions.Prefixes |
Auto-prefixes for camera numbers |
CancelEditTimeout |
Auto-cancel camera number entry (5 sec) |
ViewerStateThrottleInterval |
Debounce viewer state updates (150ms) |
PlaybackOptions.Speeds |
Shuttle playback speeds (1x to 250x) |
AllowPrepositionsInterval |
Valid preposition range (10-99) |
appsettings-camera-servers.json
{
"CameraServerConfig": {
"CameraServers": [
{
"IpAddress": "192.168.102.20",
"UserName": "sysadmin",
"Password": "masterkey",
"CameraServerType": "GCore"
},
{
"IpAddress": "192.168.102.186",
"UserName": "sysadmin",
"Password": "masterkey",
"CameraServerType": "GeviScope"
}
]
}
}
Supported server types: GeViScope, GCore, GeViSoft
appsettings-monitor-wall.json (Topology)
Defines the physical monitor wall layout:
graph TD
subgraph "Wall (WallId: 1, Name: HDŘÚ TSK/DIC)"
subgraph "Segment 1 (4 rows × 8 cols)"
M1["Monitor #01<br/>2×2 span<br/>VM: 151,152,153,154"]
M2["Monitor #02<br/>2×2 span<br/>VM: 155,156,157,158"]
M3["Monitor #03"]
M4["Monitor #04"]
M5["Monitor #05<br/>...etc"]
end
end
Hierarchy:
Wall
└── Segment (grid: RowCount × ColumnCount)
└── Monitor (physical screen, positioned at Row/Col with RowSpan/ColSpan)
└── VirtualMonitor (individual video feed, positioned within monitor grid)
A single physical monitor can display multiple virtual monitors (e.g., 2×2 quad view). Each VirtualMonitor has a unique VirtualMonitorId that maps to a ViewerId in the camera server.
appsettings-function-buttons.json
Maps F1-F7 and Home keys to actions per wall:
{
"FunctionButtonsConfig": {
"FunctionButtons": [
{
"WallId": 1,
"Buttons": [
{
"Button": "F1",
"Actions": [
{ "ViewerId": 1001, "ActionType": "CrossSwitch", "SourceId": 501111 },
{ "ViewerId": 1002, "ActionType": "CrossSwitch", "SourceId": 502345 }
]
},
{
"Button": "F4",
"Actions": [
{ "ViewerId": 1001, "ActionType": "SequenceStart", "SourceId": 258 }
]
}
]
}
]
}
}
Action types: CrossSwitch (switch camera to viewer), SequenceStart (start camera cycling)
appsettings-sequences.json
{
"SequencesConfig": {
"Sequences": [
{
"Id": 1,
"CategoryId": 1,
"Name": "Test-A",
"Interval": 1,
"MediaChannels": [500100, 500101, 500102, 500103]
}
]
}
}
AppServer Configuration
{
"ConnectionStrings": {
"SqliteConnection": "Data Source=copilot.db"
},
"CameaApiClient": {
"Uri": "http://localhost:8081",
"Timeout": "00:00:01.5",
"TimeZone": "Central Europe Standard Time"
},
"LockExpirationConfig": {
"LockExpirationTimeout": "00:05:00",
"NotificationBeforeExpiration": "00:01:00"
},
"Kestrel": {
"Endpoints": {
"Https": {
"Url": "https://*:443",
"Certificate": {
"Subject": "copilot.test.d6.colsys.cz",
"Store": "Root",
"Location": "LocalMachine"
}
}
}
}
}
Network Topology
┌───────────────────────────────────────────────────────────┐
│ 192.168.102.0/24 Network │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ GeViScope Server │ │ G-Core Server │ │
│ │ 192.168.102.186 │ │ 192.168.102.20 │ │
│ │ sysadmin/master │ │ sysadmin/master │ │
│ └────────┬─────────┘ └────────┬────────┘ │
│ │ TCP (native SDK) │ TCP (native SDK) │
│ │ │ │
│ ┌────────┴──────────────────────┴────────┐ │
│ │ Keyboard SBC │ │
│ │ (LattePanda Sigma) │ │
│ │ Copilot.App.exe (WPF) │ │
│ └────────┬───────────────────────────────┘ │
│ │ HTTPS/WSS (SignalR) │
│ │ │
│ ┌────────┴───────────────────────────────┐ │
│ │ AppServer │ │
│ │ copilot.test.d6.colsys.cz:443 │ │
│ │ Copilot.AppServer.exe │ │
│ │ + SQLite DB + Camea API (:8081) │ │
│ └────────────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────────────┐ │
│ │ Elasticsearch (logging) │ │
│ │ localhost:9200 │ │
│ └────────────────────────────────────────┘ │
└───────────────────────────────────────────────────────────┘