Skip to content

plugin sdk queries

Andre Lafleur edited this page May 10, 2026 · 10 revisions

About plugin queries

Plugins respond to queries from clients (Security Desk, Config Tool, Web SDK). Understanding the query flow is essential for proper implementation.

Query flow architecture

sequenceDiagram
    participant Client as Client (Security Desk)
    participant Directory
    participant Host as Plugin Host
    participant Plugin

    Client->>Directory: Send ReportQuery
    Directory->>Host: Distribute to roles with supported report types
    Host->>Host: Filter based on SupportedQueries and SupportedCustomReports
    Host->>Plugin: OnQueryReceived()
    Plugin->>Plugin: Process query
    Plugin-->>Directory: Send results
    Directory-->>Client: Return results
Loading

Key points

  • Directory distributes queries to roles that registered supported report types
  • SDK filters queries using SupportedQueries and SupportedCustomReports
  • Plugin receives only queries it supports
  • Plugin must send results and completion

Declaring query support

Plugins must explicitly declare which query types they support by overriding SupportedQueries:

public override List<ReportQueryType> SupportedQueries => 
    new List<ReportQueryType>
    {
        ReportQueryType.DoorActivity,
        ReportQueryType.AuditTrails,
        ReportQueryType.Custom
    };

Important

  • Only queries in SupportedQueries are routed to your plugin
  • The SDK filters queries before calling OnQueryReceived()
  • If SupportedQueries is empty or not overridden, the plugin receives no queries
  • Custom queries require filtering by CustomReportId

Processing Queries

The query handler is the entry point for all plugin-side query processing.

OnQueryReceived()

This abstract method must be implemented by all plugins:

protected override void OnQueryReceived(ReportQueryReceivedEventArgs args)
{
    // Process query and send results
}

Method is called when

  • A query matching SupportedQueries is received
  • For Custom queries, CustomReportId matches SupportedCustomReports
  • Client is waiting for response

Query execution considerations

  • Return quickly or offload long-running work
  • Use async work for long-running queries

ReportQueryReceivedEventArgs Properties

Property Type Description
Query ReportQuery The query to process. Cast to specific type (for example, CustomQuery) as needed.
MessageId int Message identifier for this query request. Use with QuerySource to uniquely identify a query.
QuerySource Guid Unique identifier of the Application that sent the query. Combined with MessageId to uniquely identify the request.
DispatchedSystems List<Guid> Systems the query has been dispatched to.

Note

MessageId alone is not unique across multiple clients. Always use both MessageId and QuerySource together to identify a specific query request.

Query processing requirements

When OnQueryReceived() is called, you must:

  1. Don't block - Return quickly or process asynchronously
  2. Send results - Use Engine.ReportManager.SendQueryResult() for each result batch
  3. Always complete - Must call SendQueryCompleted() even if no results
  4. Handle errors gracefully - Catch exceptions and report errors

Query completion is mandatory

protected override void OnQueryReceived(ReportQueryReceivedEventArgs args)
{
    try
    {
        // Process query...
        ProcessQuery(args.Query);
    }
    finally
    {
        // ALWAYS send query completed
        Engine.ReportManager.SendQueryCompleted(
            args.MessageId, 
            args.QuerySource, 
            PluginGuid, 
            true, 
            ReportError.None, 
            string.Empty);
    }
}

If you don't call SendQueryCompleted(), the client waits until timeout.

Sending query results

Send results in one or more batches, then explicitly signal completion so the client can stop waiting.

Result Flow

flowchart TB
    A[OnQueryReceived called] --> B[Process query]
    B --> C{More results?}
    C -->|Yes| D[SendQueryResult<br/>messageId, results]
    D --> C
    C -->|No| E[SendQueryCompleted<br/>messageId, ...]
Loading

SendQueryResult()

Used to send result data to the client:

var results = new ReportQueryResults(ReportQueryType.DoorActivity)
{
    Results = dataSet, // DataSet containing result tables
    QuerySource = args.QuerySource,
    ResultSource = PluginGuid
};

Engine.ReportManager.SendQueryResult(args.MessageId, results);

Key points

  • Can be called multiple times for the same query (streaming results)
  • Each call sends a batch of data
  • Client receives results incrementally
  • Use appropriate ReportQueryResults type for query

SendQueryCompleted()

Signals that no more results will be sent:

