Files
geutebruck/geutebruck-api/ACTION_MAPPING_IMPLEMENTATION.md
Administrator 14893e62a5 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>
2025-12-31 18:10:54 +01:00

15 KiB

Action Mapping Implementation Status & Plan

Last Updated: 2025-12-11 Feature: GeViSoft Action Mapping Configuration API


Overview

This document tracks the implementation of action mapping configuration endpoints for the Geutebruck API. Action mappings allow GeViServer to trigger output actions automatically when input actions occur (e.g., motion detection triggers camera routing and alarm notifications).


SDK Documentation References

Primary Documentation (Extracted & Searchable)

Location: C:\Gevisoft\Documentation\extracted_html\

Comprehensive Guide:

  • C:\DEV\COPILOT\gevisoft-sdk-reference.md - Complete SDK reference with action mapping examples

Key Documentation Files:

  • GeViSoft_SDK_Documentation\313Action Mapping.htm - Action mapping concepts and usage
  • GeViSoft_SDK_Documentation\414StateQueries.htm - State query patterns (including action mapping queries)
  • GeViSoft_API_Documentation\class_ge_vi_a_p_i_client.html - GeViAPIClient class reference

Critical SDK Methods for Action Mapping

// Reading Action Mapping Configuration
CStateQuery* query = new CSQGetActionMappingTable();
CStateAnswer* answer = m_APIClient->SendStateQuery(query, INFINITE);
query->DeleteObject();

if (answer && answer->m_AnswerKind == sak_ActionMappingTable)
{
    CSAActionMappingTable* mappings =
        reinterpret_cast<CSAActionMappingTable*>(answer);

    // Process action mappings...

    answer->DeleteObject();
}

// Writing Action Mapping Configuration
CSAActionMappingTable* modifiedMappings = /* your modified mappings */;
CStateQuery* setQuery = new CSQSetActionMappingTable(modifiedMappings);
CStateAnswer* answer = m_APIClient->SendStateQuery(setQuery, INFINITE);

if (answer && answer->m_AnswerKind == sak_OK)
{
    // Success!
}
setQuery->DeleteObject();
answer->DeleteObject();

Current Implementation Status

Completed (Phase 1 - MVP Workaround)

Files Implemented:

  1. src\sdk-bridge\GeViScopeBridge\SDK\ActionMappingHandler.cs

    • Basic action mapping handler
    • Current approach: In-memory storage + alarm queries
    • CRUD operations: Create, Read, Update, Delete
    • Execution tracking (count, last executed)
  2. src\sdk-bridge\GeViScopeBridge\Services\ActionMappingServiceImplementation.cs

    • gRPC service wrapper for ActionMappingHandler
    • Exposes GetActionMappings() and GetActionMapping() RPCs
  3. src\sdk-bridge\GeViScopeBridge\SDK\AlarmQueryService.cs

    • Queries alarm data from GeViServer
    • Used as workaround to read action mappings
  4. src\sdk-bridge\DiagnoseActionMapping\Program.cs

    • Diagnostic tool for testing action mapping functionality
    • Validates SDK connection and action sending

What Works:

  • In-memory action mapping storage
  • Basic CRUD operations
  • Execution tracking and statistics
  • gRPC service exposure
  • Diagnostic testing tool

⚠️ Known Limitations (Current Implementation)

  1. No Direct GeViServer Integration:

    • Action mappings stored in application memory only
    • Does NOT read from GeViServer's action mapping table
    • Does NOT write back to GeViServer configuration
    • Lost on restart
  2. Alarm Query Workaround:

    • Uses AlarmQueryService to query alarms as a proxy for action mappings
    • Not the correct SDK method for action mapping configuration
  3. Missing SDK State Queries:

    • CSQGetActionMappingTable() - NOT implemented
    • CSQSetActionMappingTable() - NOT implemented

Phase 2 - Proper SDK Integration (TO DO)

Required Changes

1. Implement State Query Support for Action Mappings

File: src\sdk-bridge\GeViScopeBridge\SDK\StateQueryHandler.cs

Add methods:

/// <summary>
/// Read action mapping table from GeViServer
/// </summary>
public async Task<CSAActionMappingTable> GetActionMappingTableAsync()
{
    var query = new CSQGetActionMappingTable();
    var answer = await _geviWrapper.SendStateQueryAsync(query);

    if (answer?.m_AnswerKind == sak_ActionMappingTable)
    {
        return (CSAActionMappingTable)answer;
    }

    throw new Exception("Failed to retrieve action mapping table");
}

/// <summary>
/// Write action mapping table to GeViServer
/// </summary>
public async Task<bool> SetActionMappingTableAsync(CSAActionMappingTable mappingTable)
{
    var query = new CSQSetActionMappingTable(mappingTable);
    var answer = await _geviWrapper.SendStateQueryAsync(query);

    return answer?.m_AnswerKind == sak_OK;
}

2. Update ActionMappingHandler.cs

Changes Required:

public class ActionMappingHandler
{
    private readonly StateQueryHandler _stateQueryHandler; // ADD THIS

