Phase 2 Complete: SDK Bridge Foundation (T011-T026)
Implemented complete C# gRPC service wrapping GeViScope SDK: ✅ gRPC Protocol Definitions (T011-T014): - common.proto: Status, Error, Timestamp messages - camera.proto: CameraService with ListCameras, GetCamera RPCs - monitor.proto: MonitorService with ListMonitors, GetMonitor RPCs - crossswitch.proto: CrossSwitchService with ExecuteCrossSwitch, ClearMonitor, GetRoutingState, HealthCheck RPCs ✅ SDK Wrapper Classes (T015-T021): - GeViDatabaseWrapper.cs: Connection lifecycle with retry logic (3 attempts, exponential backoff) - StateQueryHandler.cs: GetFirst/GetNext enumeration pattern for cameras/monitors - ActionDispatcher.cs: CrossSwitch and ClearVideoOutput action execution - ErrorTranslator.cs: SDK errors → gRPC status codes → HTTP status codes ✅ gRPC Service Implementations (T022-T026): - CameraService.cs: List/get camera information from GeViServer - MonitorService.cs: List/get monitor/viewer information from GeViServer - CrossSwitchService.cs: Execute cross-switching, clear monitors, query routing state - Program.cs: gRPC server with Serilog logging, dependency injection - appsettings.json: GeViServer connection configuration Key Features: - Async/await pattern throughout - Comprehensive error handling and logging - In-memory routing state tracking - Health check endpoint - Connection retry with exponential backoff - Proper resource disposal Architecture: FastAPI (Python) ←gRPC→ SDK Bridge (C# .NET 8.0) ←SDK→ GeViServer Ready for Phase 3: Python API Foundation 🤖 Generated with Claude Code (https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
119
src/sdk-bridge/GeViScopeBridge/Services/MonitorService.cs
Normal file
119
src/sdk-bridge/GeViScopeBridge/Services/MonitorService.cs
Normal file
@@ -0,0 +1,119 @@
|
||||
using Grpc.Core;
|
||||
using GeViScopeBridge.Protos;
|
||||
using GeViScopeBridge.SDK;
|
||||
using GeViScopeBridge.Utils;
|
||||
using Serilog;
|
||||
|
||||
namespace GeViScopeBridge.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// gRPC service for monitor (video output) operations
|
||||
/// </summary>
|
||||
public class MonitorServiceImplementation : MonitorService.MonitorServiceBase
|
||||
{
|
||||
private readonly StateQueryHandler _stateQuery;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public MonitorServiceImplementation(StateQueryHandler stateQuery, ILogger logger)
|
||||
{
|
||||
_stateQuery = stateQuery ?? throw new ArgumentNullException(nameof(stateQuery));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List all monitors/viewers (video outputs)
|
||||
/// </summary>
|
||||
public override async Task<ListMonitorsResponse> ListMonitors(
|
||||
ListMonitorsRequest request,
|
||||
ServerCallContext context)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.Information("ListMonitors called");
|
||||
|
||||
var monitors = await _stateQuery.EnumerateMonitorsAsync();
|
||||
|
||||
var response = new ListMonitorsResponse
|
||||
{
|
||||
TotalCount = monitors.Count
|
||||
};
|
||||
|
||||
foreach (var monitor in monitors)
|
||||
{
|
||||
response.Monitors.Add(new MonitorInfo
|
||||
{
|
||||
Id = monitor.Id,
|
||||
Name = monitor.Name,
|
||||
Description = monitor.Description,
|
||||
IsActive = monitor.IsActive,
|
||||
CurrentCameraId = monitor.CurrentCameraId,
|
||||
Status = monitor.Status,
|
||||
LastUpdated = new Timestamp
|
||||
{
|
||||
Seconds = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
|
||||
Nanos = 0
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_logger.Information("ListMonitors completed: {Count} monitors", monitors.Count);
|
||||
return response;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "Failed to list monitors");
|
||||
throw ErrorTranslator.CreateRpcException(ex, "Failed to list monitors");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get detailed information about a specific monitor
|
||||
/// </summary>
|
||||
public override async Task<MonitorInfo> GetMonitor(
|
||||
GetMonitorRequest request,
|
||||
ServerCallContext context)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.Information("GetMonitor called for monitor {MonitorId}", request.MonitorId);
|
||||
|
||||
// Enumerate all monitors and find the requested one
|
||||
var monitors = await _stateQuery.EnumerateMonitorsAsync();
|
||||
var monitor = monitors.FirstOrDefault(m => m.Id == request.MonitorId);
|
||||
|
||||
if (monitor == null)
|
||||
{
|
||||
throw new RpcException(new Status(StatusCode.NotFound,
|
||||
$"Monitor with ID {request.MonitorId} not found"));
|
||||
}
|
||||
|
||||
var response = new MonitorInfo
|
||||
{
|
||||
Id = monitor.Id,
|
||||
Name = monitor.Name,
|
||||
Description = monitor.Description,
|
||||
IsActive = monitor.IsActive,
|
||||
CurrentCameraId = monitor.CurrentCameraId,
|
||||
Status = monitor.Status,
|
||||
LastUpdated = new Timestamp
|
||||
{
|
||||
Seconds = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
|
||||
Nanos = 0
|
||||
}
|
||||
};
|
||||
|
||||
_logger.Information("GetMonitor completed for monitor {MonitorId}", request.MonitorId);
|
||||
return response;
|
||||
}
|
||||
catch (RpcException)
|
||||
{
|
||||
throw; // Re-throw RpcExceptions as-is
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "Failed to get monitor {MonitorId}", request.MonitorId);
|
||||
throw ErrorTranslator.CreateRpcException(ex, "Failed to get monitor");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user