Engine.ReportManager.SendQueryCompleted(
    args.MessageId,
    args.QuerySource,
    PluginGuid,
    true,
    ReportError.None,
    string.Empty
);

Parameters

  • successful - true if query succeeded, false if error
  • error - ReportError enumeration value
  • errorDetail - Optional error details for client

Custom Queries

For ReportQueryType.Custom, you must filter by CustomReportId:

public override List<Guid> SupportedCustomReports => 
    new List<Guid> { CustomReportPageGuid };

protected override void OnQueryReceived(ReportQueryReceivedEventArgs args)
{
    if (args.Query is CustomQuery customQuery)
    {
        if (customQuery.CustomReportId == CustomReportPageGuid)
        {
            // Handle this custom report
            HandleCustomReport(customQuery);
        }
    }
    
    Engine.ReportManager.SendQueryCompleted(...);
}

If the client report defines a custom ReportFilter, the query also includes the serialized custom filter payload in CustomQuery.FilterData. The payload format is application-defined. Use the same format on the client and server.

Custom query workflow

  1. Add ReportQueryType.Custom to SupportedQueries
  2. Override SupportedCustomReports with your report GUIDs
  3. Check CustomReportId in OnQueryReceived()
  4. Read CustomQuery.FilterData if the report defines a custom filter
  5. Only process queries matching your GUIDs

Query Cancellation

Clients can cancel queries that are taking too long, or the Directory may cancel queries that timeout:

protected override void OnQueryCancelled(ReportQueryCancelledEventArgs args)
{
    // Cancel any ongoing work for this query
    // No need to call SendQueryCompleted() for cancellations
}

ReportQueryCancelledEventArgs Properties

Property Type Description
MessageId int Message identifier of the original query request. Use with QueryId to locate the query to cancel.
QueryId Guid Unique identifier matching ReportQuery.QueryId of the original query.
SystemsToCancel IEnumerable<Guid> External systems for which this query should be cancelled. Check if your PluginGuid is in this list.

Cancellation notes

  • Cancellations are informational - no response required
  • May come from clients or from Directory timeouts
  • Use to clean up resources for long-running queries
  • Check SystemsToCancel to see if cancellation applies to your plugin

Async query processing

Don't block the query thread - process asynchronously:

protected override void OnQueryReceived(ReportQueryReceivedEventArgs args)
{
    Task.Run(() => 
    {
        try
        {
            // Long-running query processing
            var results = ProcessQuery(args.Query);
            Engine.ReportManager.SendQueryResult(args.MessageId, results);
        }
        catch (Exception ex)
        {
            Logger.TraceError(ex, "Query processing failed");
        }
        finally
        {
            Engine.ReportManager.SendQueryCompleted(
                args.MessageId, args.QuerySource, PluginGuid, 
                true, ReportError.None, string.Empty);
        }
    });
}

Supported query types

Plugins can respond to any ReportQueryType by adding it to SupportedQueries. Common types include:

Category Query Types
Access Control CardholderActivity, DoorActivity, AreaActivity, ElevatorActivity, CredentialActivity, UnitActivity, ZoneActivity, AccessPointActivity
Video CameraEvent, VideoMotionEvent, VideoFile, VideoSequence, ArchiverEvent
Intrusion IntrusionAreaActivity, IntrusionUnitActivity, IntrusionDetectionActivity
Health Health, HealthEvent, HealthStatistics
LPR LprReads, LprHits, PatrollerPositions
Custom Custom (requires SupportedCustomReports)
Other AuditTrails, ActivityTrails, EntityState, Thumbnail

See the ReportQueryType enum for the complete list.

Query performance considerations

For large datasets, structure the response so the client can start processing before the full result set is ready.

Chunking Results

For large result sets, send data in chunks:

const int ChunkSize = 1000;
var allResults = GetQueryResults(args.Query);

for (int i = 0; i < allResults.Count; i += ChunkSize)
{
    var chunk = allResults.Skip(i).Take(ChunkSize).ToList();
    var dataSet = CreateDataSet(chunk);
    
    var results = new ReportQueryResults(args.Query.ReportQueryType)
    {
        Results = dataSet,
        QuerySource = args.QuerySource,
        ResultSource = PluginGuid
    };
    
    Engine.ReportManager.SendQueryResult(args.MessageId, results);
}

See also

Platform SDK

Plugin SDK

Workspace SDK

Media SDK

Macro SDK

Web SDK

Media Gateway

Genetec Web Player

Clone this wiki locally