Files
COPILOT/Docs/IMPLEMENTATION_QUICK_REFERENCE.md
klas 40143734fc Initial commit: COPILOT D6 Flutter keyboard controller
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>
2026-02-12 14:57:38 +01:00

7.1 KiB

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)

POST http://localhost:7720/api/crossswitch
Content-Type: application/json

{
  "camera_id": 101,
  "monitor_id": 5,
  "mode": 0
}

PTZ Control

POST http://localhost:7720/api/ptz/move
Content-Type: application/json

{
  "camera_id": 101,
  "pan": 50,
  "tilt": 30,
  "zoom": 0
}

Query Active Alarms

GET http://localhost:7720/api/alarms/active

Query Monitor State

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

{
  "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
}