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>
225 lines
7.1 KiB
Markdown
225 lines
7.1 KiB
Markdown
# COPILOT Implementation Quick Reference
|
|
|
|
## Architecture at a Glance
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ KEYBOARD (LattePanda Sigma) │
|
|
│ ┌────────────────────────────────────────────────────────────┐ │
|
|
│ │ Flutter App (UI + PRIMARY Logic if elected) │ │
|
|
│ │ • Camera/Monitor selection │ │
|
|
│ │ • PTZ controls │ │
|
|
│ │ • Alarm display │ │
|
|
│ │ • Sequence management (PRIMARY only) │ │
|
|
│ └─────────────────────────┬──────────────────────────────────┘ │
|
|
│ │ localhost HTTP │
|
|
│ ┌─────────────────────────┼──────────────────────────────────┐ │
|
|
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
|
|
│ │ │GeViScope│ │ G-Core │ │GeViSrvr │ C# Bridges (.NET 8) │ │
|
|
│ │ │ :7720 │ │ :7721 │ │ :7710 │ │ │
|
|
│ │ └────┬────┘ └────┬────┘ └────┬────┘ │ │
|
|
│ └───────┼───────────┼───────────┼────────────────────────────┘ │
|
|
└──────────┼───────────┼───────────┼──────────────────────────────┘
|
|
│ │ │
|
|
▼ ▼ ▼
|
|
GeViScope G-Core GeViServer
|
|
Servers Servers (PTZ only)
|
|
```
|
|
|
|
---
|
|
|
|
## Key Commands
|
|
|
|
### ViewerConnectLive (Switch Camera to Monitor)
|
|
```http
|
|
POST http://localhost:7720/api/crossswitch
|
|
Content-Type: application/json
|
|
|
|
{
|
|
"camera_id": 101,
|
|
"monitor_id": 5,
|
|
"mode": 0
|
|
}
|
|
```
|
|
|
|
### PTZ Control
|
|
```http
|
|
POST http://localhost:7720/api/ptz/move
|
|
Content-Type: application/json
|
|
|
|
{
|
|
"camera_id": 101,
|
|
"pan": 50,
|
|
"tilt": 30,
|
|
"zoom": 0
|
|
}
|
|
```
|
|
|
|
### Query Active Alarms
|
|
```http
|
|
GET http://localhost:7720/api/alarms/active
|
|
```
|
|
|
|
### Query Monitor State
|
|
```http
|
|
GET http://localhost:7720/api/monitors
|
|
```
|
|
|
|
---
|
|
|
|
## State Queries (SDK)
|
|
|
|
| Query | Purpose | Answer Type |
|
|
|-------|---------|-------------|
|
|
| `GeViSQ_GetFirstAlarm(activeOnly, enabledOnly)` | First active alarm | `GeViSA_AlarmInfo` |
|
|
| `GeViSQ_GetNextAlarm(...)` | Next alarm in list | `GeViSA_AlarmInfo` |
|
|
| `GeViSQ_GetFirstVideoOutput(activeOnly, enabledOnly)` | First monitor | `GeViSA_VideoOutputInfo` |
|
|
| `GeViSQ_GetNextVideoOutput(...)` | Next monitor | `GeViSA_VideoOutputInfo` |
|
|
| `GeViSQ_GetFirstVideoInput(activeOnly, enabledOnly)` | First camera | `GeViSA_VideoInputInfo` |
|
|
| `GeViSQ_GetNextVideoInput(...)` | Next camera | `GeViSA_VideoInputInfo` |
|
|
|
|
---
|
|
|
|
## Event Notifications (Subscribe via PLC)
|
|
|
|
| Event | When Fired | Key Fields |
|
|
|-------|------------|------------|
|
|
| `EventStarted` | Alarm triggered | EventID, TypeID, ForeignKey |
|
|
| `EventStopped` | Alarm cleared | EventID, TypeID |
|
|
| `ViewerConnected` | Camera switched to monitor | Viewer, Channel, PlayMode |
|
|
| `ViewerCleared` | Monitor cleared | Viewer |
|
|
| `ViewerSelectionChanged` | Monitor content changed | Viewer, Channel, PlayMode |
|
|
| `VCAlarmQueueNotification` | Alarm queue change | Viewer, Notification, AlarmID |
|
|
| `DigitalInput` | External contact change | Contact, State |
|
|
|
|
---
|
|
|
|
## Alarm States (PlcViewerAlarmState)
|
|
|
|
| Value | Name | Description | Monitor Blocked? |
|
|
|-------|------|-------------|------------------|
|
|
| 0 | `vasNewAlarm` | New alarm added | YES |
|
|
| 1 | `vasPresented` | Currently displayed | YES |
|
|
| 2 | `vasStacked` | In queue, not displayed | Depends |
|
|
| 3 | `vasConfirmed` | Acknowledged | NO |
|
|
|
|
---
|
|
|
|
## PTZ Lock Flow
|
|
|
|
```
|
|
1. Keyboard → PRIMARY: RequestLock(cameraId, priority)
|
|
2. PRIMARY checks:
|
|
- Camera locked? → Compare priority
|
|
- High > Low priority wins
|
|
- Same priority → First wins
|
|
3. PRIMARY → Keyboard: LockGranted/LockDenied
|
|
4. If granted: Keyboard sends PTZ directly to server
|
|
5. Lock expires after 5 minutes
|
|
6. Warning sent at 4 minutes (1 min before expiry)
|
|
```
|
|
|
|
---
|
|
|
|
## Failover Timeline
|
|
|
|
```
|
|
T+0s PRIMARY stops sending heartbeats
|
|
T+2s STANDBY misses 1st heartbeat
|
|
T+4s STANDBY misses 2nd heartbeat
|
|
T+6s STANDBY misses 3rd heartbeat → Declares itself PRIMARY
|
|
T+6s New PRIMARY broadcasts role change
|
|
T+6s Keyboards reconnect to new PRIMARY
|
|
```
|
|
|
|
---
|
|
|
|
## Degraded Mode (No PRIMARY/STANDBY)
|
|
|
|
| Feature | Works? | Notes |
|
|
|---------|--------|-------|
|
|
| ViewerConnectLive | ✅ | Direct to server |
|
|
| PTZ Control | ✅ | Direct to server |
|
|
| PTZ Locking | ❌ | No coordinator |
|
|
| Sequences | ❌ | Runs on PRIMARY |
|
|
| State Sync | ❌ | No broadcaster |
|
|
| Alarms | ⚠️ | Local only per keyboard |
|
|
|
|
---
|
|
|
|
## Playback Commands
|
|
|
|
```
|
|
// Seek to timestamp
|
|
ViewerPlayFromTime(viewer, channel, "play forward",
|
|
"2024/01/15 14:30:00,000 GMT+01:00")
|
|
|
|
// Set playback speed (2x)
|
|
ViewerSetPlayMode(viewer, "play forward", 2.0)
|
|
|
|
// Jump back 60 seconds
|
|
ViewerJumpByTime(viewer, channel, "play forward", -60)
|
|
|
|
// Export snapshot
|
|
ViewerExportPicture(viewer, "C:\\Snapshots\\frame.bmp")
|
|
```
|
|
|
|
---
|
|
|
|
## Port Reference
|
|
|
|
| Port | Service |
|
|
|------|---------|
|
|
| 7700-7703 | GeViServer (native) |
|
|
| 7710 | GeViServer Bridge (REST) |
|
|
| 7720 | GeViScope Bridge (REST) |
|
|
| 7721 | G-Core Bridge (REST) |
|
|
| 8090 | PRIMARY WebSocket |
|
|
| 50051 | gRPC (if used) |
|
|
|
|
---
|
|
|
|
## Startup Sequence
|
|
|
|
```
|
|
1. Start bridges (GeViScope, G-Core, GeViServer)
|
|
2. Wait for bridge health checks to pass
|
|
3. Query current alarm state from all servers
|
|
4. Query current monitor state from all servers
|
|
5. Subscribe to event notifications
|
|
6. Connect to PRIMARY (or start election if none)
|
|
7. Sync shared state from PRIMARY
|
|
8. Start periodic alarm sync (every 30s)
|
|
9. Ready to accept user commands
|
|
```
|
|
|
|
---
|
|
|
|
## Error Handling
|
|
|
|
| Error | Action |
|
|
|-------|--------|
|
|
| Bridge unreachable | Retry 3x, then show offline status |
|
|
| Command timeout | Retry 1x, then report failure |
|
|
| PRIMARY unreachable | Continue in degraded mode |
|
|
| Alarm query fails | Use cached state, retry on next sync |
|
|
| Lock request timeout | Assume denied, inform user |
|
|
|
|
---
|
|
|
|
## Logging Format
|
|
|
|
```json
|
|
{
|
|
"timestamp": "2024-01-15T14:30:00.123Z",
|
|
"level": "INFO",
|
|
"keyboard_id": "KB-001",
|
|
"user": "operator1",
|
|
"event": "command_executed",
|
|
"command": "ViewerConnectLive",
|
|
"params": { "camera_id": 101, "monitor_id": 5 },
|
|
"duration_ms": 45,
|
|
"success": true
|
|
}
|
|
```
|