-
Notifications
You must be signed in to change notification settings - Fork 4
plugin sdk request manager
Plugins can handle custom requests from client applications (custom tasks, config pages, other plugins) using the RequestManager.
Client Application
↓
Sends Request<TRequest>
↓
Directory routes to plugin based on PluginGuid
↓
RequestManager calls registered handler
↓
Handler returns Response<TResponse>
↓
Response flows back to client
Key concepts:
- Request/response communication pattern (RPC-style)
- Type-safe request and response classes
- Requests routed by plugin GUID
- Multiple handlers for different request types
- Synchronous and asynchronous handlers supported
Custom configuration pages:
- Page requests current settings
- Plugin returns configuration
- Page sends updated settings
- Plugin validates and applies
Custom tasks:
- User triggers task in Security Desk
- Task sends command to plugin
- Plugin executes operation
- Returns result to task
Inter-plugin communication:
- One plugin requests data from another
- Plugins coordinate actions
- Share state or resources
Diagnostics and testing:
- Test connections
- Query internal state
- Trigger operations
- Get statistics
Simple function that returns response immediately:
protected override void OnPluginLoaded()
{
// Use Plugin's protected helper method
AddRequestHandler<GetStatusRequest, StatusResponse>(HandleGetStatus);
}
private StatusResponse HandleGetStatus(GetStatusRequest request)
{
return new StatusResponse
{
IsConnected = m_isConnected,
DeviceCount = m_devices.Count,
Uptime = DateTime.UtcNow - m_startTime
};
}When to use:
- Fast operations (< 100ms)
- No I/O required
- Simple data retrieval
- Immediate results available
Async method that can await operations:
protected override void OnPluginLoaded()
{
// Use Plugin's protected helper method
AddAsyncRequestHandler<TestConnectionRequest, TestConnectionResponse>(
HandleTestConnectionAsync);
}
private async Task<TestConnectionResponse> HandleTestConnectionAsync(
TestConnectionRequest request,
RequestManagerContext context)
{
try
{
var startTime = DateTime.UtcNow;
await TestConnectionToExternalSystemAsync(
request.Timeout,
context.RequestCancellationToken);
var elapsed = DateTime.UtcNow - startTime;
return new TestConnectionResponse
{
Success = true,
ResponseTime = elapsed
};
}
catch (Exception ex)
{
return new TestConnectionResponse
{
Success = false,
Error = ex.Message
};
}
}When to use:
- Slow operations (> 100ms)
- I/O operations (database, network)
- Long-running tasks
- Need async/await
- Need cancellation support or user context
The RequestManagerContext parameter provides information about the request origin and supports cancellation:
public class RequestManagerContext
{
// Cancellation support
CancellationToken RequestCancellationToken { get; }
TimeSpan RequestTimeout { get; }
// Request source information
Guid SourceApplication { get; } // Application that sent request
Guid SourceUser { get; } // User who initiated request
string SourceUsername { get; } // Username of requester
// Request metadata
DateTime ReceptionTimestamp { get; } // When request was received (UTC)
IReadOnlyCollection<Guid> DestinationEntities { get; } // Target entities
}Cancellation support:
private async Task<Response> HandleRequestAsync(
Request request,
RequestManagerContext context)
{
// Pass cancellation token to async operations
var data = await FetchDataAsync(context.RequestCancellationToken);
// Check if cancelled
if (context.RequestCancellationToken.IsCancellationRequested)
return new Response { Cancelled = true };
return new Response { Data = data };
}User-specific logic:
private Response HandleRequest(Request request, RequestManagerContext context)
{
var user = Engine.GetEntity<User>(context.SourceUser);
Logger.TraceInformation($"Request from {context.SourceUsername} received at {context.ReceptionTimestamp}");
// Apply user-specific permissions or behavior
if (user.HasPrivilege(MyPrivilege))
{
return ProcessRequest(request);
}
return new Response { Error = "Insufficient privileges" };
}Timeout handling:
private async Task<Response> HandleRequestAsync(
Request request,
RequestManagerContext context)
{
Logger.TraceDebug($"Request timeout: {context.RequestTimeout}");
try
{
// Use the timeout from context
using var cts = CancellationTokenSource.CreateLinkedTokenSource(
context.RequestCancellationToken);
cts.CancelAfter(context.RequestTimeout);
return await ProcessWithTimeoutAsync(request, cts.Token);
}
catch (OperationCanceledException)
{
return new Response { Error = "Request timed out" };
}
}Callback pattern for complex scenarios:
protected override void OnPluginLoaded()
{
// Use Plugin's protected helper method
AddRequestHandler<ComplexRequest, ComplexResponse>(HandleComplexRequest);
}
private void HandleComplexRequest(
ComplexRequest request,
RequestCompletion<ComplexResponse> completion)
{
// Process in background
Task.Run(async () =>
{
try
{
var result = await ProcessComplexOperationAsync(request);
completion.SetResult(new ComplexResponse { Result = result });
}
catch (Exception ex)
{
completion.SetException(ex);
}
});
}When to use:
- Need manual control over completion
- Complex error handling
- Progress reporting scenarios
- Legacy async patterns
protected override void OnPluginLoaded()
{
// Register multiple handlers using Plugin's protected helper methods
AddRequestHandler<GetConfigRequest, PluginConfig>(HandleGetConfig);
AddRequestHandler<SetConfigRequest, SetConfigResponse>(HandleSetConfig);
AddAsyncRequestHandler<TestConnectionRequest, TestConnectionResponse>(
HandleTestConnectionAsync);
}protected override void Dispose(bool disposing)
{
if (disposing)
{
// Remove handlers using Plugin's protected helper methods
RemoveRequestHandler<GetConfigRequest, PluginConfig>(HandleGetConfig);
RemoveRequestHandler<SetConfigRequest, SetConfigResponse>(HandleSetConfig);
RemoveAsyncRequestHandler<TestConnectionRequest, TestConnectionResponse>(
HandleTestConnectionAsync);
}
}Important
- Register handlers in
OnPluginLoaded()
- Remove handlers in
Dispose() - Match handler type when removing (sync vs async)
Requests are routed by:
-
Request type - The generic type
TRequest - Plugin GUID - Specified in client request
- Handler registration - Plugin must register handler for type
Multiple plugins can handle the same request type - routing is by PluginGuid.
Define request and response classes:
[Serializable]
public class GetDeviceListRequest
{
public bool IncludeOffline { get; set; }
public DateTime Since { get; set; }
}
[Serializable]
public class DeviceListResponse
{
public List<DeviceInfo> Devices { get; set; }
public int TotalCount { get; set; }
}
[Serializable]
public class DeviceInfo
{
public Guid DeviceGuid { get; set; }
public string Name { get; set; }
public bool IsOnline { get; set; }
}Requirements:
- Must be
[Serializable] - Must be able to serialize/deserialize (JSON or binary)
- Keep classes simple (POCOs)
- Avoid complex object graphs
// Get configuration
AddRequestHandler<GetConfigRequest, PluginConfig>(request => LoadConfiguration());
// Set configuration
AddRequestHandler<SetConfigRequest, SetConfigResponse>(request =>
{
try
{
if (!ValidateConfiguration(request.Config, out string error))
{
return new SetConfigResponse
{
Success = false,
Error = error
};
}
UpdateConfiguration(request.Config);
ApplyConfiguration(request.Config);
return new SetConfigResponse { Success = true };
}
catch (Exception ex)
{
Logger.TraceError(ex, "Failed to update configuration");
return new SetConfigResponse
{
Success = false,
Error = ex.Message
};
}
});AddAsyncRequestHandler<TestConnectionRequest, TestConnectionResponse>(
async (request, context) =>
{
var sw = Stopwatch.StartNew();
try
{
await ConnectToExternalSystemAsync(
request.Timeout,
context.RequestCancellationToken);
return new TestConnectionResponse
{
Success = true,
Message = "Connection successful",
ResponseTime = sw.Elapsed
};
}
catch (TimeoutException)
{
return new TestConnectionResponse
{
Success = false,
Message = "Connection timeout",
ResponseTime = sw.Elapsed
};
}
catch (Exception ex)
{
return new TestConnectionResponse
{
Success = false,
Message = ex.Message,
ResponseTime = sw.Elapsed
};
}
});AddAsyncRequestHandler<ExecuteCommandRequest, CommandResponse>(
async (request, context) =>
{
try
{
var result = await ExecuteCommandOnHardwareAsync(
request.DeviceGuid,
request.Command,
request.Parameters,
context.RequestCancellationToken);
return new CommandResponse
{
Success = true,
Result = result
};
}
catch (Exception ex)
{
Logger.TraceError(ex, "Command execution failed");
return new CommandResponse
{
Success = false,
Error = ex.Message
};
}
});AddRequestHandler<GetDeviceStateRequest, DeviceStateResponse>(request =>
{
var device = m_devices.FirstOrDefault(d => d.Guid == request.DeviceGuid);
if (device == null)
{
return new DeviceStateResponse { Found = false };
}
return new DeviceStateResponse
{
Found = true,
State = device.State,
LastUpdate = device.LastUpdate,
Properties = device.GetProperties()
};
});private SetConfigResponse HandleSetConfig(SetConfigRequest request)
{
try
{
ValidateAndApplyConfig(request.Config);
return new SetConfigResponse { Success = true };
}
catch (ValidationException ex)
{
return new SetConfigResponse
{
Success = false,
Error = ex.Message,
ValidationErrors = ex.Errors
};
}
catch (Exception ex)
{
Logger.TraceError(ex, "Configuration update failed");
return new SetConfigResponse
{
Success = false,
Error = "Internal error occurred"
};
}
}private StatusResponse HandleGetStatus(GetStatusRequest request)
{
if (!m_initialized)
{
throw new InvalidOperationException("Plugin not initialized");
}
return new StatusResponse { ... };
}Exception handling:
- Exceptions are serialized and sent to client
- Client receives exception details
- Use for unexpected errors only
- Prefer returning error in response for expected errors
Request handlers are called on the engine thread:
private StatusResponse HandleGetStatus(GetStatusRequest request)
{
// Safe to access Engine and plugin state
var role = Engine.GetEntity<Role>(PluginGuid);
return new StatusResponse
{
IsConnected = m_isConnected,
Configuration = LoadConfiguration()
};
}If handler spawns async work:
private void HandleLongOperation(
LongOperationRequest request,
RequestCompletion<LongOperationResponse> completion)
{
Task.Run(async () =>
{
var result = await DoLongWorkAsync();
// Queue back to engine thread for Engine access
Engine.QueueUpdate(() =>
{
UpdateEntityState(result);
completion.SetResult(new LongOperationResponse { ... });
});
});
}- Plugin SDK Overview - Plugin architecture
- Plugin SDK Threading - Threading for async request handlers
- Plugin SDK Lifecycle - When to register handlers
- Plugin SDK Configuration - Configuration request pattern
- Logging - Request logging
-
Security Center SDK Developer Guide Overview of the SDK framework and how to build integrations with Security Center.
-
Platform SDK
- Platform SDK Overview Introduction to the Platform SDK and core concepts.
- SDK Certificates Details certificates, licensing, and connection validation.
- Entity Guide Explains the core entity model, inheritance, and how to work with entities.
- Entity Cache Guide Describes the engine's local entity cache and synchronization.
- SDK Transactions Covers batching operations for performance and consistency.
- ReportManager Querying entities and activity data from Security Center.
- Events and Actions Subscribing to events and handling actions.
- Logging with the Genetec SDK How to configure logging, diagnostics, and debug methods.
- Referencing SDK Assemblies Best practices for referencing assemblies and resolving them at runtime.
- SDK Compatibility Guide Understanding backward compatibility and versioning in the SDK.
-
Plugin SDK
- Plugin SDK Overview Introduction to plugin architecture and capabilities.
- Plugin SDK Certificates SDK certificate requirements for plugin roles.
- Plugin SDK Lifecycle Initialization and disposal patterns.
- Plugin SDK Threading Threading model, QueueUpdate, and async patterns.
- Plugin SDK Configuration Configuration storage and monitoring.
- Plugin SDK Restricted Configuration Secure credential storage and admin-only configuration.
- Plugin SDK Database Database integration and schema management.
- Plugin SDK Events Event subscription and handling.
- Plugin SDK Queries Query processing and response handling.
- Plugin SDK Request Manager Request/response communication with clients.
- Plugin SDK Entity Ownership Understanding plugin-owned entities, running state management, and ownership release.
- Plugin SDK Entity Mappings Using EntityMappings for plugin-specific configuration and external system integration.
- Plugin SDK State Management Reporting plugin health and diagnostics.
- Plugin SDK Server Management High availability and server failover.
- Custom Privileges Defining and enforcing custom privileges.
- Resolving Non-SDK Assemblies Handling third-party dependencies in plugins and workspace modules.
- Deploying Plugins Registering and deploying plugins and workspace modules.
-
Workspace SDK
- Workspace SDK Overview Introduction to client-side UI extensions for Security Desk and Config Tool.
- Creating Modules Module lifecycle, registration patterns, and assembly resolution.
- Tasks and Pages Menu items, page content, and navigation.
- Components Dashboard widgets, tiles, maps, credentials, and content builders.
- Tile Extensions Custom tile widgets, views, and properties panels.
- Services Built-in services for dialogs, maps, alarms, badges, and more.
- Contextual Actions Right-click context menu extensions.
- Options Extensions Custom settings pages in application preferences.
- Monitors Multi-monitor support and shared components.
- Commands Command execution, evaluation, and interception.
- Extending Events Adding custom fields to Security Center events.
- Map Extensions Custom map objects, layers, and providers.
- Timeline Providers Custom timeline event sources for video playback.
-
Macro SDK
- Macro SDK Overview How macros work, creating and configuring macro entities, automation, and monitoring.
- Macro SDK Developer Guide Developing macro code with the UserMacro class and Security Center SDK.
-
- Getting Started Setup, authentication, and basic configuration for the Web SDK.
- Referencing Entities Entity discovery, search capabilities, and parameter formats.
- Entity Operations CRUD operations, multi-value fields, and method execution.
- Partitions Managing partitions, entity membership, and user access control.
- Custom Fields Creating, reading, writing, and filtering custom entity fields.
- Custom Card Formats Managing custom credential card format definitions.
- Actions Control operations for doors, cameras, macros, and notifications.
- Events and Alarms Real-time event monitoring, alarm monitoring, and custom events.
- Incidents Incident management, creation, and attachment handling.
- Reports Activity reports, entity queries, and historical data retrieval.
- Performance Guide Optimization tips and best practices for efficient API usage.
- Reference Entity GUIDs, EntityType enumeration, and EventType enumeration.
- Under the Hood Technical architecture, query reflection, and SDK internals.
- Troubleshooting Common error resolution and debugging techniques.
- Media Gateway Guide Setup and configuration of the Media Gateway role for video streaming.
- Web Player Guide Complete guide to integrating GWP for live and playback video streaming.
- Web Player API Reference Full API documentation with interfaces, methods, properties, and events.
- Web Player Sample Application Comprehensive demo showcasing all GWP features with timeline and PTZ controls.
- Genetec Web Player Multiplexing Sample Multi-camera grid demo using a shared WebSocket connection.