- Add GeViScope Bridge (C# .NET 8.0) on port 7720 - Full SDK wrapper for camera control, PTZ, actions/events - 17 REST API endpoints for GeViScope server interaction - Support for MCS (Media Channel Simulator) with 16 test channels - Real-time action/event streaming via PLC callbacks - Add GeViServer Bridge (C# .NET 8.0) on port 7710 - Integration with GeViSoft orchestration layer - Input/output control and event management - Update Python API with new routers - /api/geviscope/* - Proxy to GeViScope Bridge - /api/geviserver/* - Proxy to GeViServer Bridge - /api/excel/* - Excel import functionality - Add Flutter app GeViScope integration - GeViScopeRemoteDataSource with 17 API methods - GeViScopeBloc for state management - GeViScopeScreen with PTZ controls - App drawer navigation to GeViScope - Add SDK documentation (extracted from PDFs) - GeViScope SDK docs (7 parts + action reference) - GeViSoft SDK docs (12 chunks) - Add .mcp.json for Claude Code MCP server config Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
753 lines
21 KiB
C#
753 lines
21 KiB
C#
using GEUTEBRUECK.GeViScope.Wrapper.DBI;
|
|
using GEUTEBRUECK.GeViScope.Wrapper.Actions;
|
|
using GEUTEBRUECK.GeViScope.Wrapper.Actions.SystemActions;
|
|
using GEUTEBRUECK.GeViScope.Wrapper.Actions.DigitalContactsActions;
|
|
using GEUTEBRUECK.GeViScope.Wrapper.Actions.ActionDispatcher;
|
|
using GEUTEBRUECK.GeViScope.Wrapper.MediaPlayer;
|
|
using Microsoft.OpenApi.Models;
|
|
using System.Collections;
|
|
|
|
var builder = WebApplication.CreateBuilder(args);
|
|
|
|
// Add Swagger services
|
|
builder.Services.AddEndpointsApiExplorer();
|
|
builder.Services.AddSwaggerGen(c =>
|
|
{
|
|
c.SwaggerDoc("v1", new OpenApiInfo
|
|
{
|
|
Title = "GeViScope Bridge API",
|
|
Version = "v1",
|
|
Description = "REST API bridge for Geutebruck GeViScope Camera Server SDK. Provides access to camera control, video routing, PTZ, and action/event handling."
|
|
});
|
|
});
|
|
|
|
// GeViScope connection state
|
|
GscServer? gscServer = null;
|
|
GscPLCWrapper? gscPLC = null;
|
|
GscActionDispatcher? actionDispatcher = null;
|
|
string? currentAddress = null;
|
|
string? currentUsername = null;
|
|
List<string> receivedMessages = new List<string>();
|
|
List<MediaChannelInfo> mediaChannels = new List<MediaChannelInfo>();
|
|
|
|
var app = builder.Build();
|
|
|
|
// Action dispatcher event handlers
|
|
void OnCustomAction(object? sender, GscAct_CustomActionEventArgs e)
|
|
{
|
|
var msg = $"[{DateTime.Now:HH:mm:ss}] CustomAction({e.aInt}, \"{e.aString}\")";
|
|
Console.WriteLine(msg);
|
|
receivedMessages.Add(msg);
|
|
}
|
|
|
|
void OnDigitalInput(object? sender, GscAct_DigitalInputEventArgs e)
|
|
{
|
|
var msg = $"[{DateTime.Now:HH:mm:ss}] DigitalInput(GlobalNo={e.aContact.GlobalNo})";
|
|
Console.WriteLine(msg);
|
|
receivedMessages.Add(msg);
|
|
}
|
|
|
|
// PLC callback handler
|
|
void OnPLCCallback(object? sender, PLCCallbackEventArgs e)
|
|
{
|
|
try
|
|
{
|
|
if (e.PlcNotification.GetNotificationType() == GscPlcNotificationType.plcnNewActionData)
|
|
{
|
|
var action = e.PlcNotification.GetAction();
|
|
if (action != null && actionDispatcher != null)
|
|
{
|
|
if (!actionDispatcher.Dispatch(action))
|
|
{
|
|
var msg = $"[{DateTime.Now:HH:mm:ss}] Action: {action}";
|
|
Console.WriteLine(msg);
|
|
receivedMessages.Add(msg);
|
|
}
|
|
}
|
|
}
|
|
else if (e.PlcNotification.GetNotificationType() == GscPlcNotificationType.plcnNewEventData)
|
|
{
|
|
var eventData = e.PlcNotification.GetEventData();
|
|
if (eventData != null)
|
|
{
|
|
var eventType = eventData.EventNotificationType switch
|
|
{
|
|
GscPlcEventNotificationType.plcenEventStarted => "started",
|
|
GscPlcEventNotificationType.plcenEventStopped => "stopped",
|
|
GscPlcEventNotificationType.plcenEventRetriggered => "retriggered",
|
|
_ => "unknown"
|
|
};
|
|
var msg = $"[{DateTime.Now:HH:mm:ss}] Event: {eventData.EventHeader.EventName} {eventData.EventHeader.EventID} {eventType}";
|
|
Console.WriteLine(msg);
|
|
receivedMessages.Add(msg);
|
|
}
|
|
}
|
|
else if (e.PlcNotification.GetNotificationType() == GscPlcNotificationType.plcnPushCallbackLost)
|
|
{
|
|
var msg = $"[{DateTime.Now:HH:mm:ss}] Connection lost!";
|
|
Console.WriteLine(msg);
|
|
receivedMessages.Add(msg);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] PLC Callback error: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
// Helper function to create PLC and register callbacks
|
|
void CreatePLC()
|
|
{
|
|
if (gscServer == null) return;
|
|
|
|
gscPLC = gscServer.CreatePLC();
|
|
gscPLC.PLCCallback += OnPLCCallback;
|
|
gscPLC.OpenPushCallback();
|
|
|
|
actionDispatcher = new GscActionDispatcher();
|
|
actionDispatcher.OnCustomAction += OnCustomAction;
|
|
actionDispatcher.OnDigitalInput += OnDigitalInput;
|
|
|
|
gscPLC.SubscribeActionsAll();
|
|
gscPLC.SubscribeEventsAll();
|
|
}
|
|
|
|
// Helper function to destroy PLC
|
|
void DestroyPLC(bool connectionLost = false)
|
|
{
|
|
if (gscPLC != null)
|
|
{
|
|
if (!connectionLost)
|
|
{
|
|
gscPLC.UnsubscribeAll();
|
|
gscPLC.CloseCallback();
|
|
}
|
|
|
|
if (actionDispatcher != null)
|
|
{
|
|
actionDispatcher.Dispose();
|
|
actionDispatcher = null;
|
|
}
|
|
|
|
gscPLC.Dispose();
|
|
gscPLC = null;
|
|
}
|
|
}
|
|
|
|
// Helper function to load media channels
|
|
void LoadMediaChannels()
|
|
{
|
|
mediaChannels.Clear();
|
|
if (gscServer == null)
|
|
{
|
|
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] LoadMediaChannels: gscServer is null");
|
|
return;
|
|
}
|
|
|
|
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] LoadMediaChannels: Starting channel query...");
|
|
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] Server connected: {gscServer.IsConnected}");
|
|
|
|
try
|
|
{
|
|
var channelList = new ArrayList();
|
|
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] Calling MediaPlayerHelperFunctions.QueryMediaChannelList...");
|
|
MediaPlayerHelperFunctions.QueryMediaChannelList(gscServer, out channelList);
|
|
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] QueryMediaChannelList returned, channelList is null: {channelList == null}");
|
|
|
|
if (channelList != null)
|
|
{
|
|
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] Found {channelList.Count} total channels from server");
|
|
|
|
foreach (GscMediaChannelData channel in channelList)
|
|
{
|
|
// Include ALL channels (both active and inactive)
|
|
mediaChannels.Add(new MediaChannelInfo
|
|
{
|
|
ChannelID = channel.ChannelID,
|
|
GlobalNumber = channel.GlobalNumber,
|
|
Name = channel.Name,
|
|
Description = channel.Desc,
|
|
IsActive = channel.IsActive
|
|
});
|
|
Console.WriteLine($" - Channel {channel.ChannelID}: {channel.Name} (Active: {channel.IsActive})");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] channelList is NULL!");
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] Error loading media channels: {ex.Message}");
|
|
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] Stack trace: {ex.StackTrace}");
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// API ENDPOINTS
|
|
// ============================================================================
|
|
|
|
// Connection endpoint
|
|
app.MapPost("/connect", (ConnectRequest request) =>
|
|
{
|
|
try
|
|
{
|
|
// Disconnect existing connection
|
|
if (gscServer != null)
|
|
{
|
|
DestroyPLC();
|
|
gscServer.Disconnect(5000);
|
|
gscServer.Dispose();
|
|
gscServer = null;
|
|
}
|
|
|
|
// Create new connection
|
|
gscServer = new GscServer();
|
|
|
|
// Encode password
|
|
var encodedPassword = DBIHelperFunctions.EncodePassword(request.Password);
|
|
|
|
// Set connection parameters
|
|
using var connectParams = new GscServerConnectParams(request.Address, request.Username, encodedPassword);
|
|
gscServer.SetConnectParams(connectParams);
|
|
|
|
// Connect
|
|
var result = gscServer.Connect();
|
|
|
|
if (result == GscServerConnectResult.connectOk)
|
|
{
|
|
currentAddress = request.Address;
|
|
currentUsername = request.Username;
|
|
|
|
// Create PLC for actions/events
|
|
CreatePLC();
|
|
|
|
// Load media channels
|
|
LoadMediaChannels();
|
|
|
|
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] Connected to GeViScope at {request.Address}");
|
|
|
|
return Results.Ok(new
|
|
{
|
|
success = true,
|
|
message = "Connected to GeViScope",
|
|
address = request.Address,
|
|
username = request.Username,
|
|
channelCount = mediaChannels.Count,
|
|
connected_at = DateTime.UtcNow
|
|
});
|
|
}
|
|
else
|
|
{
|
|
return Results.BadRequest(new
|
|
{
|
|
success = false,
|
|
error = "Connection failed",
|
|
message = result.ToString()
|
|
});
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return Results.BadRequest(new
|
|
{
|
|
success = false,
|
|
error = "Connection error",
|
|
message = ex.Message,
|
|
stack_trace = ex.StackTrace
|
|
});
|
|
}
|
|
});
|
|
|
|
// Disconnect endpoint
|
|
app.MapPost("/disconnect", () =>
|
|
{
|
|
try
|
|
{
|
|
DestroyPLC();
|
|
|
|
if (gscServer != null)
|
|
{
|
|
gscServer.Disconnect(5000);
|
|
gscServer.Dispose();
|
|
gscServer = null;
|
|
}
|
|
|
|
currentAddress = null;
|
|
currentUsername = null;
|
|
mediaChannels.Clear();
|
|
|
|
return Results.Ok(new
|
|
{
|
|
success = true,
|
|
message = "Disconnected successfully"
|
|
});
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return Results.BadRequest(new
|
|
{
|
|
success = false,
|
|
error = "Disconnect error",
|
|
message = ex.Message
|
|
});
|
|
}
|
|
});
|
|
|
|
// Status endpoint
|
|
app.MapGet("/status", () =>
|
|
{
|
|
return Results.Ok(new
|
|
{
|
|
is_connected = gscServer != null,
|
|
address = currentAddress,
|
|
username = currentUsername,
|
|
channel_count = mediaChannels.Count
|
|
});
|
|
});
|
|
|
|
// Get media channels
|
|
app.MapGet("/channels", () =>
|
|
{
|
|
if (gscServer == null)
|
|
{
|
|
return Results.BadRequest(new { error = "Not connected" });
|
|
}
|
|
|
|
return Results.Ok(new
|
|
{
|
|
count = mediaChannels.Count,
|
|
channels = mediaChannels
|
|
});
|
|
});
|
|
|
|
// Refresh media channels
|
|
app.MapPost("/channels/refresh", () =>
|
|
{
|
|
if (gscServer == null)
|
|
{
|
|
return Results.BadRequest(new { error = "Not connected" });
|
|
}
|
|
|
|
LoadMediaChannels();
|
|
|
|
return Results.Ok(new
|
|
{
|
|
count = mediaChannels.Count,
|
|
channels = mediaChannels
|
|
});
|
|
});
|
|
|
|
// Send action (generic)
|
|
app.MapPost("/action", (ActionRequest request) =>
|
|
{
|
|
try
|
|
{
|
|
if (gscServer == null || gscPLC == null)
|
|
{
|
|
return Results.BadRequest(new { error = "Not connected" });
|
|
}
|
|
|
|
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] Sending action: {request.Action}");
|
|
|
|
var action = GscAction.Decode(request.Action);
|
|
if (action != null)
|
|
{
|
|
gscPLC.SendAction(action);
|
|
action.Dispose();
|
|
|
|
var logMsg = $"[{DateTime.Now:HH:mm:ss}] SENT: {request.Action}";
|
|
receivedMessages.Add(logMsg);
|
|
|
|
return Results.Ok(new
|
|
{
|
|
success = true,
|
|
message = "Action sent",
|
|
action = request.Action
|
|
});
|
|
}
|
|
else
|
|
{
|
|
return Results.BadRequest(new
|
|
{
|
|
success = false,
|
|
error = "Invalid action",
|
|
message = "Could not decode action string"
|
|
});
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return Results.BadRequest(new
|
|
{
|
|
success = false,
|
|
error = "Action error",
|
|
message = ex.Message
|
|
});
|
|
}
|
|
});
|
|
|
|
// Send CustomAction
|
|
app.MapPost("/custom-action", (CustomActionRequest request) =>
|
|
{
|
|
try
|
|
{
|
|
if (gscServer == null || gscPLC == null)
|
|
{
|
|
return Results.BadRequest(new { error = "Not connected" });
|
|
}
|
|
|
|
var action = new GscAct_CustomAction(request.TypeId, request.Text ?? "");
|
|
gscPLC.SendAction(action);
|
|
action.Dispose();
|
|
|
|
var logMsg = $"[{DateTime.Now:HH:mm:ss}] SENT: CustomAction({request.TypeId}, \"{request.Text}\")";
|
|
receivedMessages.Add(logMsg);
|
|
|
|
return Results.Ok(new
|
|
{
|
|
success = true,
|
|
message = "CustomAction sent",
|
|
type_id = request.TypeId,
|
|
text = request.Text
|
|
});
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return Results.BadRequest(new
|
|
{
|
|
success = false,
|
|
error = "CustomAction error",
|
|
message = ex.Message
|
|
});
|
|
}
|
|
});
|
|
|
|
// CrossSwitch - video routing
|
|
app.MapPost("/crossswitch", (CrossSwitchRequest request) =>
|
|
{
|
|
try
|
|
{
|
|
if (gscServer == null || gscPLC == null)
|
|
{
|
|
return Results.BadRequest(new { error = "Not connected" });
|
|
}
|
|
|
|
var actionStr = $"CrossSwitch({request.VideoInput}, {request.VideoOutput}, {request.SwitchMode})";
|
|
var action = GscAction.Decode(actionStr);
|
|
if (action != null)
|
|
{
|
|
gscPLC.SendAction(action);
|
|
action.Dispose();
|
|
|
|
var logMsg = $"[{DateTime.Now:HH:mm:ss}] SENT: {actionStr}";
|
|
receivedMessages.Add(logMsg);
|
|
|
|
return Results.Ok(new
|
|
{
|
|
success = true,
|
|
message = "CrossSwitch sent",
|
|
video_input = request.VideoInput,
|
|
video_output = request.VideoOutput,
|
|
switch_mode = request.SwitchMode
|
|
});
|
|
}
|
|
else
|
|
{
|
|
return Results.BadRequest(new { success = false, error = "Failed to create CrossSwitch action" });
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return Results.BadRequest(new
|
|
{
|
|
success = false,
|
|
error = "CrossSwitch error",
|
|
message = ex.Message
|
|
});
|
|
}
|
|
});
|
|
|
|
// PTZ Camera Control - Pan
|
|
app.MapPost("/camera/pan", (CameraPanRequest request) =>
|
|
{
|
|
try
|
|
{
|
|
if (gscServer == null || gscPLC == null)
|
|
{
|
|
return Results.BadRequest(new { error = "Not connected" });
|
|
}
|
|
|
|
var direction = request.Direction.ToLower() == "left" ? "Left" : "Right";
|
|
var actionStr = $"CameraPan{direction}({request.Camera}, {request.Speed})";
|
|
var action = GscAction.Decode(actionStr);
|
|
if (action != null)
|
|
{
|
|
gscPLC.SendAction(action);
|
|
action.Dispose();
|
|
|
|
return Results.Ok(new
|
|
{
|
|
success = true,
|
|
message = $"Camera pan {direction}",
|
|
camera = request.Camera,
|
|
speed = request.Speed
|
|
});
|
|
}
|
|
|
|
return Results.BadRequest(new { error = "Failed to create pan action" });
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return Results.BadRequest(new { error = ex.Message });
|
|
}
|
|
});
|
|
|
|
// PTZ Camera Control - Tilt
|
|
app.MapPost("/camera/tilt", (CameraTiltRequest request) =>
|
|
{
|
|
try
|
|
{
|
|
if (gscServer == null || gscPLC == null)
|
|
{
|
|
return Results.BadRequest(new { error = "Not connected" });
|
|
}
|
|
|
|
var direction = request.Direction.ToLower() == "up" ? "Up" : "Down";
|
|
var actionStr = $"CameraTilt{direction}({request.Camera}, {request.Speed})";
|
|
var action = GscAction.Decode(actionStr);
|
|
if (action != null)
|
|
{
|
|
gscPLC.SendAction(action);
|
|
action.Dispose();
|
|
|
|
return Results.Ok(new
|
|
{
|
|
success = true,
|
|
message = $"Camera tilt {direction}",
|
|
camera = request.Camera,
|
|
speed = request.Speed
|
|
});
|
|
}
|
|
|
|
return Results.BadRequest(new { error = "Failed to create tilt action" });
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return Results.BadRequest(new { error = ex.Message });
|
|
}
|
|
});
|
|
|
|
// PTZ Camera Control - Zoom
|
|
app.MapPost("/camera/zoom", (CameraZoomRequest request) =>
|
|
{
|
|
try
|
|
{
|
|
if (gscServer == null || gscPLC == null)
|
|
{
|
|
return Results.BadRequest(new { error = "Not connected" });
|
|
}
|
|
|
|
var direction = request.Direction.ToLower() == "in" ? "In" : "Out";
|
|
var actionStr = $"CameraZoom{direction}({request.Camera}, {request.Speed})";
|
|
var action = GscAction.Decode(actionStr);
|
|
if (action != null)
|
|
{
|
|
gscPLC.SendAction(action);
|
|
action.Dispose();
|
|
|
|
return Results.Ok(new
|
|
{
|
|
success = true,
|
|
message = $"Camera zoom {direction}",
|
|
camera = request.Camera,
|
|
speed = request.Speed
|
|
});
|
|
}
|
|
|
|
return Results.BadRequest(new { error = "Failed to create zoom action" });
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return Results.BadRequest(new { error = ex.Message });
|
|
}
|
|
});
|
|
|
|
// PTZ Camera Control - Stop all movement
|
|
app.MapPost("/camera/stop", (CameraStopRequest request) =>
|
|
{
|
|
try
|
|
{
|
|
if (gscServer == null || gscPLC == null)
|
|
{
|
|
return Results.BadRequest(new { error = "Not connected" });
|
|
}
|
|
|
|
var actionStr = $"CameraStopAll({request.Camera})";
|
|
var action = GscAction.Decode(actionStr);
|
|
if (action != null)
|
|
{
|
|
gscPLC.SendAction(action);
|
|
action.Dispose();
|
|
|
|
return Results.Ok(new
|
|
{
|
|
success = true,
|
|
message = "Camera stopped",
|
|
camera = request.Camera
|
|
});
|
|
}
|
|
|
|
return Results.BadRequest(new { error = "Failed to create stop action" });
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return Results.BadRequest(new { error = ex.Message });
|
|
}
|
|
});
|
|
|
|
// PTZ Camera Control - Go to preset
|
|
app.MapPost("/camera/preset", (CameraPresetRequest request) =>
|
|
{
|
|
try
|
|
{
|
|
if (gscServer == null || gscPLC == null)
|
|
{
|
|
return Results.BadRequest(new { error = "Not connected" });
|
|
}
|
|
|
|
var actionStr = $"CameraGotoPreset({request.Camera}, {request.Preset})";
|
|
var action = GscAction.Decode(actionStr);
|
|
if (action != null)
|
|
{
|
|
gscPLC.SendAction(action);
|
|
action.Dispose();
|
|
|
|
return Results.Ok(new
|
|
{
|
|
success = true,
|
|
message = $"Camera going to preset {request.Preset}",
|
|
camera = request.Camera,
|
|
preset = request.Preset
|
|
});
|
|
}
|
|
|
|
return Results.BadRequest(new { error = "Failed to create preset action" });
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return Results.BadRequest(new { error = ex.Message });
|
|
}
|
|
});
|
|
|
|
// Digital Output - Close contact
|
|
app.MapPost("/digital-io/close", (DigitalContactRequest request) =>
|
|
{
|
|
try
|
|
{
|
|
if (gscServer == null || gscPLC == null)
|
|
{
|
|
return Results.BadRequest(new { error = "Not connected" });
|
|
}
|
|
|
|
var actionStr = $"CloseDigitalOutput({request.ContactId})";
|
|
var action = GscAction.Decode(actionStr);
|
|
if (action != null)
|
|
{
|
|
gscPLC.SendAction(action);
|
|
action.Dispose();
|
|
|
|
return Results.Ok(new
|
|
{
|
|
success = true,
|
|
message = "Digital output closed",
|
|
contact_id = request.ContactId
|
|
});
|
|
}
|
|
|
|
return Results.BadRequest(new { error = "Failed to create action" });
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return Results.BadRequest(new { error = ex.Message });
|
|
}
|
|
});
|
|
|
|
// Digital Output - Open contact
|
|
app.MapPost("/digital-io/open", (DigitalContactRequest request) =>
|
|
{
|
|
try
|
|
{
|
|
if (gscServer == null || gscPLC == null)
|
|
{
|
|
return Results.BadRequest(new { error = "Not connected" });
|
|
}
|
|
|
|
var actionStr = $"OpenDigitalOutput({request.ContactId})";
|
|
var action = GscAction.Decode(actionStr);
|
|
if (action != null)
|
|
{
|
|
gscPLC.SendAction(action);
|
|
action.Dispose();
|
|
|
|
return Results.Ok(new
|
|
{
|
|
success = true,
|
|
message = "Digital output opened",
|
|
contact_id = request.ContactId
|
|
});
|
|
}
|
|
|
|
return Results.BadRequest(new { error = "Failed to create action" });
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return Results.BadRequest(new { error = ex.Message });
|
|
}
|
|
});
|
|
|
|
// Get message log
|
|
app.MapGet("/messages", () =>
|
|
{
|
|
return Results.Ok(new
|
|
{
|
|
count = receivedMessages.Count,
|
|
messages = receivedMessages.TakeLast(100).ToList()
|
|
});
|
|
});
|
|
|
|
// Clear message log
|
|
app.MapPost("/messages/clear", () =>
|
|
{
|
|
receivedMessages.Clear();
|
|
return Results.Ok(new { message = "Message log cleared" });
|
|
});
|
|
|
|
Console.WriteLine("========================================");
|
|
Console.WriteLine("GeViScope Bridge starting on port 7720");
|
|
Console.WriteLine("========================================");
|
|
|
|
app.Run("http://localhost:7720");
|
|
|
|
// Request/Response Models
|
|
record ConnectRequest(string Address, string Username, string Password);
|
|
record ActionRequest(string Action);
|
|
record CustomActionRequest(int TypeId, string? Text);
|
|
record CrossSwitchRequest(int VideoInput, int VideoOutput, int SwitchMode = 0);
|
|
record CameraPanRequest(int Camera, string Direction, int Speed = 50);
|
|
record CameraTiltRequest(int Camera, string Direction, int Speed = 50);
|
|
record CameraZoomRequest(int Camera, string Direction, int Speed = 50);
|
|
record CameraStopRequest(int Camera);
|
|
record CameraPresetRequest(int Camera, int Preset);
|
|
record DigitalContactRequest(int ContactId);
|
|
|
|
class MediaChannelInfo
|
|
{
|
|
public long ChannelID { get; set; }
|
|
public long GlobalNumber { get; set; }
|
|
public string Name { get; set; } = "";
|
|
public string Description { get; set; } = "";
|
|
public bool IsActive { get; set; }
|
|
}
|