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:
188
geutebruck-api/src/sdk-bridge/GeViScopeBridge/Program.cs
Normal file
188
geutebruck-api/src/sdk-bridge/GeViScopeBridge/Program.cs
Normal file
@@ -0,0 +1,188 @@
|
||||
using GeViScopeBridge.Services;
|
||||
using GeViScopeBridge.SDK;
|
||||
using Serilog;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace GeViScopeBridge
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
public static async Task Main(string[] args)
|
||||
{
|
||||
// Configure Serilog
|
||||
Log.Logger = new LoggerConfiguration()
|
||||
.MinimumLevel.Debug()
|
||||
.WriteTo.Console()
|
||||
.WriteTo.File("logs/sdk-bridge-.log", rollingInterval: RollingInterval.Day)
|
||||
.CreateLogger();
|
||||
|
||||
try
|
||||
{
|
||||
Log.Information("Starting GeViSoft/GeViScope SDK Bridge (gRPC Server)");
|
||||
|
||||
// Load configuration
|
||||
var configuration = new ConfigurationBuilder()
|
||||
.SetBasePath(Directory.GetCurrentDirectory())
|
||||
.AddJsonFile("appsettings.json", optional: false)
|
||||
.AddEnvironmentVariables()
|
||||
.Build();
|
||||
|
||||
// Get GeViScope connection settings (for video operations)
|
||||
string geviScopeHost = configuration["GeViScope:Host"] ?? "localhost";
|
||||
string geviScopeUsername = configuration["GeViScope:Username"] ?? "sysadmin";
|
||||
string geviScopePassword = configuration["GeViScope:Password"] ?? "";
|
||||
|
||||
// Get GeViSoft connection settings (for configuration/action mapping)
|
||||
string geviSoftHost = configuration["GeViSoft:Host"] ?? "localhost";
|
||||
string geviSoftUsername = configuration["GeViSoft:Username"] ?? "sysadmin";
|
||||
string geviSoftPassword = configuration["GeViSoft:Password"] ?? "";
|
||||
|
||||
int grpcPort = int.Parse(configuration["GrpcServer:Port"] ?? "50051");
|
||||
|
||||
Log.Information("Configuration loaded: GeViScope={ScopeHost}, GeViSoft={SoftHost}, gRPC Port={Port}",
|
||||
geviScopeHost, geviSoftHost, grpcPort);
|
||||
|
||||
// Create GeViScope SDK wrapper (for video operations)
|
||||
var geviScopeWrapper = new GeViDatabaseWrapper(
|
||||
geviScopeHost,
|
||||
geviScopeUsername,
|
||||
geviScopePassword,
|
||||
Log.Logger);
|
||||
|
||||
// Create GeViSoft SDK wrapper (for configuration management)
|
||||
var geviSoftWrapper = new GeViDatabaseWrapper(
|
||||
geviSoftHost,
|
||||
geviSoftUsername,
|
||||
geviSoftPassword,
|
||||
Log.Logger);
|
||||
|
||||
// Connect to GeViSoft (GeViServer) - PRIMARY connection
|
||||
Log.Information("Connecting to GeViSoft (GeViServer)...");
|
||||
bool softConnected = await geviSoftWrapper.ConnectAsync();
|
||||
|
||||
if (!softConnected)
|
||||
{
|
||||
Log.Fatal("Failed to connect to GeViSoft. Exiting.");
|
||||
return;
|
||||
}
|
||||
|
||||
Log.Information("Successfully connected to GeViSoft");
|
||||
|
||||
// Connect to GeViScope (GSCServer) - OPTIONAL for video operations
|
||||
Log.Information("Connecting to GeViScope (GSCServer)...");
|
||||
bool scopeConnected = await geviScopeWrapper.ConnectAsync();
|
||||
|
||||
if (scopeConnected)
|
||||
{
|
||||
Log.Information("Successfully connected to GeViScope");
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Warning("Failed to connect to GeViScope - video operations will be unavailable");
|
||||
}
|
||||
|
||||
// Create SDK handlers (using GeViSoft for state queries and actions)
|
||||
var stateQueryHandler = new StateQueryHandler(geviSoftWrapper, Log.Logger);
|
||||
var actionDispatcher = new ActionDispatcher(geviSoftWrapper, Log.Logger);
|
||||
|
||||
// Create action mapping handler (using GeViSoft for configuration)
|
||||
var actionMappingHandler = new ActionMappingHandler(geviSoftWrapper, stateQueryHandler, Log.Logger);
|
||||
if (softConnected)
|
||||
{
|
||||
await actionMappingHandler.InitializeAsync();
|
||||
}
|
||||
|
||||
// Create SetupClient wrapper for configuration operations
|
||||
var setupClient = new GeViSetupClientWrapper(geviSoftHost, geviSoftUsername, geviSoftPassword);
|
||||
bool setupConnected = await setupClient.ConnectAsync();
|
||||
|
||||
if (setupConnected)
|
||||
{
|
||||
Log.Information("SetupClient connected for configuration operations");
|
||||
|
||||
// DIAGNOSTIC: Log all marker names on startup
|
||||
try
|
||||
{
|
||||
Log.Information("DIAGNOSTIC: Reading configuration to identify marker names...");
|
||||
var config = setupClient.ReadAndParseConfiguration();
|
||||
if (config != null)
|
||||
{
|
||||
var allMarkers = config.RootNodes.Where(n => n.NodeType == "marker").ToList();
|
||||
var markerNames = allMarkers.Select(n => n.Name).Distinct().OrderBy(n => n).ToList();
|
||||
Log.Information("DIAGNOSTIC: Found {MarkerCount} markers with {UniqueCount} unique names:",
|
||||
allMarkers.Count, markerNames.Count);
|
||||
foreach (var name in markerNames)
|
||||
{
|
||||
var count = allMarkers.Count(m => m.Name == name);
|
||||
Log.Information(" - '{Name}': {Count} occurrences", name, count);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Warning(ex, "DIAGNOSTIC: Failed to read marker names");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Warning("SetupClient connection failed - configuration operations will be unavailable");
|
||||
}
|
||||
|
||||
// Build gRPC server
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// Add gRPC services
|
||||
builder.Services.AddGrpc();
|
||||
|
||||
// Add singletons - GeViScope for existing operations
|
||||
builder.Services.AddSingleton(geviScopeWrapper);
|
||||
builder.Services.AddSingleton<GeViDatabaseWrapper>(provider => geviScopeWrapper);
|
||||
builder.Services.AddSingleton(stateQueryHandler);
|
||||
builder.Services.AddSingleton(actionDispatcher);
|
||||
builder.Services.AddSingleton(Log.Logger);
|
||||
|
||||
// Add GeViSoft singletons for action mapping operations
|
||||
builder.Services.AddSingleton(geviSoftWrapper);
|
||||
builder.Services.AddSingleton(actionMappingHandler);
|
||||
|
||||
// Add SetupClient for configuration operations
|
||||
builder.Services.AddSingleton(setupClient);
|
||||
|
||||
// Configure Kestrel for gRPC
|
||||
builder.WebHost.ConfigureKestrel(options =>
|
||||
{
|
||||
options.ListenAnyIP(grpcPort, listenOptions =>
|
||||
{
|
||||
listenOptions.Protocols = Microsoft.AspNetCore.Server.Kestrel.Core.HttpProtocols.Http2;
|
||||
});
|
||||
});
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// Map gRPC services
|
||||
app.MapGrpcService<CameraServiceImplementation>();
|
||||
app.MapGrpcService<MonitorServiceImplementation>();
|
||||
app.MapGrpcService<CrossSwitchServiceImplementation>();
|
||||
app.MapGrpcService<ActionMappingServiceImplementation>();
|
||||
app.MapGrpcService<ConfigurationServiceImplementation>();
|
||||
|
||||
// Add health check endpoint
|
||||
app.MapGet("/", () => "GeViScope SDK Bridge (gRPC) - Use a gRPC client to connect");
|
||||
|
||||
Log.Information("gRPC server starting on port {Port}", grpcPort);
|
||||
Log.Information("Services registered: CameraService, MonitorService, CrossSwitchService, ActionMappingService, ConfigurationService");
|
||||
|
||||
await app.RunAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Fatal(ex, "Application terminated unexpectedly");
|
||||
}
|
||||
finally
|
||||
{
|
||||
Log.Information("Shutting down GeViScope SDK Bridge");
|
||||
Log.CloseAndFlush();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user