feat: Geutebruck GeViScope/GeViSoft Action Mapping System - MVP
This MVP release provides a complete full-stack solution for managing action mappings in Geutebruck's GeViScope and GeViSoft video surveillance systems. ## Features ### Flutter Web Application (Port 8081) - Modern, responsive UI for managing action mappings - Action picker dialog with full parameter configuration - Support for both GSC (GeViScope) and G-Core server actions - Consistent UI for input and output actions with edit/delete capabilities - Real-time action mapping creation, editing, and deletion - Server categorization (GSC: prefix for GeViScope, G-Core: prefix for G-Core servers) ### FastAPI REST Backend (Port 8000) - RESTful API for action mapping CRUD operations - Action template service with comprehensive action catalog (247 actions) - Server management (G-Core and GeViScope servers) - Configuration tree reading and writing - JWT authentication with role-based access control - PostgreSQL database integration ### C# SDK Bridge (gRPC, Port 50051) - Native integration with GeViSoft SDK (GeViProcAPINET_4_0.dll) - Action mapping creation with correct binary format - Support for GSC and G-Core action types - Proper Camera parameter inclusion in action strings (fixes CrossSwitch bug) - Action ID lookup table with server-specific action IDs - Configuration reading/writing via SetupClient ## Bug Fixes - **CrossSwitch Bug**: GSC and G-Core actions now correctly display camera/PTZ head parameters in GeViSet - Action strings now include Camera parameter: `@ PanLeft (Comment: "", Camera: 101028)` - Proper filter flags and VideoInput=0 for action mappings - Correct action ID assignment (4198 for GSC, 9294 for G-Core PanLeft) ## Technical Stack - **Frontend**: Flutter Web, Dart, Dio HTTP client - **Backend**: Python FastAPI, PostgreSQL, Redis - **SDK Bridge**: C# .NET 8.0, gRPC, GeViSoft SDK - **Authentication**: JWT tokens - **Configuration**: GeViSoft .set files (binary format) ## Credentials - GeViSoft/GeViScope: username=sysadmin, password=masterkey - Default admin: username=admin, password=admin123 ## Deployment All services run on localhost: - Flutter Web: http://localhost:8081 - FastAPI: http://localhost:8000 - SDK Bridge gRPC: localhost:50051 - GeViServer: localhost (default port) Generated with Claude Code (https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
493
geutebruck-api/docs/api-endpoints-explained.md
Normal file
493
geutebruck-api/docs/api-endpoints-explained.md
Normal file
@@ -0,0 +1,493 @@
|
||||
# REST API Endpoints - Data Sources Explained
|
||||
|
||||
## Overview
|
||||
|
||||
This document explains where each REST API endpoint gets its data and how the flow works.
|
||||
|
||||
---
|
||||
|
||||
## 🎥 Camera Endpoints
|
||||
|
||||
### GET /api/v1/cameras
|
||||
|
||||
**Data Source**: GeViServer Camera Manager (real-time, in-memory)
|
||||
|
||||
**Flow**:
|
||||
```
|
||||
Client → FastAPI → gRPC (CameraService) → StateQueryHandler → SDK → GeViServer Camera Manager
|
||||
```
|
||||
|
||||
**Code Path**:
|
||||
1. `routers/cameras.py` - REST endpoint
|
||||
2. `services/camera_service.py` - Business logic
|
||||
3. `clients/sdk_bridge_client.py` - gRPC client
|
||||
4. `Services/CameraService.cs` (C#) - gRPC service
|
||||
5. `SDK/StateQueryHandler.cs` (C#) - SDK wrapper
|
||||
6. `CSQGetFirstVideoInput()` + `CSQGetNextVideoInput()` - SDK enumeration
|
||||
|
||||
**What You Get**:
|
||||
```json
|
||||
[
|
||||
{
|
||||
"channel": 1,
|
||||
"name": "Front Door Camera",
|
||||
"description": "Main entrance",
|
||||
"has_ptz": true,
|
||||
"has_video_sensor": true,
|
||||
"status": "online"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
**Data Characteristics**:
|
||||
- ✅ Real-time camera list
|
||||
- ✅ Current connection status
|
||||
- ✅ Live from GeViServer memory
|
||||
- ❌ NOT stored in TestMKS.set
|
||||
- 🔄 Changes when cameras connect/disconnect
|
||||
|
||||
---
|
||||
|
||||
## 📺 Monitor Endpoints
|
||||
|
||||
### GET /api/v1/monitors
|
||||
|
||||
**Data Source**: GeViServer Monitor Manager (real-time, in-memory)
|
||||
|
||||
**Flow**:
|
||||
```
|
||||
Client → FastAPI → gRPC (MonitorService) → StateQueryHandler → SDK → GeViServer Monitor Manager
|
||||
```
|
||||
|
||||
**Code Path**:
|
||||
1. `routers/monitors.py`
|
||||
2. `services/monitor_service.py`
|
||||
3. `clients/sdk_bridge_client.py`
|
||||
4. `Services/MonitorService.cs` (C#)
|
||||
5. `SDK/StateQueryHandler.cs` (C#)
|
||||
6. `CSQGetFirstVideoOutput()` + `CSQGetNextVideoOutput()`
|
||||
|
||||
**What You Get**:
|
||||
```json
|
||||
[
|
||||
{
|
||||
"channel": 1,
|
||||
"name": "Main Monitor",
|
||||
"is_active": true,
|
||||
"current_camera_id": 7
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
**Data Characteristics**:
|
||||
- ✅ Real-time monitor/viewer list
|
||||
- ✅ Current routing state (which camera is shown)
|
||||
- ✅ GSCView client status
|
||||
- ❌ NOT in TestMKS.set
|
||||
- 🔄 Changes when monitors connect/disconnect
|
||||
|
||||
---
|
||||
|
||||
## 🔀 CrossSwitch Endpoints
|
||||
|
||||
### POST /api/v1/crossswitch
|
||||
|
||||
**Action**: Routes camera to monitor (sends command to GeViServer)
|
||||
|
||||
**Flow**:
|
||||
```
|
||||
Client → FastAPI → gRPC (CrossSwitchService) → ActionDispatcher → SDK → GeViServer Action Engine
|
||||
```
|
||||
|
||||
**Request**:
|
||||
```json
|
||||
{
|
||||
"camera_id": 7,
|
||||
"monitor_id": 3,
|
||||
"mode": 0
|
||||
}
|
||||
```
|
||||
|
||||
**Code Path**:
|
||||
1. `routers/crossswitch.py`
|
||||
2. `services/crossswitch_service.py`
|
||||
3. `Services/CrossSwitchService.cs` (C#)
|
||||
4. `SDK/ActionDispatcher.cs` (C#)
|
||||
5. `SendAction("CrossSwitch", ...)` - SDK action
|
||||
|
||||
**What Happens**:
|
||||
- GeViServer routes video from Camera 7 to Monitor 3
|
||||
- Monitor immediately shows the camera feed
|
||||
- Routing state updated in GeViServer memory
|
||||
|
||||
### DELETE /api/v1/monitors/{id}
|
||||
|
||||
**Action**: Clears monitor (stops video display)
|
||||
|
||||
**Code Path**: Similar to CrossSwitch but calls `ClearVideoOutput` action
|
||||
|
||||
---
|
||||
|
||||
## ⚙️ Configuration Endpoints (✅ IMPLEMENTED)
|
||||
|
||||
### GET /api/v1/configuration/servers
|
||||
|
||||
**Data Source**: TestMKS.set file (persistent binary configuration)
|
||||
|
||||
**Flow**:
|
||||
```
|
||||
Client → FastAPI → gRPC (ConfigurationService) → SetupClient.Download() →
|
||||
Parse .set file → Extract GeViGCoreServer folder → Return servers
|
||||
```
|
||||
|
||||
**Code Path**:
|
||||
1. `routers/configuration.py`
|
||||
2. `services/configuration_service.py`
|
||||
3. `clients/sdk_bridge_client.py`
|
||||
4. `Services/ConfigurationService.cs` (C#) - gRPC service
|
||||
5. `SDK/GeViSetupClientWrapper.cs` (C#) - Download config
|
||||
6. `Services/FolderTreeParser.cs` (C#) - Parse binary .set
|
||||
7. Navigate to "GeViGCoreServer" folder
|
||||
8. Extract all server nodes
|
||||
|
||||
**What You Get**:
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": "1",
|
||||
"alias": "Remote Server 1",
|
||||
"host": "192.168.1.100",
|
||||
"user": "admin",
|
||||
"enabled": true,
|
||||
"deactivate_echo": false,
|
||||
"deactivate_live_check": false
|
||||
},
|
||||
{
|
||||
"id": "2",
|
||||
"alias": "Remote Server 2",
|
||||
...
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
**Data Characteristics**:
|
||||
- ✅ Persistent in TestMKS.set file
|
||||
- ✅ Survives GeViServer restart
|
||||
- ✅ Visible in GeViSet UI
|
||||
- 💾 Binary format, parsed on-demand
|
||||
- 🔒 Requires SetupClient download
|
||||
|
||||
---
|
||||
|
||||
### POST /api/v1/configuration/servers
|
||||
|
||||
**Action**: Creates new G-Core server in configuration
|
||||
|
||||
**Flow**:
|
||||
```
|
||||
Client → FastAPI → gRPC → SetupClient.Download() → Parse .set →
|
||||
Auto-increment ID → Create server node → Write tree → SetupClient.Upload()
|
||||
```
|
||||
|
||||
**Request**:
|
||||
```json
|
||||
{
|
||||
"alias": "New Remote Server",
|
||||
"host": "192.168.1.200",
|
||||
"user": "admin",
|
||||
"password": "secret",
|
||||
"enabled": true
|
||||
}
|
||||
```
|
||||
|
||||
**What Happens**:
|
||||
1. Downloads current TestMKS.set file
|
||||
2. Parses binary format into folder tree
|
||||
3. Finds GeViGCoreServer folder
|
||||
4. Calculates new ID (max existing ID + 1)
|
||||
5. Creates new server node with all fields
|
||||
6. Writes modified tree back to binary
|
||||
7. Uploads to GeViServer
|
||||
8. GeViServer saves and reloads config
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
{
|
||||
"id": "14",
|
||||
"alias": "New Remote Server",
|
||||
"host": "192.168.1.200",
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### PUT /api/v1/configuration/servers/{server_id}
|
||||
|
||||
**Status**: ⚠️ KNOWN BUG - "Server ID is required"
|
||||
|
||||
**Intended Flow**: Download → Parse → Find server → Update fields → Upload
|
||||
|
||||
**Workaround**: Use DELETE + POST to replace server
|
||||
|
||||
---
|
||||
|
||||
### DELETE /api/v1/configuration/servers/{server_id}
|
||||
|
||||
**Action**: Removes server from configuration
|
||||
|
||||
**Flow**:
|
||||
```
|
||||
Client → FastAPI → gRPC → Download .set → Parse →
|
||||
Find server node by ID → Remove node → Upload
|
||||
```
|
||||
|
||||
**Critical Note**: When deleting multiple servers, always delete in **REVERSE ORDER** (highest ID first) to prevent ID shifting!
|
||||
|
||||
---
|
||||
|
||||
### GET /api/v1/configuration/action-mappings
|
||||
|
||||
**Data Source**: TestMKS.set file → ActionMapping folder
|
||||
|
||||
**Flow**: Same as servers, but navigates to "ActionMapping" folder
|
||||
|
||||
**What You Get**:
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Door Contact Mapping",
|
||||
"input_action": "GeVi DoorContact_101001",
|
||||
"output_action": "GeVi PanLeft_101027",
|
||||
"enabled": true
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
**Data Characteristics**:
|
||||
- ID is 1-based index in MappingRules list
|
||||
- Each mapping links input action → output action
|
||||
- Used for automation (e.g., door opens → camera pans)
|
||||
|
||||
---
|
||||
|
||||
### POST /api/v1/configuration/action-mappings
|
||||
|
||||
**Action**: Creates new action mapping
|
||||
|
||||
**Flow**: Download → Parse → Add to MappingRules list → Upload
|
||||
|
||||
**Request**:
|
||||
```json
|
||||
{
|
||||
"name": "VMD Trigger",
|
||||
"input_action": "GeVi VideoMotionDetection_7",
|
||||
"output_action": "GeVi StartRecording_7",
|
||||
"enabled": true
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### PUT /api/v1/configuration/action-mappings/{mapping_id}
|
||||
|
||||
**Action**: Updates existing action mapping
|
||||
|
||||
**Flow**: Download → Parse → Find by ID → Update fields → Upload
|
||||
|
||||
**Status**: ✅ WORKING (all CRUD operations functional)
|
||||
|
||||
---
|
||||
|
||||
### DELETE /api/v1/configuration/action-mappings/{mapping_id}
|
||||
|
||||
**Action**: Removes action mapping
|
||||
|
||||
**Flow**: Download → Parse → Remove from list → Upload
|
||||
|
||||
**⚠️ CRITICAL**: When deleting multiple mappings, always delete in **REVERSE ORDER**!
|
||||
|
||||
**Why?** Mappings use 1-based indices. Deleting mapping #5 shifts all subsequent mappings down by 1 index.
|
||||
|
||||
**Example Bug**:
|
||||
```
|
||||
Original: [#1, #2, #3, #4, #5, #6]
|
||||
Delete #4 → [#1, #2, #3, #5→4, #6→5]
|
||||
Delete #5 (which is now actually #6!) → WRONG MAPPING DELETED!
|
||||
```
|
||||
|
||||
**Solution**:
|
||||
```python
|
||||
# Sort by ID descending
|
||||
mappings_sorted = sorted(mappings, key=lambda x: x['id'], reverse=True)
|
||||
for mapping in mappings_sorted:
|
||||
delete_action_mapping(mapping['id']) # Delete from highest to lowest
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔐 Authentication Endpoints
|
||||
|
||||
### POST /api/v1/auth/login
|
||||
|
||||
**Data Source**: PostgreSQL database (user credentials)
|
||||
|
||||
**Flow**: FastAPI → AuthService → Database query → JWT generation
|
||||
|
||||
**Not fully implemented yet** - authentication is stubbed out
|
||||
|
||||
---
|
||||
|
||||
## ❤️ Health Endpoint
|
||||
|
||||
### GET /api/v1/health
|
||||
|
||||
**Data Source**: Real-time component checks
|
||||
|
||||
**Checks**:
|
||||
1. **Database**: PostgreSQL connection test
|
||||
2. **Redis**: Cache connectivity
|
||||
3. **SDK Bridge**: gRPC connection test (calls ping)
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
{
|
||||
"status": "healthy",
|
||||
"version": "1.0.0",
|
||||
"components": {
|
||||
"database": "healthy",
|
||||
"redis": "healthy",
|
||||
"sdk_bridge": "healthy"
|
||||
},
|
||||
"timestamp": "2025-12-16T21:15:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔍 Data Source Summary
|
||||
|
||||
| Endpoint Pattern | Data Source | Access Method | Persistent? |
|
||||
|------------------|-------------|---------------|-------------|
|
||||
| `/cameras` | GeViServer memory | SDK State Query | ❌ Real-time |
|
||||
| `/monitors` | GeViServer memory | SDK State Query | ❌ Real-time |
|
||||
| `/crossswitch` | GeViServer actions | SDK Action Dispatch | ❌ Command |
|
||||
| `/configuration/servers` | TestMKS.set | SetupClient + Parser | ✅ Persistent |
|
||||
| `/configuration/action-mappings` | TestMKS.set | SetupClient + Parser | ✅ Persistent |
|
||||
| `/auth/*` | PostgreSQL | Database query | ✅ Persistent |
|
||||
| `/health` | Component checks | Direct probe | ❌ Real-time |
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Testing the API
|
||||
|
||||
### Using Swagger UI
|
||||
|
||||
1. Open http://localhost:8000/docs
|
||||
2. All endpoints are documented with schemas
|
||||
3. Click "Try it out" to test
|
||||
|
||||
### Using curl
|
||||
|
||||
```bash
|
||||
# List cameras
|
||||
curl http://localhost:8000/api/v1/cameras
|
||||
|
||||
# List servers
|
||||
curl http://localhost:8000/api/v1/configuration/servers
|
||||
|
||||
# Get specific server
|
||||
curl http://localhost:8000/api/v1/configuration/servers/1
|
||||
|
||||
# Create server
|
||||
curl -X POST http://localhost:8000/api/v1/configuration/servers \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"alias": "Test Server",
|
||||
"host": "192.168.1.50",
|
||||
"user": "admin",
|
||||
"password": "test123",
|
||||
"enabled": true
|
||||
}'
|
||||
|
||||
# Delete server
|
||||
curl -X DELETE http://localhost:8000/api/v1/configuration/servers/14
|
||||
```
|
||||
|
||||
### Using Python
|
||||
|
||||
```python
|
||||
import requests
|
||||
|
||||
# List cameras
|
||||
response = requests.get("http://localhost:8000/api/v1/cameras")
|
||||
cameras = response.json()
|
||||
print(f"Found {len(cameras)} cameras")
|
||||
|
||||
# Get all servers
|
||||
response = requests.get("http://localhost:8000/api/v1/configuration/servers")
|
||||
servers = response.json()
|
||||
print(f"Found {len(servers)} servers")
|
||||
|
||||
# Create server
|
||||
new_server = {
|
||||
"alias": "Python Test Server",
|
||||
"host": "192.168.1.99",
|
||||
"user": "admin",
|
||||
"password": "secret",
|
||||
"enabled": True
|
||||
}
|
||||
response = requests.post(
|
||||
"http://localhost:8000/api/v1/configuration/servers",
|
||||
json=new_server
|
||||
)
|
||||
created = response.json()
|
||||
print(f"Created server with ID: {created['id']}")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🐛 Common Issues
|
||||
|
||||
### 1. Empty Camera List
|
||||
|
||||
**Cause**: No cameras configured in GeViServer
|
||||
|
||||
**Solution**: Add IP cameras in GeViSet or camera configuration
|
||||
|
||||
### 2. Configuration Endpoints Return Error
|
||||
|
||||
**Cause**: SetupClient cannot connect to GeViServer
|
||||
|
||||
**Possible reasons**:
|
||||
- GeViSet is connected (blocks SetupClient port)
|
||||
- GeViServer not running
|
||||
- Port 7702 not available
|
||||
|
||||
**Solution**:
|
||||
- Close GeViSet
|
||||
- Ensure GeViServer is running
|
||||
- Check `status-services.ps1`
|
||||
|
||||
### 3. CrossSwitch Fails
|
||||
|
||||
**Cause**: Invalid camera or monitor ID
|
||||
|
||||
**Solution**: First call `/cameras` and `/monitors` to get valid IDs
|
||||
|
||||
---
|
||||
|
||||
## 📚 Next Steps
|
||||
|
||||
1. **Test each endpoint** with Swagger UI
|
||||
2. **Verify data sources** match expectations
|
||||
3. **Check logs** for any errors
|
||||
4. **Use test scripts** in repository root:
|
||||
- `comprehensive_crud_test.py` - Full CRUD test
|
||||
- `verify_config_via_grpc.py` - Config verification
|
||||
|
||||
---
|
||||
|
||||
**Last Updated**: 2025-12-16
|
||||
**Version**: 1.0
|
||||
Reference in New Issue
Block a user