feat: GeViScope SDK integration with C# Bridge and Flutter app
- 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>
This commit is contained in:
752
geviscope-bridge/GeViScopeBridge/Program.cs
Normal file
752
geviscope-bridge/GeViScopeBridge/Program.cs
Normal file
@@ -0,0 +1,752 @@
|
||||
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; }
|
||||
}
|
||||
Reference in New Issue
Block a user