Manager Usage
The manager package provides automatic connection lifecycle management with reconnection support. This document covers the complete API for robust, long-running SimConnect applications.
See also: Configuration Options for all available options when creating a manager.
Creating a Manager
Using the Root Package (Recommended)
import "github.com/mrlm-net/simconnect"
mgr := simconnect.New("MyApp")
Using the Manager Package Directly
import "github.com/mrlm-net/simconnect/pkg/manager"
mgr := manager.New("MyApp")
Connection Lifecycle
Start
Starts the manager’s connection loop. This method blocks until the context is cancelled or the manager is stopped.
if err := mgr.Start(); err != nil {
log.Fatal("Manager stopped with error:", err)
}
The manager will:
- Attempt to connect to SimConnect
- Dispatch messages while connected
- Automatically reconnect if the connection drops (when enabled)
- Continue until
Stop()is called or context is cancelled
Stop
Gracefully stops the manager and closes the connection.
mgr.Stop()
ConnectionState
Returns the current connection state.
state := mgr.ConnectionState()
switch state {
case manager.StateDisconnected:
fmt.Println("Not connected")
case manager.StateConnecting:
fmt.Println("Connecting...")
case manager.StateConnected:
fmt.Println("Connected")
case manager.StateReconnecting:
fmt.Println("Reconnecting...")
case manager.StateStopped:
fmt.Println("Stopped")
}
Client
Returns the underlying engine client when connected. Returns nil when disconnected.
if client := mgr.Client(); client != nil {
// Use client for SimConnect operations
client.AddToDataDefinition(...)
}
Callback-Based Event Handlers
The manager provides callback-style handlers for reacting to events. Each returns an ID that can be used to remove the handler.
OnConnectionStateChange
Called when the connection state changes.
handlerID := mgr.OnConnectionStateChange(func(oldState, newState manager.ConnectionState) {
fmt.Printf("State: %v → %v\n", oldState, newState)
if newState == manager.StateConnected {
// Set up data definitions when connected
setupDataDefinitions(mgr.Client())
}
})
// Remove handler when no longer needed
mgr.RemoveConnectionStateChange(handlerID)
OnSimStateChange
Called when the simulator state changes (camera, pause/sim running state, crash and sound flags).
handlerID := mgr.OnSimStateChange(func(oldState, newState manager.SimState) {
if oldState.Paused != newState.Paused {
if newState.Paused {
fmt.Println("Simulator paused")
} else {
fmt.Println("Simulator resumed")
}
}
if oldState.SimRunning != newState.SimRunning {
if newState.SimRunning {
fmt.Println("Simulator started")
} else {
fmt.Println("Simulator stopped")
}
}
if oldState.Camera != newState.Camera {
fmt.Printf("Camera changed: %v → %v\n", oldState.Camera, newState.Camera)
}
if oldState.Crashed != newState.Crashed {
if newState.Crashed {
fmt.Println("Simulator reports a crash")
} else {
fmt.Println("Crash state reset")
}
}
if oldState.Sound != newState.Sound {
fmt.Printf("Sound event: id=%d\n", newState.Sound)
}
})
mgr.RemoveSimStateChange(handlerID)
OnMessage
Called for every SimConnect message received.
handlerID := mgr.OnMessage(func(msg engine.Message) {
switch types.SIMCONNECT_RECV_ID(msg.DwID) {
case types.SIMCONNECT_RECV_ID_SIMOBJECT_DATA:
handleObjectData(msg.AsSimObjectData())
case types.SIMCONNECT_RECV_ID_EVENT:
handleEvent(msg.AsEvent())
}
})
mgr.RemoveMessage(handlerID)
OnOpen
Called when the SimConnect connection opens.
handlerID := mgr.OnOpen(func(data *types.SIMCONNECT_RECV_OPEN) {
fmt.Printf("Connected to: %s v%d.%d\n",
engine.ParseNullTerminatedString(data.SzApplicationName[:]),
data.DwApplicationVersionMajor,
data.DwApplicationVersionMinor)
})
mgr.RemoveOpen(handlerID)
OnQuit
Called when the simulator closes the connection.
handlerID := mgr.OnQuit(func() {
fmt.Println("Simulator disconnected")
})
mgr.RemoveQuit(handlerID)
Channel-Based Subscriptions
For more control over message handling, use channel-based subscriptions. These are ideal for goroutine-based architectures.
Subscribe
Creates a subscription that receives all SimConnect messages.
sub := mgr.Subscribe("my-subscription", 10) // Buffer size of 10
defer sub.Unsubscribe()
for {
select {
case msg := <-sub.Messages():
handleMessage(msg)
case <-sub.Done():
return
}
}
SubscribeWithFilter
Creates a subscription with a custom filter function.
// Only receive data messages for specific request IDs
sub := mgr.SubscribeWithFilter("position-data", 10, func(msg engine.Message) bool {
if types.SIMCONNECT_RECV_ID(msg.DwID) != types.SIMCONNECT_RECV_ID_SIMOBJECT_DATA {
return false
}
data := msg.AsSimObjectData()
return data.DwRequestID == PositionReqID
})
defer sub.Unsubscribe()
SubscribeWithType
Creates a subscription filtered by message types.
// Only receive object data and event messages
sub := mgr.SubscribeWithType("game-events", 10,
types.SIMCONNECT_RECV_ID_SIMOBJECT_DATA,
types.SIMCONNECT_RECV_ID_EVENT,
)
defer sub.Unsubscribe()
Callback-Style System Event Handlers
For simple event handling, the manager provides callback-style handlers that register functions to be invoked when specific events occur:
// Pause event handler
pauseID := mgr.OnPause(func(paused bool) {
if paused {
fmt.Println("Simulator paused")
} else {
fmt.Println("Simulator unpaused")
}
})
mgr.RemovePause(pauseID)
// SimRunning event handler
simID := mgr.OnSimRunning(func(running bool) {
if running {
fmt.Println("Simulator started")
} else {
fmt.Println("Simulator stopped")
}
})
mgr.RemoveSimRunning(simID)
// Crash event handler
crashID := mgr.OnCrashed(func() {
fmt.Println("Aircraft crashed!")
})
mgr.RemoveCrashed(crashID)
// Crash reset handler
resetID := mgr.OnCrashReset(func() {
fmt.Println("Crash reset")
})
mgr.RemoveCrashReset(resetID)
// Sound event handler
soundID := mgr.OnSoundEvent(func(soundEventID uint32) {
fmt.Printf("Sound event: %d\n", soundEventID)
})
mgr.RemoveSoundEvent(soundID)
// View event handler
viewID := mgr.OnView(func(viewID uint32) {
fmt.Printf("View changed: %d\n", viewID)
})
mgr.RemoveView(viewID)
// Flight loaded handler
flightID := mgr.OnFlightLoaded(func(filename string) {
fmt.Printf("Flight loaded: %s\n", filename)
})
mgr.RemoveFlightLoaded(flightID)
// Aircraft loaded handler
aircraftID := mgr.OnAircraftLoaded(func(filename string) {
fmt.Printf("Aircraft loaded: %s\n", filename)
})
mgr.RemoveAircraftLoaded(aircraftID)
// Flight plan activated handler
planID := mgr.OnFlightPlanActivated(func(filename string) {
fmt.Printf("Flight plan activated: %s\n", filename)
})
mgr.RemoveFlightPlanActivated(planID)
// Flight plan deactivated handler
deactivatedID := mgr.OnFlightPlanDeactivated(func() {
fmt.Println("Flight plan deactivated")
})
mgr.RemoveFlightPlanDeactivated(deactivatedID)
// Object added handler
addID := mgr.OnObjectAdded(func(objectID uint32, objType uint32) {
fmt.Printf("Object added: id=%d type=%d\n", objectID, objType)
})
mgr.RemoveObjectAdded(addID)
// Object removed handler
remID := mgr.OnObjectRemoved(func(objectID uint32, objType uint32) {
fmt.Printf("Object removed: id=%d type=%d\n", objectID, objType)
})
mgr.RemoveObjectRemoved(remID)
Typed System Event Subscriptions
For more control over event handling (e.g., goroutine-based processing), use channel-based subscriptions. The manager exposes convenience subscriptions for common system events (wrapping message subscriptions and delivering typed payloads):
SubscribeOnPause(id, bufferSize)— deliversPauseEventcontaining a booleanPausedfield indicating whether the simulator is paused.SubscribeOnSimRunning(id, bufferSize)— deliversSimRunningEventcontaining a booleanRunningfield indicating whether the simulator is running.SubscribeOnFlightLoaded(id, bufferSize)— deliversFilenameEventwith the loaded flight filename.SubscribeOnAircraftLoaded(id, bufferSize)— deliversFilenameEventwith the loaded aircraft.AIRfilename.SubscribeOnFlightPlanActivated(id, bufferSize)— deliversFilenameEventwith the activated flight plan filename.SubscribeOnFlightPlanDeactivated(id, bufferSize)— delivers rawengine.Messagefor theFlight Plan Deactivatedsystem event (void event, no data).SubscribeOnObjectAdded(id, bufferSize)— deliversObjectEventwhen an AI object is added (containsObjectIDandObjType).SubscribeOnObjectRemoved(id, bufferSize)— deliversObjectEventwhen an AI object is removed (containsObjectIDandObjType).SubscribeOnCrashed(id, bufferSize)— delivers rawengine.Messagefor theCrashedsystem event (filter pre-applied).SubscribeOnCrashReset(id, bufferSize)— delivers rawengine.Messagefor theCrash Resetsystem event.SubscribeOnSoundEvent(id, bufferSize)— delivers rawengine.Messagefor theSoundsystem event (sound ID available inDwData).SubscribeOnView(id, bufferSize)— delivers rawengine.Messagefor theViewsystem event (view ID available inDwData).
Example — receive pause/sim running notifications:
pauseSub := mgr.SubscribeOnPause("pause-sub", 5)
defer pauseSub.Unsubscribe()
go func() {
for ev := range pauseSub.Events() {
if ev.Paused {
fmt.Println("Simulator paused")
} else {
fmt.Println("Simulator unpaused")
}
}
}()
simSub := mgr.SubscribeOnSimRunning("sim-sub", 5)
defer simSub.Unsubscribe()
for ev := range simSub.Events() {
if ev.Running {
fmt.Println("Simulator running")
} else {
fmt.Println("Simulator stopped")
}
}
Example — receive flight-loaded notifications:
sub := mgr.SubscribeOnFlightLoaded("flight-load", 5)
defer sub.Unsubscribe()
for {
select {
case ev := <-sub.Events():
fmt.Printf("Flight loaded: %s\n", ev.Filename)
case <-sub.Done():
return
}
}
Example — subscribe to crash/sound/view events (raw message subscription delivered by helpers):
subCrash := mgr.SubscribeOnCrashed("crash-sub", 4)
defer subCrash.Unsubscribe()
go func() {
for msg := range subCrash.Messages() {
ev := msg.AsEvent()
if ev != nil {
fmt.Printf("[sub] Crashed event: data=%d\n", ev.DwData)
}
}
}()
subSound := mgr.SubscribeOnSoundEvent("sound-sub", 4)
defer subSound.Unsubscribe()
go func() {
for msg := range subSound.Messages() {
ev := msg.AsEvent()
if ev != nil {
fmt.Printf("[sub] Sound event id=%d data=%d\n", ev.UEventID, ev.DwData)
}
}
}()
subView := mgr.SubscribeOnView("view-sub", 4)
defer subView.Unsubscribe()
go func() {
for msg := range subView.Messages() {
ev := msg.AsEvent()
if ev != nil {
fmt.Printf("[sub] View changed: viewID=%d\n", ev.DwData)
}
}
}()
Example — monitor AI object add/remove events:
subAdd := mgr.SubscribeOnObjectAdded("obj-add", 20)
defer subAdd.Unsubscribe()
subRem := mgr.SubscribeOnObjectRemoved("obj-rem", 20)
defer subRem.Unsubscribe()
go func() {
for ev := range subAdd.Events() {
fmt.Printf("Object added: id=%d type=%d\n", ev.ObjectID, ev.ObjType)
}
}()
for ev := range subRem.Events() {
fmt.Printf("Object removed: id=%d type=%d\n", ev.ObjectID, ev.ObjType)
}
Example — monitor flight plan activation/deactivation:
subActivated := mgr.SubscribeOnFlightPlanActivated("plan-activate", 4)
defer subActivated.Unsubscribe()
subDeactivated := mgr.SubscribeOnFlightPlanDeactivated("plan-deactivate", 4)
defer subDeactivated.Unsubscribe()
go func() {
for ev := range subActivated.Events() {
fmt.Printf("Flight plan activated: %s\n", ev.Filename)
}
}()
go func() {
for range subDeactivated.Messages() {
fmt.Println("Flight plan deactivated")
}
}()
Notes:
- These helpers filter and forward the appropriate SimConnect message types. They are safe to use concurrently and will automatically cancel when the manager stops.
- The manager already subscribes to the corresponding SimConnect system events on connection open; these helpers simply provide typed channels for consumers.
Custom System Events
In addition to the built-in system events (Pause, Sim, Crashed, etc.), the manager allows subscription to custom SimConnect system events by name. These can be used to monitor any simulator event not covered by the pre-defined handlers.
OnCustomSystemEvent (Callback)
Register a callback handler for a custom system event:
// Subscribe to a custom system event (e.g., "6Hz" for high-frequency timer)
handlerID, err := mgr.OnCustomSystemEvent("6Hz", func(eventName string, data uint32) {
fmt.Printf("Custom event '%s' fired: data=%d\n", eventName, data)
})
if err != nil {
log.Printf("Failed to subscribe to custom event: %v", err)
}
// Remove handler when no longer needed
if err := mgr.RemoveCustomSystemEvent("6Hz", handlerID); err != nil {
log.Printf("Failed to remove custom event handler: %v", err)
}
SubscribeToCustomSystemEvent (Channel)
For goroutine-based processing, use channel subscriptions:
// Subscribe to custom event via channel
sub, err := mgr.SubscribeToCustomSystemEvent("6Hz", 10)
if err != nil {
log.Printf("Failed to subscribe to custom event: %v", err)
return
}
defer sub.Unsubscribe()
go func() {
for ev := range sub.Events() {
fmt.Printf("Custom event '%s': data=%d\n", ev.EventName, ev.Data)
}
}()
// Unsubscribe from the custom event
if err := mgr.UnsubscribeFromCustomSystemEvent("6Hz"); err != nil {
log.Printf("Failed to unsubscribe: %v", err)
}
Reserved Event Names
The following event names are reserved for built-in manager subscriptions and cannot be used with custom event APIs:
Pause,Sim,Crashed,CrashReset,Sound,ViewFlightLoaded,AircraftLoaded,FlightPlanActivated,FlightPlanDeactivatedObjectAdded,ObjectRemoved
Attempting to subscribe to a reserved event name using the custom APIs will return an error.
Custom Event ID Allocation
Custom system events are assigned IDs from the manager-reserved range (999,999,850 - 999,999,886, 37 slots). See ID Management for details. Custom event subscriptions are automatically cleared on disconnect.
Example: Multiple Custom Events
// Subscribe to multiple custom events
events := []string{"1sec", "4sec", "6Hz"}
for _, eventName := range events {
_, err := mgr.OnCustomSystemEvent(eventName, func(name string, data uint32) {
fmt.Printf("[%s] fired: %d\n", name, data)
})
if err != nil {
log.Printf("Failed to subscribe to %s: %v", eventName, err)
}
}
// Later, unsubscribe from all custom events on shutdown
for _, eventName := range events {
// Callback handlers can be removed individually by ID or all at once by unsubscribing
if err := mgr.UnsubscribeFromCustomSystemEvent(eventName); err != nil {
log.Printf("Failed to unsubscribe from %s: %v", eventName, err)
}
}
GetSubscription
Retrieves an existing subscription by ID.
if sub := mgr.GetSubscription("my-subscription"); sub != nil {
// Use existing subscription
}
Subscription Interface
All subscriptions implement the Subscription interface:
| Method | Returns | Description |
|---|---|---|
Messages() |
<-chan engine.Message |
Channel receiving messages |
Done() |
<-chan struct{} |
Closed when subscription ends |
Unsubscribe() |
- | Stops and removes the subscription |
ID() |
string |
Subscription identifier |
State Subscriptions
Specialized subscriptions for state changes.
SubscribeConnectionStateChange
sub := mgr.SubscribeConnectionStateChange("conn-monitor", 5)
defer sub.Unsubscribe()
for {
select {
case change := <-sub.Changes():
fmt.Printf("Connection: %v → %v\n", change.Old, change.New)
case <-sub.Done():
return
}
}
SubscribeSimStateChange
sub := mgr.SubscribeSimStateChange("sim-monitor", 5)
defer sub.Unsubscribe()
for {
select {
case change := <-sub.Changes():
if change.New.Paused {
pauseDataCollection()
} else {
resumeDataCollection()
}
case <-sub.Done():
return
}
}
SubscribeOnOpen
sub := mgr.SubscribeOnOpen("open-monitor", 5)
defer sub.Unsubscribe()
for {
select {
case openData := <-sub.Opens():
initializeConnection(openData)
case <-sub.Done():
return
}
}
SubscribeOnQuit
sub := mgr.SubscribeOnQuit("quit-monitor", 5)
defer sub.Unsubscribe()
for {
select {
case <-sub.Quits():
cleanupResources()
case <-sub.Done():
return
}
}
Simulator State
SimState
Returns the current simulator state when connected.
state := mgr.SimState()
fmt.Printf("Camera: %v, Paused: %v, Crashed: %v\n", state.Camera, state.Paused, state.Crashed)
SimState Structure
| Field | Type | Description |
|---|---|---|
Camera |
CameraState |
Current camera mode |
Substate |
CameraSubstate |
Camera substate |
Paused |
bool |
Whether simulation is paused |
SimRunning |
bool |
Whether simulation is running |
SimulationRate |
float64 |
Current simulation rate multiplier |
SimulationTime |
float64 |
Seconds since simulation start |
LocalTime |
float64 |
Seconds since midnight (local) |
ZuluTime |
float64 |
Seconds since midnight (Zulu/UTC) |
IsInVR |
bool |
Whether user is in VR mode |
IsUsingMotionControllers |
bool |
Motion controllers active |
IsUsingJoystickThrottle |
bool |
Joystick throttle active |
IsInRTC |
bool |
In real-time communication |
IsAvatar |
bool |
Avatar mode active |
IsAircraft |
bool |
Controlling an aircraft |
Crashed |
bool |
Crash state reported by sim |
CrashReset |
bool |
Crash reset flag |
Sound |
uint32 |
Last sound event ID |
LocalDay |
int |
Local day of month |
LocalMonth |
int |
Local month of year |
LocalYear |
int |
Local year |
ZuluDay |
int |
Zulu day of month |
ZuluMonth |
int |
Zulu month of year |
ZuluYear |
int |
Zulu year |
Realism |
float64 |
Realism setting value |
VisualModelRadius |
float64 |
Visual model radius (meters) |
SimDisabled |
bool |
Simulation disabled flag |
RealismCrashDetection |
bool |
Crash detection enabled |
RealismCrashWithOthers |
bool |
Crash with others enabled |
TrackIREnabled |
bool |
TrackIR head tracking enabled |
UserInputEnabled |
bool |
User input enabled |
SimOnGround |
bool |
Aircraft is on ground |
AmbientTemperature |
float64 |
Ambient temperature (Celsius) |
AmbientPressure |
float64 |
Ambient pressure (inHg) |
AmbientWindVelocity |
float64 |
Ambient wind speed (Knots) |
AmbientWindDirection |
float64 |
Ambient wind direction (Degrees) |
AmbientVisibility |
float64 |
Ambient visibility (Meters) |
AmbientInCloud |
bool |
Whether aircraft is in cloud |
AmbientPrecipState |
uint32 |
Precipitation state mask (2=None, 4=Rain, 8=Snow) |
BarometerPressure |
float64 |
Barometric pressure (Millibars) |
SeaLevelPressure |
float64 |
Sea level pressure (Millibars) |
GroundAltitude |
float64 |
Ground elevation at aircraft position (Feet) |
MagVar |
float64 |
Magnetic variation (Degrees) |
SurfaceType |
uint32 |
Surface type enum |
Latitude |
float64 |
Aircraft latitude (Degrees) |
Longitude |
float64 |
Aircraft longitude (Degrees) |
Altitude |
float64 |
Aircraft altitude MSL (Feet) |
IndicatedAltitude |
float64 |
Indicated altitude (Feet) |
TrueHeading |
float64 |
True heading (Degrees) |
MagneticHeading |
float64 |
Magnetic heading (Degrees) |
Pitch |
float64 |
Aircraft pitch (Degrees) |
Bank |
float64 |
Aircraft bank (Degrees) |
GroundSpeed |
float64 |
Ground velocity (Knots) |
IndicatedAirspeed |
float64 |
Indicated airspeed (Knots) |
TrueAirspeed |
float64 |
True airspeed (Knots) |
VerticalSpeed |
float64 |
Vertical speed (Feet per second) |
SmartCameraActive |
bool |
Whether smart camera is active |
HandAnimState |
int32 |
Hand animation state (Enum: 0-12 frame IDs) |
HideAvatarInAircraft |
bool |
Whether avatar is hidden in aircraft |
MissionScore |
float64 |
Current mission score |
ParachuteOpen |
bool |
Whether parachute is open |
ZuluSunriseTime |
float64 |
Zulu sunrise time (Seconds since midnight) |
ZuluSunsetTime |
float64 |
Zulu sunset time (Seconds since midnight) |
TimeZoneOffset |
float64 |
Time zone offset (Seconds, local minus Zulu) |
TooltipUnits |
int32 |
Tooltip units (Enum: 0=Default, 1=Metric, 2=US) |
UnitsOfMeasure |
int32 |
Units of measure (Enum: 0=English, 1=Metric/feet, 2=Metric/meters) |
AmbientInSmoke |
bool |
Whether aircraft is in smoke |
EnvSmokeDensity |
float64 |
Smoke density (Percent over 100) |
EnvCloudDensity |
float64 |
Cloud density (Percent over 100) |
DensityAltitude |
float64 |
Density altitude (Feet) |
SeaLevelAmbientTemperature |
float64 |
Sea level ambient temperature (Celsius) |
State Helpers
The manager package provides convenience functions for common state checks:
import "github.com/mrlm-net/simconnect/pkg/manager"
state := mgr.SimState()
// Check if in any playable/interactive state
if manager.IsInGame(state) {
fmt.Println("In game")
}
// Check if actively playing (unpaused, running, in flight)
if manager.IsPlaying(state) {
fmt.Println("Playing")
}
// Check if in running game (active flight)
if manager.IsInRunningGame(state) {
fmt.Println("In running game")
}
// Check if showing loading screen
if manager.IsInLoadingGame(state) {
fmt.Println("Loading game")
}
// Check if in drone camera mode
if manager.IsInDroneCamera(state) {
fmt.Println("In drone camera")
}
// Check if in any in-game menu state
if manager.IsInGameMenuStates(state) {
fmt.Println("In game menu")
}
State transition helpers:
oldState := mgr.SimState()
// ... wait for state change ...
newState := mgr.SimState()
// Detect in-game state change
if manager.IsInGameChange(oldState, newState) {
fmt.Println("In-game state changed")
}
// Detect known MSFS camera bug (switching to main menu)
if manager.IsInGameMenuMainBug(oldState, newState) {
fmt.Println("Main menu camera bug detected")
}
Available helper functions:
| Function | Description |
|---|---|
IsInGame(state) |
Whether sim is in any playable/interactive state |
IsInGameChange(old, new) |
Whether in-game state changed between two states |
IsInRunningGame(state) |
Whether sim is actively running a flight |
IsInLoadingGame(state) |
Whether sim is showing loading screen |
IsInGameMenuMainBug(old, new) |
Whether state change matches known MSFS camera bug |
IsInGameMenuStates(state) |
Whether sim is in any in-game menu state |
IsInDroneCamera(state) |
Whether sim is in drone camera mode |
IsPlaying(state) |
Whether sim is actively playing (unpaused, running, in flight) |
Camera States
Common camera state values (see pkg/manager/state.go for the complete list of 20+ camera states):
| Value | Constant | Description |
|---|---|---|
| 2 | CameraStateCockpit |
Cockpit view |
| 3 | CameraStateExternalChase |
External/chase view |
| 4 | CameraStateDrone |
Drone camera |
| 5 | CameraStateFixedOnPlane |
Fixed on plane |
| 6 | CameraStateEnvironment |
Environment camera |
| 7 | CameraStateSixDoF |
Six degrees of freedom |
| 8 | CameraStateGameplay |
Gameplay camera |
| 9 | CameraStateShowcase |
Showcase mode |
| 10 | CameraStateDroneAircraft |
Drone Aircraft |
| 12 | CameraStateWorldMap |
World Map |
| 17 | CameraStateReplay |
Replay |
Configuration Getters
Inspect the manager’s configuration at runtime:
fmt.Printf("Auto-reconnect: %v\n", mgr.IsAutoReconnect())
fmt.Printf("Retry interval: %v\n", mgr.RetryInterval())
fmt.Printf("Connection timeout: %v\n", mgr.ConnectionTimeout())
fmt.Printf("Reconnect delay: %v\n", mgr.ReconnectDelay())
fmt.Printf("Shutdown timeout: %v\n", mgr.ShutdownTimeout())
fmt.Printf("Max retries: %d\n", mgr.MaxRetries())
ID Management
The manager reserves IDs 999,000-999,999 for internal use. See Request ID Management for details.
Validating User IDs
const MyDataDefID = 1000
if !manager.IsValidUserID(MyDataDefID) {
log.Fatal("ID conflicts with manager reserved range")
}
Dataset Registration
The manager exposes dataset registration methods directly, eliminating the need to access the underlying client for common data definition operations. All methods return manager.ErrNotConnected if called when not connected.
RegisterDataset
Registers a complete pre-built dataset with SimConnect.
import "github.com/mrlm-net/simconnect/pkg/datasets/traffic"
mgr.OnConnectionStateChange(func(old, new manager.ConnectionState) {
if new == manager.StateConnected {
// Register a pre-built dataset directly on the manager
if err := mgr.RegisterDataset(DataDefID, traffic.NewAircraftDataset()); err != nil {
log.Printf("Failed to register dataset: %v", err)
}
}
})
AddToDataDefinition
Adds individual data definitions to a definition group.
mgr.OnConnectionStateChange(func(old, new manager.ConnectionState) {
if new == manager.StateConnected {
mgr.AddToDataDefinition(DataDefID, "PLANE LATITUDE", "degrees",
types.SIMCONNECT_DATATYPE_FLOAT64, 0, 0)
mgr.AddToDataDefinition(DataDefID, "PLANE LONGITUDE", "degrees",
types.SIMCONNECT_DATATYPE_FLOAT64, 0, 1)
mgr.AddToDataDefinition(DataDefID, "PLANE ALTITUDE", "feet",
types.SIMCONNECT_DATATYPE_FLOAT64, 0, 2)
}
})
RequestDataOnSimObject
Requests periodic data for a specific object (e.g., user aircraft).
err := mgr.RequestDataOnSimObject(
DataReqID, // Request ID
DataDefID, // Definition ID
types.SIMCONNECT_OBJECT_ID_USER, // User aircraft
types.SIMCONNECT_PERIOD_SECOND, // Update every second
types.SIMCONNECT_DATA_REQUEST_FLAG_CHANGED, // Only when changed
0, 0, 0, // origin, interval, limit
)
if err != nil {
if errors.Is(err, manager.ErrNotConnected) {
log.Println("Not connected, will retry later")
}
}
RequestDataOnSimObjectType
Requests data for all objects of a type within a radius.
// Request data for all aircraft within 25km
err := mgr.RequestDataOnSimObjectType(
TrafficReqID,
TrafficDefID,
25000, // radius in meters
types.SIMCONNECT_SIMOBJECT_TYPE_AIRCRAFT,
)
ClearDataDefinition
Clears all definitions from a definition group.
err := mgr.ClearDataDefinition(DataDefID)
SetDataOnSimObject
Sets data values on a simulation object.
type AircraftPosition struct {
Latitude float64
Longitude float64
Altitude float64
}
pos := AircraftPosition{
Latitude: 47.4502,
Longitude: -122.3088,
Altitude: 5000,
}
err := mgr.SetDataOnSimObject(
DataDefID,
types.SIMCONNECT_OBJECT_ID_USER,
0, // flags
0, // array count (0 for single object)
uint32(unsafe.Sizeof(pos)),
unsafe.Pointer(&pos),
)
Dataset Methods Summary
| Method | Description |
|---|---|
RegisterDataset(defID, dataset) |
Register a complete dataset |
AddToDataDefinition(...) |
Add single data definition |
RequestDataOnSimObject(...) |
Request data for specific object |
RequestDataOnSimObjectType(...) |
Request data for object type |
ClearDataDefinition(defID) |
Clear all definitions |
SetDataOnSimObject(...) |
Set data on an object |
All methods return manager.ErrNotConnected when not connected. Use errors.Is() to check:
if errors.Is(err, manager.ErrNotConnected) {
// Handle not connected state
}
Example: Complete Application
package main
import (
"context"
"fmt"
"log/slog"
"os"
"os/signal"
"time"
"github.com/mrlm-net/simconnect/pkg/engine"
"github.com/mrlm-net/simconnect/pkg/manager"
"github.com/mrlm-net/simconnect/pkg/types"
)
type AircraftData struct {
Latitude float64
Longitude float64
Altitude float64
}
const (
DataDefID = 1000
DataReqID = 1001
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
// Handle Ctrl+C
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, os.Interrupt)
go func() {
<-sigChan
fmt.Println("\nShutting down...")
cancel()
}()
logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
Level: slog.LevelInfo,
}))
mgr := manager.New("MyApp",
manager.WithContext(ctx),
manager.WithLogger(logger),
manager.WithAutoReconnect(true),
manager.WithRetryInterval(10*time.Second),
)
// Set up data definitions when connected
mgr.OnConnectionStateChange(func(old, new manager.ConnectionState) {
if new == manager.StateConnected {
setupDataDefinitions(mgr)
}
})
// Handle incoming data
mgr.OnMessage(func(msg engine.Message) {
if types.SIMCONNECT_RECV_ID(msg.DwID) == types.SIMCONNECT_RECV_ID_SIMOBJECT_DATA {
data := msg.AsSimObjectData()
if data.DwRequestID == DataReqID {
aircraft := engine.CastDataAs[AircraftData](&data.DwData)
fmt.Printf("Position: %.4f, %.4f @ %.0fft\n",
aircraft.Latitude, aircraft.Longitude, aircraft.Altitude)
}
}
})
// React to pause state
mgr.OnSimStateChange(func(old, new manager.SimState) {
if old.Paused != new.Paused {
if new.Paused {
fmt.Println("⏸ Paused")
} else {
fmt.Println("▶ Resumed")
}
}
})
// Start the manager (blocks until context cancelled)
if err := mgr.Start(); err != nil {
logger.Error("Manager stopped", "error", err)
}
}
func setupDataDefinitions(mgr manager.Manager) {
mgr.AddToDataDefinition(DataDefID, "PLANE LATITUDE", "degrees",
types.SIMCONNECT_DATATYPE_FLOAT64, 0, 0)
mgr.AddToDataDefinition(DataDefID, "PLANE LONGITUDE", "degrees",
types.SIMCONNECT_DATATYPE_FLOAT64, 0, 0)
mgr.AddToDataDefinition(DataDefID, "PLANE ALTITUDE", "feet",
types.SIMCONNECT_DATATYPE_FLOAT64, 0, 0)
mgr.RequestDataOnSimObject(
DataReqID, DataDefID,
types.SIMCONNECT_OBJECT_ID_USER,
types.SIMCONNECT_PERIOD_SECOND,
types.SIMCONNECT_DATA_REQUEST_FLAG_CHANGED,
0, 0, 0,
)
}
Example: Channel-Based Processing
package main
import (
"context"
"fmt"
"github.com/mrlm-net/simconnect/pkg/engine"
"github.com/mrlm-net/simconnect/pkg/manager"
"github.com/mrlm-net/simconnect/pkg/types"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
mgr := manager.New("ChannelApp", manager.WithContext(ctx))
// Use channel-based subscriptions
go processPositionData(mgr)
go monitorConnectionState(mgr)
mgr.Start()
}
func processPositionData(mgr manager.Manager) {
sub := mgr.SubscribeWithType("position", 100,
types.SIMCONNECT_RECV_ID_SIMOBJECT_DATA,
)
defer sub.Unsubscribe()
for {
select {
case msg := <-sub.Messages():
data := msg.AsSimObjectData()
// Process position data...
fmt.Printf("Received data for request %d\n", data.DwRequestID)
case <-sub.Done():
return
}
}
}
func monitorConnectionState(mgr manager.Manager) {
sub := mgr.SubscribeConnectionStateChange("state-monitor", 10)
defer sub.Unsubscribe()
for {
select {
case change := <-sub.Changes():
fmt.Printf("Connection state: %v → %v\n", change.Old, change.New)
case <-sub.Done():
return
}
}
}
See Also
- Event Lifecycle — Complete event reference with dispatch architecture
- Manager Configuration — All configuration options
- Client Usage — Direct engine client API
- Request ID Management — ID allocation strategy
- Examples — Working code samples