# 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 ```cpp // 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(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: ```csharp /// /// Read action mapping table from GeViServer /// public async Task 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"); } /// /// Write action mapping table to GeViServer /// public async Task 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**: ```csharp public class ActionMappingHandler { private readonly StateQueryHandler _stateQueryHandler; // ADD THIS /// /// Enumerate action mappings from LIVE GeViServer database /// public async Task> EnumerateActionMappingsAsync(bool enabledOnly = false) { // REPLACE alarm query workaround with: var mappingTable = await _stateQueryHandler.GetActionMappingTableAsync(); // Convert CSAActionMappingTable to List return ConvertToActionMappingConfigs(mappingTable, enabledOnly); } /// /// Save action mappings back to GeViServer /// public async Task SaveActionMappingsAsync(List mappings) { // Convert List 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: ```csharp /// /// Convert SDK action mapping table to internal format /// private List ConvertToActionMappingConfigs( CSAActionMappingTable sdkTable, bool enabledOnly) { var result = new List(); // 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; } /// /// Convert internal format to SDK action mapping table /// private CSAActionMappingTable ConvertToActionMappingTable(List 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; } /// /// Parse action string to SDK CAction object /// 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: ```csharp /// /// Update action mappings in GeViServer /// public override async Task 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: ```protobuf 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**: ```json { "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**: ```json { "mappings": [ { "id": "mapping_1", "name": "Motion Detection → Camera Routing", "input_action": "VMD_Start(101038)", "output_actions": ["CrossSwitch(101038, 1, 0)"], "enabled": true } ] } ``` **Response**: ```json { "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)