Skip to content

plugin sdk queries

Andre Lafleur edited this page Dec 12, 2025 · 1 revision

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

Client (Security Desk)
    ↓
Sends ReportQuery
    ↓
Directory
    ↓
Distributes to roles based on SupportedQueries
    ↓
Plugin Host receives query
    ↓
Filters based on SupportedQueries
    ↓
Calls Plugin.OnQueryReceived()
    ↓
Plugin processes and sends results
    ↓
Results flow back through Directory to Client

Key points:

  • Directory broadcasts queries to all roles
  • Plugin Host filters based on SupportedQueries
  • Plugin receives only queries it supports
  • Plugin must send results and completion
  • Queries are processed on a dedicated thread

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 Plugin Host automatically filters queries before calling OnQueryReceived()
  • If SupportedQueries is empty or not overridden, plugin receives no queries
  • Custom queries require additional filtering by CustomReportId

Processing Queries

OnQueryReceived()

This abstract method must be implemented by all plugins:

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

Method is called when:

  • A query matching SupportedQueries is received
  • Query has been filtered by the Plugin Host
  • Client is waiting for response

Query thread context:

  • Called on a dedicated query processing thread
  • Must not block for long periods
  • Should process quickly or delegate to async task

ReportQueryReceivedEventArgs Properties

Property Type Description
Query ReportQuery The query to process. Cast to specific type (e.g., 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, 
            success: true, 
            ReportError.None, 
            string.Empty);
    }
}

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

Sending Query Results

Result Flow

OnQueryReceived called
    ↓
Process query (may take time)
    ↓
For each batch of results:
    SendQueryResult(messageId, results)
    ↓
When all results sent:
    SendQueryCompleted(messageId, ...)

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,
    Succeeded = true
};

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(
    messageId: args.MessageId,
    querySource: args.QuerySource,
    resultSource: PluginGuid,
    success: true,
    error: ReportError.None,
    errorMessage: string.Empty
);

Parameters:

  • success - true if query succeeded, false if error
  • error - ReportError enumeration value
  • errorMessage - 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(...);
}

Custom query workflow:

  1. Add ReportQueryType.Custom to SupportedQueries
  2. Override SupportedCustomReports with your report GUIDs
  3. Check CustomReportId in OnQueryReceived()
  4. 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, 
                success: 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

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,
        Succeeded = true
    };
    
    Engine.ReportManager.SendQueryResult(args.MessageId, results);
}

Related Guides

Security Center SDK


Macro SDK Developer Guide


Web SDK Developer Guide

  • 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 Developer Guide


Web Player Developer Guide

Clone this wiki locally