    /// <summary>
    /// Enumerate action mappings from LIVE GeViServer database
    /// </summary>
    public async Task<List<ActionMappingConfig>> EnumerateActionMappingsAsync(bool enabledOnly = false)
    {
        // REPLACE alarm query workaround with:
        var mappingTable = await _stateQueryHandler.GetActionMappingTableAsync();

        // Convert CSAActionMappingTable to List<ActionMappingConfig>
        return ConvertToActionMappingConfigs(mappingTable, enabledOnly);
    }

    /// <summary>
    /// Save action mappings back to GeViServer
    /// </summary>
    public async Task<bool> SaveActionMappingsAsync(List<ActionMappingConfig> mappings)
    {
        // Convert List<ActionMappingConfig> to CSAActionMappingTable
        var mappingTable = ConvertToActionMappingTable(mappings);

        // Send to GeViServer
        return await _stateQueryHandler.SetActionMappingTableAsync(mappingTable);
    }
}

3. Add Data Conversion Methods

File: src\sdk-bridge\GeViScopeBridge\SDK\ActionMappingHandler.cs

Add conversion methods:

/// <summary>
/// Convert SDK action mapping table to internal format
/// </summary>
private List<ActionMappingConfig> ConvertToActionMappingConfigs(
    CSAActionMappingTable sdkTable,
    bool enabledOnly)
{
    var result = new List<ActionMappingConfig>();

    // Iterate through SDK mapping table entries
    // Each entry has:
    //   - Input action definition
    //   - List of output actions
    //   - Caption/description
    //   - Enable/disable state

    foreach (var entry in sdkTable.Entries)
    {
        var config = new ActionMappingConfig
        {
            Id = entry.Id.ToString(),
            Name = entry.Caption,
            Description = entry.Description ?? "",
            InputAction = entry.InputAction.ToString(),
            OutputActions = entry.OutputActions.Select(a => a.ToString()).ToList(),
            Enabled = entry.Enabled,
            // ... other fields
        };

        if (!enabledOnly || config.Enabled)
        {
            result.Add(config);
        }
    }

    return result;
}

/// <summary>
/// Convert internal format to SDK action mapping table
/// </summary>
private CSAActionMappingTable ConvertToActionMappingTable(List<ActionMappingConfig> configs)
{
    var sdkTable = new CSAActionMappingTable();

    foreach (var config in configs)
    {
        var entry = new ActionMappingEntry
        {
            Caption = config.Name,
            Description = config.Description,
            InputAction = ParseAction(config.InputAction),
            OutputActions = config.OutputActions.Select(ParseAction).ToList(),
            Enabled = config.Enabled
        };

        sdkTable.Entries.Add(entry);
    }

    return sdkTable;
}

/// <summary>
/// Parse action string to SDK CAction object
/// </summary>
private CAction ParseAction(string actionString)
{
    // Use CGeViMessage.ReadASCIIMessage to parse action string
    int bytesRead = 0;
    var message = CGeViMessage.ReadASCIIMessage(
        actionString,
        actionString.Length,
        out bytesRead
    );

    return (CAction)message;
}

4. Update gRPC Service Implementation

File: src\sdk-bridge\GeViScopeBridge\Services\ActionMappingServiceImplementation.cs

Add RPC methods:

/// <summary>
/// Update action mappings in GeViServer
/// </summary>
public override async Task<UpdateActionMappingsResponse> UpdateActionMappings(
    UpdateActionMappingsRequest request,
    ServerCallContext context)
{
    try
    {
        var configs = request.Mappings.Select(ConvertFromProto).ToList();

        bool success = await _actionMappingHandler.SaveActionMappingsAsync(configs);

        return new UpdateActionMappingsResponse
        {
            Success = success,
            Message = success ? "Action mappings updated successfully" : "Failed to update action mappings"
        };
    }
    catch (Exception ex)
    {
        _logger.Error(ex, "UpdateActionMappings failed");
        throw new RpcException(new Status(StatusCode.Internal, $"Failed to update: {ex.Message}"));
    }
}

5. Update Protobuf Definitions

File: src\sdk-bridge\GeViScopeBridge\Protos\actionmapping.proto

Add messages:

message UpdateActionMappingsRequest {
  repeated ActionMapping mappings = 1;
}

message UpdateActionMappingsResponse {
  bool success = 1;
  string message = 2;
  int32 updated_count = 3;
}

service ActionMappingService {
  rpc GetActionMappings(GetActionMappingsRequest) returns (GetActionMappingsResponse);
  rpc GetActionMapping(GetActionMappingRequest) returns (ActionMappingResponse);
  rpc UpdateActionMappings(UpdateActionMappingsRequest) returns (UpdateActionMappingsResponse); // NEW
}

Task Breakdown

