diff --git a/Samples/Workspace SDK/TimelineProviderSample/AccessControlTimelineProvider.cs b/Samples/Workspace SDK/TimelineProviderSample/AccessControlTimelineProvider.cs new file mode 100644 index 00000000..fd3af069 --- /dev/null +++ b/Samples/Workspace SDK/TimelineProviderSample/AccessControlTimelineProvider.cs @@ -0,0 +1,76 @@ +// Copyright 2026 Genetec Inc. +// Licensed under the Apache License, Version 2.0 + +namespace Genetec.Dap.CodeSamples; + +using Sdk; +using Sdk.Events.AccessPoint; +using Sdk.Queries; +using Sdk.Queries.AccessControl; +using Sdk.Workspace; +using Sdk.Workspace.Components.TimelineProvider; +using Sdk.Workspace.Pages.Contents; +using System; +using System.Data; +using System.Linq; +using System.Threading.Tasks; + +public class AccessControlTimelineProvider : TimelineProvider, IDisposable +{ + private readonly Workspace m_workspace; + + public AccessControlTimelineProvider(Workspace workspace) + { + m_workspace = workspace; + m_workspace.Sdk.EventReceived += OnEventReceived; + } + + public override void Query(ContentGroup contentGroup, DateTime startTime, DateTime endTime) + { + Task.Run(async () => + { + var query = (CardholderActivityQuery)m_workspace.Sdk.ReportManager.CreateReportQuery(ReportType.CardholderActivity); + query.TimeRange.SetTimeRange(startTime, endTime); + query.Events.Clear(); + query.Events.Add(EventType.AccessGranted); + query.Events.Add(EventType.AccessRefused); + + QueryCompletedEventArgs result = await Task.Factory.FromAsync(query.BeginQuery, query.EndQuery, null); + + var events = result.Data.AsEnumerable().Select(row => + { + var timestamp = row.Field(AccessControlReportQuery.TimestampColumnName); + var cardholderGuid = row.Field(AccessControlReportQuery.CardholderGuidColumnName); + var eventType = row.Field(AccessControlReportQuery.EventTypeColumnName); + + return new AccessTimelineEvent(cardholderGuid, timestamp, eventType == EventType.AccessGranted); + }); + + InsertEvents(events); + OnQueryCompleted(); + }); + } + + private void OnEventReceived(object sender, EventReceivedEventArgs e) + { + switch (e.EventType) + { + case EventType.AccessGranted: + case EventType.AccessRefused: + if (e.Event is AccessEvent accessEvent) + { + var timelineEvent = new AccessTimelineEvent( + accessEvent.Cardholder, + accessEvent.Timestamp, + accessEvent.Type == EventType.AccessGranted); + InsertEvent(timelineEvent); + } + break; + } + } + + public void Dispose() + { + m_workspace.Sdk.EventReceived -= OnEventReceived; + } +} diff --git a/Samples/Workspace SDK/TimelineProviderSample/AccessControlTimelineProviderBuilder.cs b/Samples/Workspace SDK/TimelineProviderSample/AccessControlTimelineProviderBuilder.cs new file mode 100644 index 00000000..da0293a0 --- /dev/null +++ b/Samples/Workspace SDK/TimelineProviderSample/AccessControlTimelineProviderBuilder.cs @@ -0,0 +1,21 @@ +// Copyright 2026 Genetec Inc. +// Licensed under the Apache License, Version 2.0 + +namespace Genetec.Dap.CodeSamples; + +using Sdk.Workspace.Components.TimelineProvider; +using System; + +public class AccessControlTimelineProviderBuilder : TimelineProviderBuilder +{ + public override string Name => nameof(AccessControlTimelineProviderBuilder); + + public override string Title => "Access Control Events"; + + public override Guid UniqueId { get; } = new Guid("8A3C5F12-9D4E-4B7A-B2C1-6E8F0A1D3C5B"); // Replace with your own unique GUID + + public override TimelineProvider CreateProvider() + { + return new AccessControlTimelineProvider(Workspace); + } +} diff --git a/Samples/Workspace SDK/TimelineProviderSample/AccessTimelineEvent.cs b/Samples/Workspace SDK/TimelineProviderSample/AccessTimelineEvent.cs new file mode 100644 index 00000000..acb51134 --- /dev/null +++ b/Samples/Workspace SDK/TimelineProviderSample/AccessTimelineEvent.cs @@ -0,0 +1,52 @@ +// Copyright 2026 Genetec Inc. +// Licensed under the Apache License, Version 2.0 + +namespace Genetec.Dap.CodeSamples; + +using Sdk; +using Sdk.Workspace.Components.TimelineProvider; +using System; +using System.Windows; +using System.Windows.Media; + +public class AccessTimelineEvent : TimelineEvent +{ + private static readonly Size s_size = new Size(16, 16); + + private static readonly ImageSource s_grantedImage; + private static readonly ImageSource s_refusedImage; + + private readonly bool m_isGranted; + + static AccessTimelineEvent() + { + s_grantedImage = EventType.AccessGranted.GetIcon(); + s_refusedImage = EventType.AccessRefused.GetIcon(); + } + + public AccessTimelineEvent(Guid cardholderGuid, DateTime timestamp, bool isGranted) : base(timestamp) + { + CardholderGuid = cardholderGuid; + m_isGranted = isGranted; + } + + public Guid CardholderGuid { get; } + + public override TimelineVisual GetVisual(Rect constraint, double msPerPixel) + { + var drawingVisual = new DrawingVisual(); + + DrawingContext drawingContext = drawingVisual.RenderOpen(); + + ImageSource image = m_isGranted ? s_grantedImage : s_refusedImage; + drawingContext.DrawImage(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 + }; + } +} diff --git a/Samples/Workspace SDK/TimelineProviderSample/AlarmTimelineProvider.cs b/Samples/Workspace SDK/TimelineProviderSample/AlarmTimelineProvider.cs index 7a42370a..c1189716 100644 --- a/Samples/Workspace SDK/TimelineProviderSample/AlarmTimelineProvider.cs +++ b/Samples/Workspace SDK/TimelineProviderSample/AlarmTimelineProvider.cs @@ -1,4 +1,4 @@ -// Copyright 2025 Genetec Inc. +// Copyright 2026 Genetec Inc. // Licensed under the Apache License, Version 2.0 namespace Genetec.Dap.CodeSamples; @@ -13,24 +13,14 @@ namespace Genetec.Dap.CodeSamples; using Sdk.Workspace.Components.TimelineProvider; using Sdk.Workspace.Pages.Contents; -public class AlarmTimelineProvider : TimelineProvider +public class AlarmTimelineProvider : TimelineProvider, IDisposable { private readonly Workspace m_workspace; public AlarmTimelineProvider(Workspace workspace) { m_workspace = workspace; - - workspace.Sdk.AlarmAcknowledged += OnAlarmAcknowledged; - - void OnAlarmAcknowledged(object sender, AlarmAcknowledgedEventArgs e) - { - foreach (AlarmTimelineEvent timelineEvent in GetEvents().OfType() - .Where(timelineEvent => timelineEvent.AlarmGuid == e.AlarmGuid && timelineEvent.InstanceId == e.InstanceId)) - { - RemoveEvent(timelineEvent); - } - } + m_workspace.Sdk.AlarmAcknowledged += OnAlarmAcknowledged; } public override void Query(ContentGroup contentGroup, DateTime startTime, DateTime endTime) @@ -50,11 +40,26 @@ public override void Query(ContentGroup contentGroup, DateTime startTime, DateTi var alarmGuid = row.Field(AlarmActivityQuery.AlarmColumnName); var instanceId = row.Field(AlarmActivityQuery.InstanceIdColumnName); var triggerTime = row.Field(AlarmActivityQuery.TriggerTimeColumnName); - + return new AlarmTimelineEvent(alarmGuid, instanceId, triggerTime); }); InsertEvents(events); + OnQueryCompleted(); }); } -} \ No newline at end of file + + private void OnAlarmAcknowledged(object sender, AlarmAcknowledgedEventArgs e) + { + foreach (AlarmTimelineEvent timelineEvent in GetEvents().OfType() + .Where(t => t.AlarmGuid == e.AlarmGuid && t.InstanceId == e.InstanceId)) + { + RemoveEvent(timelineEvent); + } + } + + public void Dispose() + { + m_workspace.Sdk.AlarmAcknowledged -= OnAlarmAcknowledged; + } +} diff --git a/Samples/Workspace SDK/TimelineProviderSample/AlarmTimelineProviderBuilder.cs b/Samples/Workspace SDK/TimelineProviderSample/AlarmTimelineProviderBuilder.cs index 1c66d20c..26074934 100644 --- a/Samples/Workspace SDK/TimelineProviderSample/AlarmTimelineProviderBuilder.cs +++ b/Samples/Workspace SDK/TimelineProviderSample/AlarmTimelineProviderBuilder.cs @@ -3,8 +3,8 @@ namespace Genetec.Dap.CodeSamples; -using System; using Sdk.Workspace.Components.TimelineProvider; +using System; public class AlarmTimelineProviderBuilder : TimelineProviderBuilder { @@ -12,7 +12,7 @@ public class AlarmTimelineProviderBuilder : TimelineProviderBuilder public override string Title => "Active Alarms"; - public override Guid UniqueId { get; } = new Guid("4765D714-2BD6-42A8-99E3-0A0767C76321"); + public override Guid UniqueId { get; } = new Guid("4765D714-2BD6-42A8-99E3-0A0767C76321"); // Replace with your own unique GUID public override TimelineProvider CreateProvider() { diff --git a/Samples/Workspace SDK/TimelineProviderSample/Certificates/Genetec.Dap.CodeSamples.AccessControlTimelineProviderBuilder.cert b/Samples/Workspace SDK/TimelineProviderSample/Certificates/Genetec.Dap.CodeSamples.AccessControlTimelineProviderBuilder.cert new file mode 100644 index 00000000..b9a1fc92 --- /dev/null +++ b/Samples/Workspace SDK/TimelineProviderSample/Certificates/Genetec.Dap.CodeSamples.AccessControlTimelineProviderBuilder.cert @@ -0,0 +1,5 @@ + + Genetec + Demo Certificate for SDK Development only + KxsD11z743Hf5Gq9mv3+5ekxzemlCiUXkTFY5ba1NOGcLCmGstt2n0zYE9NsNimv + diff --git a/Samples/Workspace SDK/TimelineProviderSample/SampleModule.cs b/Samples/Workspace SDK/TimelineProviderSample/SampleModule.cs index d23fdd60..05188f51 100644 --- a/Samples/Workspace SDK/TimelineProviderSample/SampleModule.cs +++ b/Samples/Workspace SDK/TimelineProviderSample/SampleModule.cs @@ -1,24 +1,47 @@ -// Copyright 2025 Genetec Inc. +// Copyright 2026 Genetec Inc. // Licensed under the Apache License, Version 2.0 namespace Genetec.Dap.CodeSamples; +using Genetec.Sdk.Workspace.Components; using Sdk; using Sdk.Workspace.Modules; public class SampleModule : Module { + private AlarmTimelineProviderBuilder m_alarmBuilder; + private AccessControlTimelineProviderBuilder m_accessControlBuilder; + public override void Load() { if (Workspace.ApplicationType == ApplicationType.SecurityDesk) { - var builder = new AlarmTimelineProviderBuilder(); - builder.Initialize(Workspace); - Workspace.Components.Register(builder); + m_alarmBuilder = new AlarmTimelineProviderBuilder(); + Register(m_alarmBuilder); + + m_accessControlBuilder = new AccessControlTimelineProviderBuilder(); + Register(m_accessControlBuilder); + + void Register(Component component) + { + component.Initialize(Workspace); + Workspace.Components.Register(component); + } } } public override void Unload() { + if (m_alarmBuilder != null) + { + Workspace.Components.Unregister(m_alarmBuilder); + m_alarmBuilder = null; + } + + if (m_accessControlBuilder != null) + { + Workspace.Components.Unregister(m_accessControlBuilder); + m_accessControlBuilder = null; + } } }