VR-Engage  2.2
Loading...
Searching...
No Matches
vreSetController.h
Go to the documentation of this file.
1/*******************************************************************************
2** Copyright (c) 2025 MAK Technologies
3** All rights reserved.
4*******************************************************************************/
5
6//! \file vreSetController.h
7//! \ingroup vreVrfmodel
8//! \brief Template controller for handling VR-Engage entity control assignments
9//!
10//! This file defines the DtVreSetController template class which extends base set
11//! controllers to handle VR-Engage specific control requests. It manages role-based
12//! control assignments with multi-station support and provides resource information
13//! publishing for player-controlled entities.
14
15#pragma once
16
17#include "vreVrfmodel/export.h"
19
20
21#include "vreUtil/assert.h"
22#include "vreUtil/logger.h"
23
25
26#include <vrfmsgs/setDataRequestMessage.h>
27
28#include <vrfobjcore/physicalWorld.h>
29#include <vrfobjcore/setDataManager.h>
30#include <vrfobjcore/localObject.h>
31#include <vrfobjcore/vrfMovingObjectStateRepository.h>
32#include <vrfobjcore/attachedTerrain.h>
33
34#include <vrftasks/setAltitudeRequest.h>
35#include <vrftasks/setDataRequest.h>
36
37#include <vrfutil/kinematicTools.h>
38
39#include <terrainCS/coordSystem.h>
40
41#include <vlutil/vlString.h>
42
43class DtSimulationServices;
44class DtComponentDescriptor;
45class DtReaderWriterRegistry;
46
47namespace makVre
48{
49//! \brief Template controller for handling VR-Engage entity control assignments
50//! \tparam SetControllerBase The base set controller class to extend with VR-Engage functionality
51//!
52//! DtVreSetController is a template class that extends base set controllers with
53//! VR-Engage specific functionality for handling DtSetVreControlledRequest messages.
54//! These messages assign control of entity roles to specific VR-Engage player stations,
55//! supporting multi-station control of a single entity with different roles.
56//!
57//! The controller also handles the publishing of resource information for player-controlled
58//! entities and provides special handling for altitude setting commands.
59//!
60//! All VR-Engage specific set controllers should derive from this template class.
61//!
62//! Uses Descriptor Type: DtComponentDescriptor
63template <typename SetControllerBase>
64class VREVRFMODEL_DLL DtVreSetController : public DtVreSimComponent<SetControllerBase>
65{
66public:
67 //! \brief Constructor
68 //! \param name The name of this component
69 //! \param owner The local object that owns this component
70 //! \param simManager The simulation services manager
71 //! \param desc Component descriptor with configuration parameters
72 //! \param parentRegistry Optional parent registry for reader/writer functionality
73 //!
74 //! Creates a new VR-Engage set controller component with the specified parameters.
75 //! Initializes the resource warning flag.
76 DtVreSetController(const DtString& name, DtLocalObject* owner, DtSimulationServices* simManager,
77 DtComponentDescriptor* desc = 0, DtReaderWriterRegistry* parentRegistry = 0)
78 : DtVreSimComponent<SetControllerBase>(name, owner, simManager, desc, parentRegistry)
80 {
81 }
82
83 //! \brief Virtual destructor
84 //!
85 //! Cleans up resources used by the VR-Engage set controller component,
86 //! removing the set data callback for player control requests.
87 virtual ~DtVreSetController() override
88 {
89 this->entity()->setDataManager()->removeSetDataCallback(
91 }
92
93 //! \brief Sets the simulation radio and registers callbacks
94 //! \param radio The simulation radio to use for communication
95 //!
96 //! Overrides the base method to register the component's interest in
97 //! DtSetVreControlledType set commands. This ensures the component will
98 //! be notified when VR-Engage control assignment requests are received.
99 virtual void setRadio(DtSimRadio* radio)
100 {
101 SetControllerBase::setRadio(radio);
102
103 this->entity()->setDataManager()->addSetDataProcessorCallback(
105 }
106
107 //! \brief Static callback function for player control messages
108 //! \param msg The simulation message containing control assignment information
109 //! \param usrData User data pointer, typically pointing to an instance of this class
110 //!
111 //! Static function registered as a callback to handle VR-Engage control messages.
112 //! Forwards the message to the appropriate instance's processSetPlayerControlled method.
113 static void setPlayerControlledCallback(DtSimMessage* msg, void* usrData)
114 {
115 ((DtVreSetController*)usrData)->processSetPlayerControlled(msg);
116 }
117
119 {
121 DtString myStationId;
122 std::vector<std::string> myRoles;
123 };
124
125 //! \brief Processes a VR-Engage control assignment message
126 //! \param msg The simulation message containing control assignment information
127 //!
128 //! Processes DtSetVreControlledRequest messages, which assign or remove control of
129 //! entity roles to specific VR-Engage player stations. For each role, maintains a list
130 //! of controlling stations to support multi-station control of a single entity.
131 virtual void processSetPlayerControlled(DtSimMessage* msg)
132 {
133 DtSetDataRequestMessage* drMsg = static_cast<DtSetDataRequestMessage*>(msg);
134 DtSetVreControlledRequest* req = static_cast<DtSetVreControlledRequest*>(drMsg->setDataRequest());
135
136 // Queue the changes to happen later in tick, one per tick.
139 change.myStationId = req->stationId();
140 change.myRoles = req->roles();
141 myPendingControlChanges.push_back(change);
142 }
143
145 {
146 std::vector<std::string> keys = change.myRoles;
147
148 if (change.myControlled)
149 {
150 // unique identifier for each instance of VRE
151 DtString thisStation = change.myStationId;
152 if (thisStation.isEmpty())
153 {
154 LOG_WARN("VRESIM")
155 << "VRE set role controlled request has blank station id. Unable to identify controlling station."
156 << std::endl;
157 thisStation = "UNKNOWN";
158 }
159 // Surround the id with delimiting marks, since the id is an int,
160 // to avoid the possibility that one int will be found within another
161 thisStation = "{" + thisStation + "}";
162
163 for (int i = 0; i < keys.size(); i++)
164 {
165 std::string stations = this->entity()
166 ->getStateComponent<makVrf::DtVrfStateComponent>()
167 ->extendedData("role-" + keys[i])
168 .stdString();
169
170 // Check if this station is already in the list
171 if (stations.find(thisStation) == -1)
172 {
173 // It isn't, so append it to the list
174 this->entity()->getNextFrameStateComponent<makVrf::DtVrfStateComponent>()->setExtendedData(
175 "role-" + keys[i], stations + thisStation);
176 }
177 }
178 }
179 else
180 {
181 DtString thisStation = change.myStationId;
182 if (thisStation.isEmpty())
183 {
184 LOG_WARN("VRESIM")
185 << "VRE set role controlled request has blank station id. Unable to identify controlling station."
186 << std::endl;
187 thisStation = "UNKNOWN";
188 }
189 thisStation = "{" + thisStation + "}";
190
191 for (int i = 0; i < keys.size(); i++)
192 {
193 std::string stations = this->entity()
194 ->getStateComponent<makVrf::DtVrfStateComponent>()
195 ->extendedData("role-" + keys[i])
196 .stdString();
197 // only disable this role if we are the one occupying it
198 int foundAt = stations.find(thisStation);
199 if (foundAt > -1)
200 {
201 // Remove this station from the list
202 std::string pre = stations.substr(0, foundAt);
203 std::string post = stations.substr(foundAt + thisStation.length());
204 stations = pre + post;
205
206 if (stations.empty())
207 {
208 this->entity()->getNextFrameStateComponent<makVrf::DtVrfStateComponent>()->removeExtendedData(
209 "role-" + keys[i]);
210 }
211 else
212 {
213 this->entity()->getNextFrameStateComponent<makVrf::DtVrfStateComponent>()->setExtendedData(
214 "role-" + keys[i], stations);
215 }
216 }
217 }
218 }
219 }
220
221 //! \brief Initializes the controller before the first tick
222 //!
223 //! Performs initialization that must happen before the first tick but after
224 //! the component is fully constructed. Clears any existing role assignments
225 //! by removing all extended data entries with keys starting with "role-".
226 //! Then calls the base class preFirstTickInit().
227 virtual void preFirstTickInit()
228 {
229 // Search map for any entries with a key starting with "role-" and remove them
230 for (const std::pair<DtString, DtString>& data :
231 this->entity()->getStateComponent<makVrf::DtVrfStateComponent>()->extendedData())
232 {
233 if (data.first.findString("role-") == 0)
234 {
235 this->entity()->getNextFrameStateComponent<makVrf::DtVrfStateComponent>()->removeExtendedData(data.first);
236 }
237 }
238
240 }
241
242 //! \brief Updates the controller state each simulation frame
243 //!
244 //! Handles different behavior based on whether the entity is player controlled.
245 //! If not player controlled, operates normally under CGF control. If player controlled,
246 //! publishes detailed resource information in the entity's extended data for
247 //! use by the VR-Engage interface.
248 virtual void tick()
249 {
250 // Handle one control change each tick.
251 if (!myPendingControlChanges.empty())
252 {
254 myPendingControlChanges.pop_front();
256 }
257
258 if (!this->isPlayerControlled())
259 {
260 // The CGF is controlling the turret, so operate normally under CGF control.
262 }
263 }
264
265 //! \brief Processes altitude setting messages with enhanced terrain handling
266 //! \param msg The simulation message containing altitude setting information
267 //!
268 //! Overrides the base method to correctly handle setting altitude of 0 m above terrain.
269 //! Provides special handling for altitude values of 0, which are interpreted as
270 //! "place the entity directly on the terrain" rather than "place at 0 MSL".
271 //! Also handles terrain clamping appropriately.
272 virtual void processSetAltitude(DtSimMessage* msg)
273 {
274 DtSetDataRequestMessage* drMsg = (DtSetDataRequestMessage*)msg;
275 DtSetAltitudeRequest* req = (DtSetAltitudeRequest*)drMsg->setDataRequest();
276
277 // Set the actual altitude
278 DtVrfObjectStateRepository* vrfObjSR = this->localStateRepository();
279 if (!vrfObjSR)
280 {
281 return;
282 }
283
284 double altitude = req->altitude();
285 bool clamp = false;
286 DtTerrainIntersectStatus status;
287
288 double terrainHeight = this->physicalWorld()->terrainHeight(this->entity()->nextFrameLocalPosition(), status,
289 DtTerrainIntersectOptions::nonBlocking().withMask(
290 this->entity()->parameters()->movementTerrainCategoryMask()));
291
292 if (status.dataAvailable())
293 {
294 if (altitude == 0)
295 {
296 clamp = true;
297
298 if (status.dataAvailable())
299 {
300 altitude = terrainHeight;
301 }
302 }
303 else
304 {
305 if (status.dataAvailable())
306 {
307 altitude += terrainHeight;
308 }
309 }
310 }
311 else
312 {
313 // If the MSL is close to the terrain, clamp it
314 if (status.dataAvailable())
315 {
316 if (DtALMOST(terrainHeight, altitude, 1.0))
317 {
318 clamp = true;
319 }
320 }
321 }
322
323 DtGeodeticCoord newGeodeticPosition;
324 newGeodeticPosition.setGeocentric(this->entity()->nextFrameWorldPosition());
325 newGeodeticPosition.setAlt(altitude);
326 this->objectConsoleDebug(this->type()) << "Setting altitude: " << altitude << std::endl;
327
328 DtVector newLocalPosition;
329 this->terrainAttachedTo()->coordinateSystem()->dis2local(newGeodeticPosition.geocentric(), newLocalPosition);
330
331 // If we are setting altitude we should certainly not be clamping to ground!
332 DtVrfMovingObjectStateRepository* movingSR = vrfObjSR->as<DtVrfMovingObjectStateRepository>();
333
334 if (movingSR)
335 {
336 movingSR->setAmClampedToGround(clamp);
337 }
338
339 this->entity()->internalState()->place(newLocalPosition);
340
341 // this might work better if there is a good way to get a local topo dcm at MSL
342 // DtDcm topoToLocalDcm;
343 // DtVector localPosition = vrfObjSR->vrfKinematicState()->localPosition();
344 // // fixme might have to set localPositio to zero height here
345 // terrainAttachedTo()->coordinateSystem()->topoToLocal(localPosition, topoToLocalDcm);
346 // DtCoordTransform transform(localPosition, topoToLocalDcm);
347 // transform.coordTrans(DtVector(0, 0, -req->altitude()), localPosition);
348 //
349 // objectConsoleDebug(type()) << "Setting altitude: " << req->altitude() << std::endl;
350 // this->entity()->place(localPosition);
351 }
352
353 //! \brief Factory method to create a new instance of this component
354 //! \param name The name for the new component
355 //! \param owner The local object that will own this component
356 //! \param simManager The simulation services manager
357 //! \param desc Optional component descriptor
358 //! \param parentRegistry Optional parent registry for reader/writer functionality
359 //! \return Pointer to the newly created component
360 //!
361 //! Static factory method used by the component creation system to instantiate
362 //! new instances of this component type.
363 static DtSimComponent* creator(const DtString& name, DtLocalObject* owner, DtSimulationServices* simManager,
364 DtComponentDescriptor* desc = 0, DtReaderWriterRegistry* parentRegistry = 0)
365 {
366 return new DtVreSetController<SetControllerBase>(name, owner, simManager, desc, parentRegistry);
367 }
368
369private:
370 //! \brief Default constructor (not implemented)
371 //!
372 //! Default constructor is private and not implemented to prevent
373 //! creation of instances without proper initialization. Here because
374 //! the compiler would generate an unprotected one otherwise.
376
377 //! \brief Copy constructor (not implemented)
378 //!
379 //! Copy constructor is private and not implemented to prevent
380 //! copy construction.
382
383 //! \brief Assignment operator (not implemented)
384 //! \return Reference to this object
385 //!
386 //! Assignment operator is private and not implemented to prevent assignment.
388
389protected:
390 //! \brief Flag indicating if a resource warning has been logged
391 //!
392 //! Tracks whether a warning about exceeding the maximum resource string size
393 //! has already been logged, to prevent spamming the log with the same warning.
395
396 std::list<SetVreControlledContent> myPendingControlChanges;
397};
398} // namespace makVre
Provides assertion macros and functions for error detection and handling.
Request to set the VREngage control state of an entity.
Definition setVreControlled.h:38
virtual unsigned stationId() const
Gets the published ID of the player station.
virtual bool playerStationControlled() const
Gets the player station control state.
virtual std::vector< std::string > roles() const
Gets the controlled roles for the entity.
std::list< SetVreControlledContent > myPendingControlChanges
Definition vreSetController.h:396
virtual void setRadio(DtSimRadio *radio)
Sets the simulation radio and registers callbacks.
Definition vreSetController.h:99
DtVreSetController()
Default constructor (not implemented)
virtual ~DtVreSetController() override
Virtual destructor.
Definition vreSetController.h:87
virtual void processSetPlayerControlled(DtSimMessage *msg)
Processes a VR-Engage control assignment message.
Definition vreSetController.h:131
virtual void updatePlayerControlled(const SetVreControlledContent &change)
Definition vreSetController.h:144
bool myWarnedAboutResource
Flag indicating if a resource warning has been logged.
Definition vreSetController.h:394
virtual void processSetAltitude(DtSimMessage *msg)
Processes altitude setting messages with enhanced terrain handling.
Definition vreSetController.h:272
static DtSimComponent * creator(const DtString &name, DtLocalObject *owner, DtSimulationServices *simManager, DtComponentDescriptor *desc=0, DtReaderWriterRegistry *parentRegistry=0)
Factory method to create a new instance of this component.
Definition vreSetController.h:363
DtVreSetController(const DtVreSetController &orig)
Copy constructor (not implemented)
virtual void tick()
Updates the controller state each simulation frame.
Definition vreSetController.h:248
const DtVreSetController & operator=(const DtVreSetController &orig)
Assignment operator (not implemented)
static void setPlayerControlledCallback(DtSimMessage *msg, void *usrData)
Static callback function for player control messages.
Definition vreSetController.h:113
DtVreSetController(const DtString &name, DtLocalObject *owner, DtSimulationServices *simManager, DtComponentDescriptor *desc=0, DtReaderWriterRegistry *parentRegistry=0)
Constructor.
Definition vreSetController.h:76
virtual void preFirstTickInit()
Initializes the controller before the first tick.
Definition vreSetController.h:227
DtVreSimComponent(const DtString &name, DtLocalObject *owner, DtSimulationServices *simManager, DtComponentDescriptor *desc=0, DtReaderWriterRegistry *parentRegistry=0)
Definition vreSimComponent.h:54
bool isPlayerControlled(const std::string &role="") const
Definition vreSimComponent.h:76
Defines export macros for the vreVrfmodel library.
#define VREVRFMODEL_DLL
Definition export.h:22
Provides logging functionality with various severity levels and channels.
#define LOG_WARN(channel)
Macro to log a warning message to log files.
Definition logger.h:69
Include export definitions for this library.
Definition glsVreMessageUtil.h:49
const DtSetMessageType DtSetVreControlledType("set-vre-controlled")
Message type for VREngage control state requests.
VREngage control state request message.
std::vector< std::string > myRoles
Definition vreSetController.h:122
DtString myStationId
Definition vreSetController.h:121
bool myControlled
Definition vreSetController.h:120
Base template class for VR-Engage simulation components.