Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions src/SimConnect.NET/Events/SimSystemEventReceivedEventArgs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// <copyright file="SimSystemEventReceivedEventArgs.cs" company="BARS">
// Copyright (c) BARS. All rights reserved.
// </copyright>

namespace SimConnect.NET.Events
{
/// <summary>
/// Provides data for an event that is raised when a Simconnect system event is raised.
/// </summary>
/// <remarks>
/// Initializes a new instance of the <see cref="SimSystemEventReceivedEventArgs"/> class with the specified event identifier and.
/// associated data.
/// </remarks>
/// <param name="eventId">The unique identifier for the system event.</param>
/// <param name="data">The data associated with the system event.</param>
public class SimSystemEventReceivedEventArgs(uint eventId, uint data) : EventArgs
{
/// <summary>
/// Gets the unique identifier for the event.
/// </summary>
public uint EventId { get; } = eventId;

/// <summary>
/// Gets the data associated with the event.
/// </summary>
public uint Data { get; } = data;
}
}
66 changes: 65 additions & 1 deletion src/SimConnect.NET/SimConnectClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// Copyright (c) BARS. All rights reserved.
// </copyright>

using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
Expand Down Expand Up @@ -70,6 +69,11 @@ public SimConnectClient(string applicationName = "SimConnect.NET Client")
/// </summary>
public event EventHandler<RawSimConnectMessageEventArgs>? RawMessageReceived;

/// <summary>
/// Occurs when a subscribed event is fired.
/// </summary>
public event EventHandler<SimSystemEventReceivedEventArgs>? SystemEventReceived;

/// <summary>
/// Gets a value indicating whether the client is connected to SimConnect.
/// </summary>
Expand Down Expand Up @@ -345,6 +349,40 @@ public async Task DisconnectAsync()
}
}

/// <summary>
/// Subscribes to a specific simulator system event.
/// </summary>
/// <param name="systemEventName">The name of the system event (e.g., "SimStart", "4Sec", "Crashed").</param>
/// <param name="systemEventId">A user-defined ID to identify this subscription.</param>
/// <param name="cancellationToken">Cancellation token for the operation.</param>
/// <returns>A task representing the event.</returns>
/// <exception cref="InvalidOperationException">Thrown when a sim connection wasn't found.</exception>
/// <exception cref="SimConnectException">Thrown when the event wasn't subscribed.</exception>
public async Task SubscribeToEventAsync(string systemEventName, uint systemEventId, CancellationToken cancellationToken = default)
{
ObjectDisposedException.ThrowIf(this.disposed, nameof(SimConnectClient));

if (!this.isConnected)
{
throw new InvalidOperationException("Not connected to SimConnect.");
}

await Task.Run(
() =>
{
var result = SimConnectNative.SimConnect_SubscribeToSystemEvent(
this.simConnectHandle,
systemEventId,
systemEventName);

if (result != (int)SimConnectError.None)
{
throw new SimConnectException($"Failed to subscribe to event {systemEventName}: {(SimConnectError)result}", (SimConnectError)result);
}
},
cancellationToken).ConfigureAwait(false);
}

/// <summary>
/// Processes the next SimConnect message.
/// </summary>
Expand Down Expand Up @@ -419,6 +457,9 @@ public async Task<bool> ProcessNextMessageAsync(CancellationToken cancellationTo
case SimConnectRecvId.VorList:
case SimConnectRecvId.NdbList:
break;
case SimConnectRecvId.Event:
this.ProcessSystemEvent(ppData);
break;
default:
this.simVarManager?.ProcessReceivedData(ppData, pcbData);
break;
Expand Down Expand Up @@ -543,6 +584,29 @@ private void ProcessOpen(IntPtr ppData)
}
}

/// <summary>
/// Processes a system event message from SimConnect.
/// </summary>
/// <param name="ppData">Pointer to the received Event data.</param>
private void ProcessSystemEvent(IntPtr ppData)
{
try
{
var recvEvent = Marshal.PtrToStructure<SimConnectRecvEvent>(ppData);

this.SystemEventReceived?.Invoke(this, new SimSystemEventReceivedEventArgs(recvEvent.EventId, recvEvent.Data));

if (SimConnectLogger.IsLevelEnabled(SimConnectLogger.LogLevel.Debug))
{
SimConnectLogger.Debug($"System Event Received: ID={recvEvent.EventId} Data={recvEvent.Data}");
}
}
catch (Exception ex) when (!ExceptionHelper.IsCritical(ex))
{
SimConnectLogger.Error("Error processing system event", ex);
}
}

/// <summary>
/// Starts the background message processing loop.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// <copyright file="SystemEventSubscriptionTests.cs" company="BARS">
// Copyright (c) BARS. All rights reserved.
// </copyright>

namespace SimConnect.NET.Tests.Net8.Tests
{
internal class SystemEventSubscriptionTests : ISimConnectTest
{
public string Name => "SystemEventSubscription";

public string Description => "Tests system event subscription";

public string Category => "System Event";

public async Task<bool> RunAsync(SimConnectClient client, CancellationToken cancellationToken = default)
{
try
{
if (!client.IsConnected)
{
Console.WriteLine(" ❌ Client should already be connected");
return false;
}

Console.WriteLine(" ✅ Connection status verified");

using var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
cts.CancelAfter(TimeSpan.FromSeconds(15));

bool testEventReceived = false;
client.SystemEventReceived += (sender, e) =>
{
switch (e.EventId)
{
case 100:
Console.WriteLine("4 seconds has passed!");
testEventReceived = true;
break;
}
};

await client.SubscribeToEventAsync("4sec", 100, cts.Token);

Console.WriteLine("Listening for events...");

while (!testEventReceived && !cts.Token.IsCancellationRequested)
{
await Task.Delay(500, cts.Token);
}
if (!testEventReceived)
{
Console.WriteLine(" ❌ Did not receive expected system event");
return false;
}
Console.WriteLine(" ✅ Received expected system event");
return true;
}
catch (OperationCanceledException)
{
Console.WriteLine(" ❌ Connection test timed out");
return false;
}
catch (Exception ex)
{
Console.WriteLine($" ❌ Connection test failed: {ex.Message}");
return false;
}
}
}
}
4 changes: 4 additions & 0 deletions tests/SimConnect.NET.Tests.Net8/Tests/TestRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public TestRunner()
new InputEventTests(),
new InputEventValueTests(),
new PerformanceTests(),
new SystemEventSubscriptionTests(),
};
}

Expand Down Expand Up @@ -143,6 +144,9 @@ private static TestOptions ParseArguments(string[] args)
case "--verbose":
options.Verbose = true;
break;
case "--test-events":
options.Categories.Add("System Event");
break;
}
}

Expand Down