Phase 2 Tasks

  • T001: Add CSQGetActionMappingTable support to StateQueryHandler.cs
  • T002: Add CSQSetActionMappingTable support to StateQueryHandler.cs
  • T003: Implement ConvertToActionMappingConfigs() conversion method
  • T004: Implement ConvertToActionMappingTable() conversion method
  • T005: Implement ParseAction() helper for action string parsing
  • T006: Update EnumerateActionMappingsAsync() to use state queries
  • T007: Add SaveActionMappingsAsync() method to ActionMappingHandler
  • T008: Update protobuf definitions for UpdateActionMappings RPC
  • T009: Implement UpdateActionMappings gRPC service method
  • T010: Update DiagnoseActionMapping tool to test new functionality
  • T011: Write unit tests for conversion methods
  • T012: Write integration tests with live GeViServer
  • T013: Update API documentation with new endpoints
  • T014: Remove in-memory storage workaround
  • T015: Remove AlarmQueryService dependency (if no longer needed)

Phase 3 Tasks (Python API Layer)

  • T016: Create Python gRPC client for action mapping service
  • T017: Create FastAPI endpoint GET /api/v1/gevisoft/action-mappings
  • T018: Create FastAPI endpoint GET /api/v1/gevisoft/action-mappings/{id}
  • T019: Create FastAPI endpoint PUT /api/v1/gevisoft/action-mappings
  • T020: Create FastAPI endpoint POST /api/v1/gevisoft/action-mappings
  • T021: Create FastAPI endpoint DELETE /api/v1/gevisoft/action-mappings/{id}
  • T022: Add validation for action mapping schema
  • T023: Add audit logging for configuration changes
  • T024: Add caching for action mapping configuration
  • T025: Write API contract tests
  • T026: Update OpenAPI specification

API Endpoints (Target)

GET /api/v1/gevisoft/action-mappings

Description: List all action mappings from GeViServer Response:

{
  "total_count": 15,
  "enabled_count": 12,
  "disabled_count": 3,
  "mappings": [
    {
      "id": "mapping_1",
      "name": "Motion Detection → Camera Routing",
      "description": "Route camera to monitor 1 when motion detected",
      "input_action": "VMD_Start(101038)",
      "output_actions": [
        "CrossSwitch(101038, 1, 0)"
      ],
      "enabled": true,
      "execution_count": 42,
      "last_executed": "2025-12-11T10:30:00Z",
      "created_at": "2025-12-01T08:00:00Z",
      "updated_at": "2025-12-11T10:30:00Z"
    }
  ]
}

PUT /api/v1/gevisoft/action-mappings

Description: Update action mapping configuration on GeViServer Request Body:

{
  "mappings": [
    {
      "id": "mapping_1",
      "name": "Motion Detection → Camera Routing",
      "input_action": "VMD_Start(101038)",
      "output_actions": ["CrossSwitch(101038, 1, 0)"],
      "enabled": true
    }
  ]
}

Response:

{
  "success": true,
  "message": "Action mappings updated successfully",
  "updated_count": 1
}

Testing Strategy

Unit Tests

  • ActionMappingConfig model validation
  • Conversion methods (SDK ↔ internal format)
  • gRPC service method logic

Integration Tests

  • Connect to GeViServer
  • Read action mapping table
  • Modify action mapping table
  • Write action mapping table back
  • Verify changes persisted

End-to-End Tests

  • Full API workflow: GET → PUT → GET (verify changes)
  • Action mapping execution validation
  • Error handling and rollback scenarios

Dependencies

SDK Classes (C++/CLI Interop)

  • CSQGetActionMappingTable - State query to read action mappings
  • CSQSetActionMappingTable - State query to write action mappings
  • CSAActionMappingTable - State answer containing action mapping table
  • CAction - Base class for action objects
  • CGeViMessage - Base class for all messages, provides ReadASCIIMessage()

NuGet Packages

  • GEUTEBRUECK.GeViSoftSDKNET (GeViSoft SDK wrapper)
  • Grpc.AspNetCore (gRPC server)
  • Serilog (logging)

Known Issues & Risks

Issues

  1. SDK documentation incomplete: May need to inspect DLL interfaces directly
  2. Action string parsing: Complex actions may require custom parsing logic
  3. Concurrency: GeViServer may have locking on configuration changes

Risks

  1. Configuration corruption: Improper data conversion could corrupt GeViServer config
    • Mitigation: Thorough testing, backup/restore functionality
  2. Performance: Reading large action mapping tables may be slow
    • Mitigation: Caching, incremental updates

References

SDK Documentation

  • Location: C:\Gevisoft\Documentation\extracted_html\
  • Comprehensive Guide: C:\DEV\COPILOT\gevisoft-sdk-reference.md
  • Action Mapping Guide: GeViSoft_SDK_Documentation\313Action Mapping.htm
  • State Queries: GeViSoft_SDK_Documentation\414StateQueries.htm
  • GeViAPIClient: GeViSoft_API_Documentation\class_ge_vi_a_p_i_client.html

Code Files

  • src\sdk-bridge\GeViScopeBridge\SDK\ActionMappingHandler.cs
  • src\sdk-bridge\GeViScopeBridge\SDK\StateQueryHandler.cs
  • src\sdk-bridge\GeViScopeBridge\Services\ActionMappingServiceImplementation.cs
  • src\sdk-bridge\DiagnoseActionMapping\Program.cs

Status: Phase 1 Complete (MVP workaround) | Phase 2 Pending (proper SDK integration) Next Step: Implement state query support (T001-T002)