-
Notifications
You must be signed in to change notification settings - Fork 6
workspace sdk timeline providers
Timeline providers add custom event markers to Security Desk's playback timeline. Use them when operators reviewing video also need related events, such as alarms, access activity, or analytics detections, in the same time range.
A timeline provider extension has two parts: a TimelineProviderBuilder component that you register with Workspace.Components, and a TimelineProvider instance that answers timeline queries and can push real-time updates. The example in this guide uses the public alarm sample and shows how to display active alarms as timeline markers and remove them when alarms are acknowledged.
Timeline providers follow a lifecycle managed by Security Desk:
-
Registration: Your module creates a
TimelineProviderBuilder, callsInitialize(Workspace), and registers it withWorkspace.Components. -
Provider instantiation: When the timeline needs your provider, the system calls your builder's
CreateProvider()method. -
Querying: When the user navigates the timeline, the system calls your
Query()method with a time range. Your implementation queries historical data and callsInsertEvent()orInsertEvents()to add markers. -
Real-time updates: As SDK events occur, your event handlers call
InsertEvent()orRemoveEvent()to update the timeline dynamically.
TimelineProviderBuilder does not expose an IsSupported method. If your provider only applies to certain content, inspect contentGroup.Current inside Query() and return without inserting events when the current content is not relevant.
Register the builder in your module:
public class SampleModule : Module
{
private AlarmTimelineProviderBuilder m_builder;
public override void Load()
{
if (Workspace.ApplicationType == ApplicationType.SecurityDesk)
{
m_builder = new AlarmTimelineProviderBuilder();
m_builder.Initialize(Workspace);
Workspace.Components.Register(m_builder);
}
}
public override void Unload()
{
if (m_builder != null)
{
Workspace.Components.Unregister(m_builder);
m_builder = null;
}
}
}The Initialize(Workspace) call is on the builder, not the provider. The builder's CreateProvider() method is called by the system when needed. Keep a module-level reference so Unload() can unregister the component.
Note
Timeline providers require a certificate file to load. See About Workspace SDK certificates for certificate requirements.
Create a timeline provider by inheriting from TimelineProviderBuilder:
public class AlarmTimelineProviderBuilder : TimelineProviderBuilder
{
public override string Name => nameof(AlarmTimelineProviderBuilder);
public override string Title => "Active Alarms";
public override Guid UniqueId { get; } = new Guid("...");
public override TimelineProvider CreateProvider()
{
return new AlarmTimelineProvider(Workspace);
}
}| Member | Type | Description |
|---|---|---|
Name |
string | Component name |
Title |
string | Display title in UI |
Priority |
int | Display order in the timeline provider list. Lower values appear first. |
UniqueId |
Guid | Unique identifier |
CreateProvider() |
TimelineProvider | Factory method for provider instances |
The TimelineProvider class supplies timeline events. It has a single public entry point, Query(...), plus protected helper methods for managing the timeline cache and pushing updates.
public class AlarmTimelineProvider : TimelineProvider
{
private readonly Workspace m_workspace;
public AlarmTimelineProvider(Workspace workspace)
{
m_workspace = workspace;
m_workspace.Sdk.AlarmAcknowledged += OnAlarmAcknowledged;
}
public override void Query(ContentGroup contentGroup, DateTime startTime, DateTime endTime)
{
Task.Run(async () =>
{
var query = (AlarmActivityQuery)m_workspace.Sdk.ReportManager.CreateReportQuery(ReportType.AlarmActivity);
query.TriggeredTimeRange.SetTimeRange(startTime, endTime);
query.States.Add(AlarmState.Active);
query.States.Add(AlarmState.AknowledgeRequired);
query.States.Add(AlarmState.SourceConditionInvestigating);
QueryCompletedEventArgs result = await Task.Factory.FromAsync(query.BeginQuery, query.EndQuery, null);
var events = result.Data.AsEnumerable().Select(row =>
{
var alarmGuid = row.Field<Guid>(AlarmActivityQuery.AlarmColumnName);
var instanceId = row.Field<int>(AlarmActivityQuery.InstanceIdColumnName);
var triggerTime = row.Field<DateTime>(AlarmActivityQuery.TriggerTimeColumnName);
return new AlarmTimelineEvent(alarmGuid, instanceId, triggerTime);
});
InsertEvents(events);
});
}
private void OnAlarmAcknowledged(object sender, AlarmAcknowledgedEventArgs e)
{
foreach (AlarmTimelineEvent timelineEvent in GetEvents().OfType<AlarmTimelineEvent>().Where(t => t.AlarmGuid == e.AlarmGuid && t.InstanceId == e.InstanceId))
{
RemoveEvent(timelineEvent);
}
}
}| Member | Access | Description |
|---|---|---|
Query(ContentGroup, DateTime, DateTime) |
public abstract | Query events for the current content and time range |
InsertEvent(TimelineEvent) |
protected | Add a single event to the timeline |
InsertEvents(IEnumerable<TimelineEvent>) |
protected | Add multiple events to the timeline |
GetEvents() |
protected | Retrieve the provider's cached events |
RemoveEvent(TimelineEvent) |
protected | Remove an event from the timeline |
ClearCache() |
protected | Clear all cached events |
The base class also exposes OnQueryCompleted(), but the current Workspace timeline adapter and the public DAP alarm sample do not rely on it.
Create custom event types for display on the timeline by inheriting from TimelineEvent and implementing GetVisual:
public class AlarmTimelineEvent : TimelineEvent
{
private static readonly Size s_size = new Size(16, 16);
private static readonly BitmapImage s_image;
static AlarmTimelineEvent()
{
s_image = EntityType.Alarm.GetIcon();
s_image.Freeze();
}
public AlarmTimelineEvent(Guid alarmGuid, int instanceId, DateTime dateTime)
: base(dateTime)
{
AlarmGuid = alarmGuid;
InstanceId = instanceId;
}
public Guid AlarmGuid { get; }
public int InstanceId { get; }
public override TimelineVisual GetVisual(Rect constraint, double msPerPixel)
{
var drawingVisual = new DrawingVisual();
DrawingContext drawingContext = drawingVisual.RenderOpen();
drawingContext.DrawImage(s_image, new Rect(new Point(constraint.X, (constraint.Height - 16) / 2), s_size));
drawingContext.Close();
return new TimelineVisual(drawingVisual)
{
AlignmentY = AlignmentY.Center,
AlignmentX = AlignmentX.Center
};
}
}The TimelineEvent base class provides:
| Member | Type | Description |
|---|---|---|
Timestamp |
DateTime | When the event occurred (required, set via constructor) |
Priority |
int | Display priority, higher values appear topmost (default: 0) |
| Method | Description |
|---|---|
GetVisual(Rect, double) |
Abstract method to create the visual representation |
Add custom properties like AlarmGuid and InstanceId to your derived class.
The base TimelineEvent equality implementation uses Timestamp and Priority. If your event type can produce multiple distinct events with the same timestamp and priority, override Equals and GetHashCode() so the timeline cache can distinguish them.
- Overview
- Connecting to Security Center
- SDK certificates
- Referencing SDK assemblies
- SDK compatibility
- Entities
- Entity cache
- Transactions
- Events
- Actions
- Security Desk
- Custom events
- ReportManager
- ReportManager query reference
- DownloadAllRelatedData and StrictResults
- Privileges
- Partitions
- Mobile credentials
- Logging
- Overview
- Certificates
- Lifecycle
- Threading
- State management
- Configuration
- Restricted configuration
- Events
- Queries
- Request manager
- Database
- Entity ownership
- Entity mappings
- Server management
- Custom privileges
- Custom entity types
- Resolving non-SDK assemblies
- Deploying plugins
- .NET 8 support
- Overview
- Certificates
- Creating modules
- Tasks
- Pages
- Components
- Tile extensions
- Services
- Contextual actions
- Options extensions
- Configuration pages
- Monitors
- Shared components
- Commands
- Extending events
- Map extensions
- Timeline providers
- Image extractors
- Credential encoders
- Credential readers
- Cardholder fields extractors
- Badge printers
- Content builders
- Dashboard widgets
- Incidents
- Logon providers
- Pinnable content builders
- Custom report pages
- Overview
- Getting started
- MediaPlayer
- VideoSourceFilter
- MediaExporter
- MediaFile
- G64 converters
- FileCryptingManager
- PlaybackSequenceQuerier
- PlaybackStreamReader
- OverlayFactory
- PtzCoordinatesManager
- AudioTransmitter
- AudioRecorder
- AnalogMonitorController
- Camera blocking
- Overview
- Getting started
- Referencing entities
- Entity operations
- About access control in the Web SDK
- About video in the Web SDK
- Users and user groups
- Partitions
- Custom fields
- Custom card formats
- Actions
- Events and alarms
- Incidents
- Reports
- Tasks
- Macros
- Custom entity types
- System endpoints
- Performance guide
- Reference
- Under the hood
- Troubleshooting