|
VR-Engage
2.2
|
VR-Engage operates as a distributed system with separate frontend and backend processes, even when running on a single machine. The frontend handles visualization, user input, and immediate feedback, while the backend manages simulation physics, entity behavior, and network interoperability. This section covers the VR-Engage-specific mechanisms for communication between these processes: the Messaging Framework for structured commands and events, the joystick message pipeline for control input, state properties for backend-to-frontend entity state, and extended data for custom per-entity information.
For information on the Player Attribute Store (frontend component state coordination), see the Player Attribute Store section in the Player Station Framework documentation.
The frontend and backend processes communicate through a network connection, typically over localhost for single-machine deployments or across the network for distributed configurations. The frontend process (vrEngage.exe) handles input processing, control logic, UI rendering, and the Player Attribute Store for coordinating frontend component state. The backend process (vrEngageSim.exe) runs the simulation: joystick controllers receive input commands, actuators apply control inputs to entity models, the entity model (vreVrfmodel) computes physics and behavior, and the networking layer publishes entity state via DIS or HLA.
This separation provides process isolation that simplifies development and testing. Frontend components can be developed and debugged independently from backend simulation logic. The network boundary also supports distributed deployments where visualization runs on a different machine than simulation, which is common in multi-channel display configurations or instructor-operator station setups.
VR-Engage uses several distinct communication mechanisms, each suited to different data patterns:
| Mechanism | Direction | Purpose | Examples |
|---|---|---|---|
| Joystick Messages | Frontend → Backend | Control input | Throttle, steering, weapon commands |
| State Properties | Backend → Frontend | Entity state updates | Speed, fuel level, damage state |
| Extended Data | Backend → Frontend | Custom entity data | Role-specific values, subsystem status |
| Command Messages | Frontend → Backend | Discrete actions | Fire weapon, change mode, task entity |
| Player Attribute Store | Frontend only | Frontend component coordination | UI state, settings, display preferences |
The Messaging Framework provides structured, type-safe publish/subscribe communication between components. Messages are strongly-typed C++ classes generated from Lua schema definitions. Each message type has a unique identifier, defined fields with specific data types, and automatic serialization for cross-process delivery.
Messages flow through a central dispatcher (DtVreMessageManager) that routes them to registered subscribers based on message type. The framework handles serialization for cross-process delivery and supports both synchronous local delivery and asynchronous remote delivery.
Messages can flow within a single process or across the frontend/backend boundary. Within the frontend, components exchange messages frequently for coordination: UI state changes, player station lifecycle events, input focus notifications, and overlay visibility updates all use local message delivery. Cross-process messages require serialization and network transport, which the framework handles transparently.
Components publish messages by creating message instances and passing them to the message manager:
Components subscribe to message types by registering handler functions:
Message handlers return a DtVreMessageResult that controls further dispatch:
| Result | Meaning |
|---|---|
IGNORED | Handler did not process the message; continue dispatching to other subscribers |
HANDLED | Handler processed the message; continue dispatching to other subscribers |
EATEN | Handler processed the message; stop dispatching (no further subscribers receive it) |
Most handlers return HANDLED or IGNORED, allowing multiple components to observe the same message. The EATEN result is used when a component takes exclusive action, such as an input handler consuming a key press that should not propagate to other components.
Intra-process message examples (frontend):
Cross-process message examples:
JoystickMessage: Carries control input from frontend control logic to backend controllersVrfSetStatePropertyMessage: Delivers entity state updates from backend actuators to frontend displaysVrfSetLocationMessage: Sends location commands from frontend to backend entityFireMessage: Transmits weapon fire events between processesVR-Engage defines messages in Lua schema files organized by functional area. The build system processes these files to generate C++ classes with complete serialization support.
Core message categories include:
joystick.lua, input.lua): Control input from frontend to backendentity.lua, ownship.lua): Entity state and lifecycle eventsvrfEntitySetDataRequests.lua, vrfEntityTasks.lua): Backend entity controlvrfSensors.lua, sensorContacts.lua): Sensor data and updatesvrfSessionStatus.lua, connection.lua): Connection and session managementplayerStationUI.lua, playerStationState.lua): Frontend state coordinationCustom messages are defined in Lua schema files. The schema specifies the message name, type identifier, and field definitions:
The schema fields are:
| Field | Purpose |
|---|---|
fileName | Base name for generated .h and .cpp files |
className | C++ class name (the generator appends Message suffix) |
messageType | Hierarchical type string for routing and wildcard matching |
attributes | List of typed fields with accessor/mutator generation |
comment | Documentation comment for the generated class |
Supported attribute types include primitives (bool, int, Int32, UInt32, float, double, string, String), VR-Engage types (EntityIdentifier, EntityEnum, Vector3d), lists (List<String>), and enumerations defined in the enums block.
The generateMessages() CMake function processes Lua schema files and produces C++ source code during the build. The generator creates header and source files with the message class, type-safe accessors, serialization methods, and factory registration code. Generated classes automatically register with DtVreMessageFactory when the containing library loads, allowing the message manager to instantiate and route messages correctly.
For each Lua message definition, the generator produces:
get and set methods for each attributecreate() factory method and theType() type accessorFor detailed instructions on integrating message generation into your CMake build, including the generateMessages() function parameters and library linking requirements, see the Environment Setup & Build Guide. The Custom PDU and Entity Detection examples demonstrate complete message definition and generation workflows.
The code generator produces C++ classes with type-safe accessors for each field:
Message types use dot-notation hierarchies that group related messages and support wildcard subscriptions. A message type like entity.extendedState.request has three levels: the category (entity), subcategory (extendedState), and specific message (request). This structure allows components to subscribe to messages at any level of specificity.
Wildcard subscriptions use * to match any value at a specific hierarchy level. A subscription to entity.* receives all messages in the entity category, regardless of subcategory or specific type. This is useful for components that need to monitor a broad category of events, such as a logging component that captures all entity-related messages or a debugging tool that tracks all sensor updates.
Examples of hierarchical message types:
| Message Type | Category | Purpose |
|---|---|---|
system.input.joystick | Input | Joystick control commands |
entity.discovered | Entity | Entity first seen on network |
entity.state.bool | Entity | Boolean state property updates |
vrf.set.entity.location | VRF | Set entity location commands |
simulation.fire | Simulation | Weapon fire events |
Wildcard subscription examples:
| Pattern | Matches |
|---|---|
system.input.* | All input messages (joystick, etc.) |
entity.* | All entity messages (discovered, realized, removed, state, etc.) |
vrf.set.* | All VR-Forces set-data messages |
simulation.* | All simulation messages (fire, state, etc.) |
Subscribing with a wildcard:
The DtVreMessageId class handles type parsing and matching. When a message is published, the dispatcher compares its type against all registered subscriptions, including wildcard patterns, and invokes matching handlers in registration order.
VR-Engage provides several specialized mechanisms for exchanging data between frontend and backend. Each is optimized for particular data patterns and use cases.
The joystick message system provides the primary communication path from frontend input devices to backend actuators. When a player operates a control (joystick, keyboard, gamepad), the frontend translates this input into a JoystickMessage that crosses the process boundary to the backend.
This section covers the message passing aspects of joystick input. For backend processing—how the VR-Forces joystick infrastructure routes messages to controllers and actuators—see the Joystick controller section in VR-Forces Integration.
The frontend creates joystick messages in response to mapped input actions. These messages travel through the message manager to the backend, where DtVrfRemoteControlConnector receives them and routes them into the VR-Forces joystick system.
sequenceDiagram
participant Device as Input Device
participant Input as DtInputLogic
participant Control as Control Logic
participant MsgMgr as DtVreMessageManager
participant Remote as DtVrfRemoteControlConnector
participant VRF as VR-Forces Joystick System
Device->>Input: Raw Input Event
Input->>Control: Action Callback
Control->>Control: Create JoystickMessage
Control->>MsgMgr: queueMessage()
MsgMgr->>Remote: Deliver Message
Remote->>VRF: Route to Controller
The JoystickMessage carries control input from frontend to backend:
| Field | Purpose | Example Values |
|---|---|---|
entityId | Target entity for the control input | Entity identifier of controlled vehicle |
functionGroup | Controller group that handles this input | "driver", "gunner", "pilot" |
function | Specific control function name | "throttle", "steering", "fire" |
value | Control value, typically normalized | -1.0 to 1.0 for axes, 0.0 or 1.0 for buttons |
repeat | Whether the action should repeat | true for held buttons, false for toggle actions |
Frontend control logic components convert input actions to joystick messages. The pattern involves initializing input logic, registering action handlers, caching the function group from role configuration, and creating messages in response to actions.
The DtVrfRemoteControlConnector class receives joystick messages on the backend. When a JoystickMessage arrives, the connector extracts the message parameters and routes them into the VR-Forces joystick system:
The message manager delivers joystick messages to DtVrfRemoteControlConnector, which then uses the VR-Forces joystick source infrastructure to route the input to the appropriate controller component. For details on how controllers process these inputs and update actuator ports, see the Joystick controller section in VR-Forces Integration.
The glsVreMessageUtil.h header provides convenience functions for common joystick operations:
State properties provide a mechanism for the backend to publish entity state values that the frontend can access. Unlike the Player Attribute Store (which is frontend-centric), state properties originate from backend simulation components and flow to the frontend for display in UI elements.
sequenceDiagram
participant Sim as Simulation Component
participant Model as vreVrfmodel
participant Prop as State Property
participant Msg as VrfSetStatePropertyMessage
participant Frontend as Frontend Component
participant UI as QML UI
Sim->>Model: Update entity state
Model->>Prop: Set property value
Prop->>Msg: Create update message
Msg->>Frontend: Deliver via connector
Frontend->>UI: Update QML property
The VrfSetStatePropertyMessage carries state property updates from backend to frontend:
State properties are commonly used to drive HUD and dashboard displays. The frontend connects state properties to QML object properties through the overlay connection system:
The role configuration maps state properties to QML properties using the bindQmlPropertyToAttribute table:
Backend actuator components publish state properties to communicate state changes to the frontend:
Extended data provides a mechanism for transmitting custom per-entity data that extends beyond standard DIS/HLA entity state. This is useful for VR-Engage-specific entity properties that need to be shared between frontend and backend or with other VR-Engage instances.
The DtExtendedStateConnector manages access to extended state properties:
Extended data supports several property types:
| Type | C++ Type | Use Case |
|---|---|---|
bool | bool | Flags and toggles |
int | int32_t | Counts, indices, enumerations |
float | float | Measurements, ratios |
string | std::string | Text data, identifiers |
ammoClipMap | Custom | Ammunition clip configurations |
This section covers patterns and practices for effective IPC design, including mechanism selection, performance optimization, and debugging techniques.
Selecting between messaging and attributes depends on the nature of the data and its usage pattern. Each mechanism has strengths suited to different scenarios.
Messages are appropriate for discrete occurrences that happen at specific moments:
Messages carry context about the event (what happened, when, to whom) and trigger immediate responses from subscribers. They don't persist after delivery—if a component isn't subscribed when a message arrives, it won't receive that message.
Attributes are appropriate for values that persist over time and can be queried at any moment:
Attributes maintain their values until explicitly changed. New components can read current state immediately upon initialization without waiting for update messages.
| Characteristic | Messaging | Attribute Store |
|---|---|---|
| Persistence | Transient (event-based) | Persistent (state-based) |
| Query pattern | Push (subscription) | Pull (query) or Push (callbacks) |
| Timing | Discrete moments | Continuous availability |
| Late subscribers | Miss past events | See current state |
| Data volume | Any size | Best for simple values |
| Update frequency | Variable | Regular synchronization |
Many features benefit from combining both mechanisms. A weapon system might use attributes for ammunition count (persistent state) and messages for fire commands (discrete events).
Efficient IPC is critical for maintaining high frame rates and responsive simulation. Both mechanisms have performance characteristics that influence design decisions.
Message overhead includes serialization, network transmission, and deserialization. For high-frequency messages, consider:
Attribute store overhead includes path resolution, value storage, and synchronization. For optimal performance:
The attribute synchronization rate balances responsiveness against network overhead. Higher rates provide more immediate state updates but increase bandwidth consumption. Configure the rate based on application requirements:
Diagnosing communication problems requires visibility into message flow and attribute state. VR-Engage provides logging and diagnostic tools for troubleshooting.
Enable message tracing to log all published and received messages:
With tracing enabled, the log shows message flow:
Monitor attribute changes through logging or the diagnostic overlay:
Message not received: Verify subscription is registered before messages are published. Check that message types match exactly between publisher and subscriber. Ensure the message manager is properly initialized.
Attribute not synchronized: Confirm both processes are connected and the sync layer is running. Check that attribute paths match exactly (paths are case-sensitive). Verify the attribute exists before reading.
High latency: Check network configuration for localhost vs. remote connections. Review message rates and consider batching. Monitor CPU usage on both processes for bottlenecks.
State oscillation: Indicates both processes are writing the same attribute. Designate one process as authoritative for each attribute to prevent conflicts.
Effective IPC design follows patterns that ensure reliability, performance, and maintainability.
Design for failure: Network connections can fail or experience delays. Components should handle missing messages and stale attributes gracefully. Use timeouts and fallback values where appropriate.
Minimize coupling: Components should communicate through well-defined message and attribute interfaces rather than direct references. This enables independent testing and deployment.
Document contracts: Clearly document which component owns each attribute and which messages each component publishes or subscribes to. This prevents conflicts and simplifies debugging.
Version messages carefully: When message formats change, consider backward compatibility. Adding optional fields is safer than modifying existing fields.
Test across processes: IPC behavior can differ between same-process and cross-process scenarios. Test with actual distributed deployment to catch serialization and timing issues.
VR-Engage achieves network interoperability with external simulators through VR-Link, MAK's middleware library that abstracts the differences between DIS (Distributed Interactive Simulation) and HLA (High Level Architecture) protocols. The backend process handles all network publishing and subscribing through VR-Forces and VR-Link.
| Protocol | Transport | Use Case | Key Characteristics |
|---|---|---|---|
| DIS | UDP Multicast | Real-time tactical training | Connectionless, low latency, standardized PDUs |
| HLA | RTI Connection | Large-scale federation | Federation management, time synchronization, FOM-based |
VR-Link provides protocol-agnostic APIs that work identically across DIS and HLA. These examples show VR-Link API patterns; for complete API documentation, refer to the VR-Link Developer's Guide:
The backend automatically publishes standard entity state (position, orientation, velocity, appearance) through the dead reckoning infrastructure. Custom data can be transmitted via articulated parts, comment interactions, or VR-Engage's extended data mechanism.
Network parameters are configured in exercise files:
For comprehensive DIS/HLA networking documentation, see:
See also