2 // Copyright (C) 2009 - 2012 Mathias Fröhlich <Mathias.Froehlich@web.de>
4 // This program is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU General Public License as
6 // published by the Free Software Foundation; either version 2 of the
7 // License, or (at your option) any later version.
9 // This program is distributed in the hope that it will be useful, but
10 // WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 // General Public License for more details.
14 // You should have received a copy of the GNU General Public License
15 // along with this program; if not, write to the Free Software
16 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
25 #include <simgear/compiler.h>
33 #include <simgear/debug/logstream.hxx>
34 #include <simgear/math/SGMath.hxx>
35 #include <simgear/misc/sg_path.hxx>
36 #include <simgear/props/props.hxx>
37 #include <simgear/structure/exception.hxx>
38 #include <simgear/xml/easyxml.hxx>
40 #include <simgear/hla/HLAFederate.hxx>
41 #include <simgear/hla/HLAArrayDataElement.hxx>
42 #include <simgear/hla/HLADataElement.hxx>
43 #include <simgear/hla/HLADataType.hxx>
44 #include <simgear/hla/HLALocation.hxx>
45 #include <simgear/hla/HLAObjectClass.hxx>
46 #include <simgear/hla/HLAObjectInstance.hxx>
47 #include <simgear/hla/HLAPropertyDataElement.hxx>
48 #include <simgear/hla/HLAVariantRecordDataElement.hxx>
50 #include <AIModel/AIMultiplayer.hxx>
51 #include <AIModel/AIManager.hxx>
53 #include <FDM/flightProperties.hxx>
55 #include <Main/fg_props.hxx>
56 #include <Main/globals.hxx>
59 // FIXME make classes here private ...
61 namespace sg = simgear;
69 class FGHLA::XMLConfigReader : public XMLVisitor {
75 const std::string& getFederateObjectModel() const
76 { return _federateObjectModel; }
78 HLAVersion getRTIVersion() const
79 { return _rtiVersion; }
80 const std::list<std::string>& getRTIArguments() const
81 { return _rtiArguments; }
86 std::string _inProperty;
87 std::string _outProperty;
89 typedef std::list<DataElement> DataElementList;
92 DataElementList _dataElementList;
93 std::list<std::pair<std::string, std::string> > _modelMap;
95 struct SimTimeConfig {
97 DataElementList _dataElementList;
99 struct PositionConfig {
101 DataElementList _dataElementList;
103 struct MPPropertiesConfig {
106 struct ObjectClassConfig {
109 ModelConfig _modelConfig;
110 SimTimeConfig _simTimeConfig;
111 PositionConfig _positionConfig;
112 MPPropertiesConfig _mpPropertiesConfig;
113 DataElementList _dataElementList;
115 typedef std::list<ObjectClassConfig> ObjectClassConfigList;
117 const ObjectClassConfigList& getObjectClassConfigList() const
118 { return _objectClassConfigList; }
121 virtual void startXML()
123 _modeStack.push(TopLevelMode);
125 virtual void endXML()
129 virtual void startElement(const char* name, const XMLAttributes& atts)
131 if (strcmp(name, "hlaConfiguration") == 0) {
132 if (!isModeStackTop(TopLevelMode))
133 throw sg_exception("hlaConfiguration tag not at top level");
135 _modeStack.push(HLAConfigurationMode);
138 else if (strcmp(name, "rti") == 0) {
139 if (!isModeStackTop(HLAConfigurationMode))
140 throw sg_exception("rti tag not at top level");
142 std::string rtiVersion = getAttribute("version", atts);
143 if (rtiVersion == "RTI13")
145 else if (rtiVersion == "RTI1516")
146 _rtiVersion = RTI1516;
147 else if (rtiVersion == "RTI1516E")
148 _rtiVersion = RTI1516E;
150 throw sg_exception("invalid version attribute in rti tag");
152 _modeStack.push(RTIMode);
154 else if (strcmp(name, "argument") == 0) {
155 if (!isModeStackTop(RTIMode))
156 throw sg_exception("argument tag not inside an rti tag");
158 _modeStack.push(RTIArgumentMode);
159 _rtiArguments.push_back(std::string());
162 else if (strcmp(name, "objects") == 0) {
163 if (!isModeStackTop(HLAConfigurationMode))
164 throw sg_exception("objects tag not at top level");
166 _modeStack.push(ObjectsMode);
168 else if (strcmp(name, "objectClass") == 0) {
169 // Currently this is flat and only allows one class to be configured
170 if (!isModeStackTop(ObjectsMode))
171 throw sg_exception("objects tag not inside an objects tag");
172 if (!_objectClassConfigList.empty())
173 throw sg_exception("currently only one objectClass is allowed");
174 // if (!isModeStackTop(ObjectsMode) && !isModeStackTop(ObjectClassMode))
175 // throw sg_exception("objects tag not inside an objects or objectClass tag");
177 ObjectClassConfig objectClassConfig;
178 objectClassConfig._type = getAttribute("type", atts);
179 objectClassConfig._name = getAttribute("name", atts);
180 _objectClassConfigList.push_back(objectClassConfig);
182 _modeStack.push(ObjectClassMode);
184 else if (strcmp(name, "model") == 0) {
185 if (!isModeStackTop(ObjectClassMode))
186 throw sg_exception("position tag not inside an objectClass tag");
188 _objectClassConfigList.back()._modelConfig._type = getAttribute("type", atts);
190 _modeStack.push(ModelMode);
192 else if (strcmp(name, "key") == 0) {
193 if (!isModeStackTop(ModelMode))
194 throw sg_exception("key tag not inside an model tag");
196 std::pair<std::string,std::string> p;
197 p.first = getAttribute("external", atts);
198 p.second = getAttribute("modelPath", atts);
199 _objectClassConfigList.back()._modelConfig._modelMap.push_back(p);
201 _modeStack.push(KeyMode);
203 else if (strcmp(name, "simTime") == 0) {
204 if (!isModeStackTop(ObjectClassMode))
205 throw sg_exception("simTime tag not inside an objectClass tag");
207 _objectClassConfigList.back()._simTimeConfig._type = getAttribute("type", atts);
209 _modeStack.push(SimTimeMode);
211 else if (strcmp(name, "position") == 0) {
212 if (!isModeStackTop(ObjectClassMode))
213 throw sg_exception("position tag not inside an objectClass tag");
215 _objectClassConfigList.back()._positionConfig._type = getAttribute("type", atts);
217 _modeStack.push(PositionMode);
219 else if (strcmp(name, "mpProperties") == 0) {
220 if (!isModeStackTop(ObjectClassMode))
221 throw sg_exception("mpProperties tag not inside an objectClass tag");
223 _objectClassConfigList.back()._mpPropertiesConfig._name = getAttribute("name", atts);
225 _modeStack.push(MPPropertiesMode);
227 else if (strcmp(name, "dataElement") == 0) {
229 DataElement dataElement;
230 dataElement._type = getAttribute("type", atts);
231 dataElement._name = getAttribute("name", atts);
232 if (isModeStackTop(ModelMode)) {
233 _objectClassConfigList.back()._modelConfig._dataElementList.push_back(dataElement);
234 } else if (isModeStackTop(SimTimeMode)) {
235 _objectClassConfigList.back()._simTimeConfig._dataElementList.push_back(dataElement);
236 } else if (isModeStackTop(PositionMode)) {
237 _objectClassConfigList.back()._positionConfig._dataElementList.push_back(dataElement);
238 } else if (isModeStackTop(ObjectClassMode)) {
239 std::string io = getAttribute("io", atts);
240 dataElement._inProperty = getAttribute("in", atts);
241 if (dataElement._inProperty.empty())
242 dataElement._inProperty = io;
243 dataElement._outProperty = getAttribute("out", atts);
244 if (dataElement._outProperty.empty())
245 dataElement._outProperty = io;
246 _objectClassConfigList.back()._dataElementList.push_back(dataElement);
248 throw sg_exception("dataElement tag not inside a position, model or objectClass tag");
251 _modeStack.push(DataElementMode);
254 else if (strcmp(name, "federateObjectModel") == 0) {
255 if (!isModeStackTop(HLAConfigurationMode))
256 throw sg_exception("federateObjectModel tag not at top level");
258 _federateObjectModel = getAttribute("name", atts);
259 _modeStack.push(FederateObjectModelMode);
263 throw sg_exception(std::string("Unknown tag ") + name);
267 virtual void data(const char * s, int length)
269 if (isModeStackTop(RTIArgumentMode)) {
270 _rtiArguments.back().append(s, length);
274 virtual void endElement(const char* name)
279 std::string getAttribute(const char* name, const XMLAttributes& atts)
281 int index = atts.findAttribute(name);
282 if (index < 0 || atts.size() <= index)
283 return std::string();
284 return std::string(atts.getValue(index));
286 std::string getAttribute(const std::string& name, const XMLAttributes& atts)
288 int index = atts.findAttribute(name.c_str());
289 if (index < 0 || atts.size() <= index)
290 return std::string();
291 return std::string(atts.getValue(index));
295 // poor peoples xml validation ...
298 HLAConfigurationMode,
309 FederateObjectModelMode
311 bool isModeStackTop(Mode mode)
313 if (_modeStack.empty())
315 return _modeStack.top() == mode;
317 std::stack<Mode> _modeStack;
319 std::string _federateObjectModel;
320 HLAVersion _rtiVersion;
321 std::list<std::string> _rtiArguments;
323 ObjectClassConfigList _objectClassConfigList;
326 class PropertyReferenceSet : public SGReferenced {
328 void insert(const std::string& relativePath, const SGSharedPtr<sg::HLAPropertyDataElement>& dataElement)
330 if (_rootNode.valid())
331 dataElement->setPropertyNode(_rootNode->getNode(relativePath, true));
332 _pathDataElementPairList.push_back(PathDataElementPair(relativePath, dataElement));
335 void setRootNode(SGPropertyNode* rootNode)
337 _rootNode = rootNode;
338 for (PathDataElementPairList::iterator i = _pathDataElementPairList.begin();
339 i != _pathDataElementPairList.end(); ++i) {
340 i->second->setPropertyNode(_rootNode->getNode(i->first, true));
343 SGPropertyNode* getRootNode()
344 { return _rootNode.get(); }
347 SGSharedPtr<SGPropertyNode> _rootNode;
349 typedef std::pair<std::string, SGSharedPtr<sg::HLAPropertyDataElement> > PathDataElementPair;
350 typedef std::list<PathDataElementPair> PathDataElementPairList;
351 PathDataElementPairList _pathDataElementPairList;
354 class AbstractSimTime : public SGReferenced {
356 virtual ~AbstractSimTime() {}
358 virtual double getTimeStamp() const = 0;
359 virtual void setTimeStamp(double) = 0;
362 class AbstractModel : public SGReferenced {
364 virtual ~AbstractModel() {}
366 virtual std::string getModelPath() const = 0;
367 virtual void setModelPath(const std::string&) = 0;
370 // Factory class that is used to create an apternative data element for the multiplayer property attribute
371 class MPPropertyVariantRecordDataElementFactory :
372 public sg::HLAVariantArrayDataElement::AlternativeDataElementFactory {
374 MPPropertyVariantRecordDataElementFactory(PropertyReferenceSet* propertyReferenceSet) :
375 _propertyReferenceSet(propertyReferenceSet)
378 virtual sg::HLADataElement* createElement(const sg::HLAVariantRecordDataElement& variantRecordDataElement, unsigned index)
380 const sg::HLAVariantRecordDataType* dataType = variantRecordDataElement.getDataType();
383 const sg::HLAEnumeratedDataType* enumDataType = dataType->getEnumeratedDataType();
386 const sg::HLADataType* alternativeDataType = dataType->getAlternativeDataType(index);
387 if (!alternativeDataType)
390 // The relative property path should be in the semantics field name
391 std::string relativePath = dataType->getAlternativeSemantics(index);
392 sg::HLAPropertyDataElement* dataElement = new sg::HLAPropertyDataElement(alternativeDataType, (SGPropertyNode*)0);
393 _propertyReferenceSet->insert(relativePath, dataElement);
398 SGSharedPtr<PropertyReferenceSet> _propertyReferenceSet;
401 class FGHLA::MultiplayerObjectInstance : public sg::HLAObjectInstance {
403 MultiplayerObjectInstance(sg::HLAObjectClass* objectClass) :
404 sg::HLAObjectInstance(objectClass),
405 _propertyReferenceSet(new PropertyReferenceSet),
406 _mpProperties(new sg::HLAVariantArrayDataElement)
408 _mpProperties->setAlternativeDataElementFactory(new MPPropertyVariantRecordDataElementFactory(_propertyReferenceSet.get()));
410 virtual ~MultiplayerObjectInstance()
413 SGSharedPtr<PropertyReferenceSet> _propertyReferenceSet;
414 SGSharedPtr<sg::HLAAbstractLocation> _location;
415 SGSharedPtr<AbstractSimTime> _simTime;
416 SGSharedPtr<AbstractModel> _model;
417 SGSharedPtr<sg::HLAVariantArrayDataElement> _mpProperties;
420 class FGHLA::MPUpdateCallback : public sg::HLAObjectInstance::UpdateCallback {
422 virtual void updateAttributeValues(sg::HLAObjectInstance& objectInstance, const sg::RTIData& tag)
424 updateAttributeValues(static_cast<MultiplayerObjectInstance&>(objectInstance));
425 objectInstance.encodeAttributeValues();
426 objectInstance.sendAttributeValues(tag);
429 virtual void updateAttributeValues(sg::HLAObjectInstance& objectInstance, const SGTimeStamp& timeStamp, const sg::RTIData& tag)
431 updateAttributeValues(static_cast<MultiplayerObjectInstance&>(objectInstance));
432 objectInstance.encodeAttributeValues();
433 objectInstance.sendAttributeValues(timeStamp, tag);
436 void updateAttributeValues(MultiplayerObjectInstance& objectInstance)
438 objectInstance._simTime->setTimeStamp(globals->get_sim_time_sec());
440 SGGeod position = _ifce.getPosition();
441 // The quaternion rotating from the earth centered frame to the
442 // horizontal local frame
443 SGQuatd qEc2Hl = SGQuatd::fromLonLat(position);
444 SGQuatd hlOr = SGQuatd::fromYawPitchRoll(_ifce.get_Psi(), _ifce.get_Theta(), _ifce.get_Phi());
445 objectInstance._location->setCartPosition(SGVec3d::fromGeod(position));
446 objectInstance._location->setCartOrientation(qEc2Hl*hlOr);
447 // The angular velocitied in the body frame
448 double p = _ifce.get_P_body();
449 double q = _ifce.get_Q_body();
450 double r = _ifce.get_R_body();
451 objectInstance._location->setAngularBodyVelocity(SGVec3d(p, q, r));
452 // The body uvw velocities in the interface are wrt the wind instead
453 // of wrt the ec frame
454 double n = _ifce.get_V_north()*SG_FEET_TO_METER;
455 double e = _ifce.get_V_east()*SG_FEET_TO_METER;
456 double d = _ifce.get_V_down()*SG_FEET_TO_METER;
457 objectInstance._location->setLinearBodyVelocity(hlOr.transform(SGVec3d(n, e, d)));
459 if (objectInstance._mpProperties.valid() && objectInstance._mpProperties->getNumElements() == 0) {
460 if (objectInstance._propertyReferenceSet.valid() && objectInstance._propertyReferenceSet->getRootNode()) {
461 const sg::HLADataType* elementDataType = objectInstance._mpProperties->getElementDataType();
462 const sg::HLAVariantRecordDataType* variantRecordDataType = elementDataType->toVariantRecordDataType();
463 for (unsigned i = 0, count = 0; i < variantRecordDataType->getNumAlternatives(); ++i) {
464 std::string name = variantRecordDataType->getAlternativeSemantics(i);
465 SGPropertyNode* node = objectInstance._propertyReferenceSet->getRootNode()->getNode(name);
468 objectInstance._mpProperties->getOrCreateElement(count++)->setAlternativeIndex(i);
474 FlightProperties _ifce;
477 class FGHLA::MPReflectCallback : public sg::HLAObjectInstance::ReflectCallback {
479 virtual void reflectAttributeValues(sg::HLAObjectInstance& objectInstance,
480 const sg::HLAIndexList& indexList, const sg::RTIData& tag)
482 objectInstance.reflectAttributeValues(indexList, tag);
483 reflectAttributeValues(static_cast<MultiplayerObjectInstance&>(objectInstance));
485 virtual void reflectAttributeValues(sg::HLAObjectInstance& objectInstance, const sg::HLAIndexList& indexList,
486 const SGTimeStamp& timeStamp, const sg::RTIData& tag)
488 objectInstance.reflectAttributeValues(indexList, timeStamp, tag);
489 reflectAttributeValues(static_cast<MultiplayerObjectInstance&>(objectInstance));
492 void reflectAttributeValues(MultiplayerObjectInstance& objectInstance)
494 // Puh, damn ordering problems with properties startup and so on
495 if (_aiMultiplayer.valid()) {
496 FGExternalMotionData motionInfo;
497 motionInfo.time = objectInstance._simTime->getTimeStamp();
500 motionInfo.position = objectInstance._location->getCartPosition();
501 motionInfo.orientation = toQuatf(objectInstance._location->getCartOrientation());
502 motionInfo.linearVel = toVec3f(objectInstance._location->getLinearBodyVelocity());
503 motionInfo.angularVel = toVec3f(objectInstance._location->getAngularBodyVelocity());
504 motionInfo.linearAccel = SGVec3f::zeros();
505 motionInfo.angularAccel = SGVec3f::zeros();
507 _aiMultiplayer->addMotionInfo(motionInfo, SGTimeStamp::now().getSeconds());
510 std::string modelPath = objectInstance._model->getModelPath();
511 if (modelPath.empty())
514 aiMgr = static_cast<FGAIManager*>(globals->get_subsystem("ai-model"));
518 _aiMultiplayer = new FGAIMultiplayer;
519 _aiMultiplayer->setPath(modelPath.c_str());
520 aiMgr->attach(_aiMultiplayer.get());
522 objectInstance._propertyReferenceSet->setRootNode(_aiMultiplayer->getPropertyRoot());
528 if (!_aiMultiplayer.valid())
530 _aiMultiplayer->setDie(true);
534 SGSharedPtr<FGAIMultiplayer> _aiMultiplayer;
537 class SimTimeFactory : public SGReferenced {
539 virtual ~SimTimeFactory() {}
540 virtual AbstractSimTime* createSimTime(sg::HLAObjectInstance&) const = 0;
543 // A SimTime implementation that works with the simulation
544 // time in an attribute. Used when we cannot do real time management.
545 class AttributeSimTimeFactory : public SimTimeFactory {
547 class SimTime : public AbstractSimTime {
549 virtual double getTimeStamp() const
551 virtual void setTimeStamp(double simTime)
552 { _simTime = simTime; }
554 sg::HLADataElement* getDataElement()
555 { return _simTime.getDataElement(); }
558 sg::HLADoubleData _simTime;
561 virtual AbstractSimTime* createSimTime(sg::HLAObjectInstance& objectInstance) const
563 SimTime* simTime = new SimTime;
564 objectInstance.setAttributeDataElement(_simTimeIndex, simTime->getDataElement());
567 void setSimTimeIndex(const sg::HLADataElementIndex& simTimeIndex)
568 { _simTimeIndex = simTimeIndex; }
571 sg::HLADataElementIndex _simTimeIndex;
574 // A SimTime implementation that works with the simulation
575 // time in two attributes like the avation sim net fom works
576 class MSecAttributeSimTimeFactory : public SimTimeFactory {
578 class SimTime : public AbstractSimTime {
580 virtual double getTimeStamp() const
582 return _secSimTime + 1e-3*_msecSimTime;
584 virtual void setTimeStamp(double simTime)
586 double sec = floor(simTime);
588 _msecSimTime = 1e3*(simTime - sec);
591 sg::HLADataElement* getSecDataElement()
592 { return _secSimTime.getDataElement(); }
593 sg::HLADataElement* getMSecDataElement()
594 { return _msecSimTime.getDataElement(); }
597 sg::HLADoubleData _secSimTime;
598 sg::HLADoubleData _msecSimTime;
601 virtual AbstractSimTime* createSimTime(sg::HLAObjectInstance& objectInstance) const
603 SimTime* simTime = new SimTime;
604 objectInstance.setAttributeDataElement(_secIndex, simTime->getSecDataElement());
605 objectInstance.setAttributeDataElement(_msecIndex, simTime->getMSecDataElement());
608 void setSecIndex(const sg::HLADataElementIndex& secIndex)
609 { _secIndex = secIndex; }
610 void setMSecIndex(const sg::HLADataElementIndex& msecIndex)
611 { _msecIndex = msecIndex; }
614 sg::HLADataElementIndex _secIndex;
615 sg::HLADataElementIndex _msecIndex;
618 class ModelFactory : public SGReferenced {
620 virtual ~ModelFactory() {}
621 virtual AbstractModel* createModel(sg::HLAObjectInstance& objectInstance) const = 0;
624 class AttributeModelFactory : public ModelFactory {
626 class Model : public AbstractModel {
628 virtual std::string getModelPath() const
629 { return _modelPath; }
630 virtual void setModelPath(const std::string& modelPath)
631 { _modelPath = modelPath; }
633 sg::HLADataElement* getDataElement()
634 { return _modelPath.getDataElement(); }
637 sg::HLAStringData _modelPath;
640 virtual AbstractModel* createModel(sg::HLAObjectInstance& objectInstance) const
642 Model* model = new Model;
643 objectInstance.setAttributeDataElement(_modelIndex, model->getDataElement());
646 void setModelIndex(const sg::HLADataElementIndex& modelIndex)
647 { _modelIndex = modelIndex; }
650 sg::HLADataElementIndex _modelIndex;
653 class AttributeMapModelFactory : public ModelFactory {
655 class Model : public AbstractModel {
657 Model(const AttributeMapModelFactory* mapModelFactory) :
658 _mapModelFactory(mapModelFactory)
661 virtual std::string getModelPath() const
663 if (_modelPath.getValue().empty())
664 return _modelPath.getValue();
665 return _mapModelFactory->mapToFlightgear(_modelPath);
667 virtual void setModelPath(const std::string& modelPath)
668 { _modelPath = _mapModelFactory->mapToExternal(modelPath); }
670 sg::HLADataElement* getDataElement()
671 { return _modelPath.getDataElement(); }
674 sg::HLAStringData _modelPath;
675 SGSharedPtr<const AttributeMapModelFactory> _mapModelFactory;
678 virtual AbstractModel* createModel(sg::HLAObjectInstance& objectInstance) const
680 Model* model = new Model(this);
681 objectInstance.setAttributeDataElement(_modelIndex, model->getDataElement());
684 void setModelIndex(const sg::HLADataElementIndex& modelIndex)
685 { _modelIndex = modelIndex; }
687 std::string mapToFlightgear(const std::string& externalModel) const
689 std::map<std::string,std::string>::const_iterator i;
690 i = _externalToModelPathMap.find(externalModel);
691 if (i != _externalToModelPathMap.end())
695 std::string mapToExternal(const std::string& modelPath) const
697 std::map<std::string,std::string>::const_iterator i;
698 i = _modelPathToExternalMap.find(modelPath);
699 if (i != _modelPathToExternalMap.end())
701 return _externalDefault;
704 void setExternalDefault(const std::string& externalDefault)
705 { _externalDefault = externalDefault; }
706 void addExternalModelPathPair(const std::string& external, const std::string& modelPath)
708 _externalToModelPathMap[external] = modelPath;
709 _modelPathToExternalMap[modelPath] = external;
713 sg::HLADataElementIndex _modelIndex;
715 std::map<std::string,std::string> _externalToModelPathMap;
716 std::map<std::string,std::string> _modelPathToExternalMap;
717 std::string _externalDefault;
720 class FGHLA::MultiplayerObjectClass : public sg::HLAObjectClass {
722 MultiplayerObjectClass(const std::string& name, sg::HLAFederate* federate) :
723 HLAObjectClass(name, federate)
725 virtual ~MultiplayerObjectClass()
728 virtual MultiplayerObjectInstance* createObjectInstance(const std::string& name)
729 { return new MultiplayerObjectInstance(this); }
731 virtual void discoverInstance(sg::HLAObjectInstance& objectInstance, const sg::RTIData& tag)
733 HLAObjectClass::discoverInstance(objectInstance, tag);
734 MPReflectCallback* reflectCallback = new MPReflectCallback;
735 objectInstance.setReflectCallback(reflectCallback);
736 attachDataElements(static_cast<MultiplayerObjectInstance&>(objectInstance), false);
738 virtual void removeInstance(sg::HLAObjectInstance& objectInstance, const sg::RTIData& tag)
740 HLAObjectClass::removeInstance(objectInstance, tag);
741 MPReflectCallback* reflectCallback;
742 reflectCallback = dynamic_cast<MPReflectCallback*>(objectInstance.getReflectCallback().get());
743 if (!reflectCallback) {
744 SG_LOG(SG_IO, SG_WARN, "HLA: expected to have a different attribute callback in remove instance.");
747 reflectCallback->setDie();
749 virtual void registerInstance(sg::HLAObjectInstance& objectInstance)
751 HLAObjectClass::registerInstance(objectInstance);
752 MPUpdateCallback* updateCallback = new MPUpdateCallback;
753 objectInstance.setUpdateCallback(updateCallback);
754 MultiplayerObjectInstance& mpObjectInstance = static_cast<MultiplayerObjectInstance&>(objectInstance);
755 attachDataElements(mpObjectInstance, true);
756 mpObjectInstance._model->setModelPath(fgGetString("/sim/model/path", "default"));
757 mpObjectInstance._propertyReferenceSet->setRootNode(fgGetNode("/", true));
759 virtual void deleteInstance(sg::HLAObjectInstance& objectInstance)
761 HLAObjectClass::deleteInstance(objectInstance);
764 virtual void createAttributeDataElements(sg::HLAObjectInstance& objectInstance)
766 sg::HLAObjectClass::createAttributeDataElements(objectInstance);
769 void setLocationFactory(sg::HLALocationFactory* locationFactory)
770 { _locationFactory = locationFactory; }
771 sg::HLALocationFactory* getLocationFactory()
772 { return _locationFactory.get(); }
774 void setSimTimeFactory(SimTimeFactory* simTimeFactory)
775 { _simTimeFactory = simTimeFactory; }
776 SimTimeFactory* getSimTimeFactory()
777 { return _simTimeFactory.get(); }
779 void setModelFactory(ModelFactory* modelFactory)
780 { _modelFactory = modelFactory; }
781 ModelFactory* getModelFactory()
782 { return _modelFactory.get(); }
784 void setMPPropertiesIndex(const sg::HLADataElementIndex& index)
785 { _mpPropertiesIndex = index; }
786 const sg::HLADataElementIndex& getMPPropertiesIndex() const
787 { return _mpPropertiesIndex; }
789 void setInputProperty(const sg::HLADataElementIndex& index, const std::string& property)
791 _inputProperties[index] = property;
793 void setOutputProperty(const sg::HLADataElementIndex& index, const std::string& property)
795 _outputProperties[index] = property;
799 void attachDataElements(MultiplayerObjectInstance& objectInstance, bool outgoing)
801 objectInstance.setAttributeDataElement(_mpPropertiesIndex, objectInstance._mpProperties.get());
803 if (_locationFactory.valid())
804 objectInstance._location = _locationFactory->createLocation(objectInstance);
805 if (_modelFactory.valid())
806 objectInstance._model = _modelFactory->createModel(objectInstance);
807 if (_simTimeFactory.valid())
808 objectInstance._simTime = _simTimeFactory->createSimTime(objectInstance);
811 attachPropertyDataElements(*objectInstance._propertyReferenceSet,
812 objectInstance, _outputProperties);
814 attachPropertyDataElements(*objectInstance._propertyReferenceSet,
815 objectInstance, _inputProperties);
817 typedef std::map<sg::HLADataElementIndex, std::string> IndexPropertyMap;
819 void attachPropertyDataElements(PropertyReferenceSet& propertyReferenceSet,
820 sg::HLAObjectInstance& objectInstance,
821 const IndexPropertyMap& attributePathPropertyMap)
823 for (IndexPropertyMap::const_iterator i = attributePathPropertyMap.begin();
824 i != attributePathPropertyMap.end(); ++i) {
825 sg::HLAPropertyDataElement* dataElement = new sg::HLAPropertyDataElement;
826 propertyReferenceSet.insert(i->second, dataElement);
827 objectInstance.setAttributeDataElement(i->first, dataElement);
831 IndexPropertyMap _inputProperties;
832 IndexPropertyMap _outputProperties;
834 SGSharedPtr<sg::HLALocationFactory> _locationFactory;
835 SGSharedPtr<SimTimeFactory> _simTimeFactory;
836 SGSharedPtr<ModelFactory> _modelFactory;
838 sg::HLADataElementIndex _mpPropertiesIndex;
841 class FGHLA::Federate : public sg::HLAFederate {
845 virtual bool readObjectModel()
846 { return readRTI1516ObjectModelTemplate(getFederationObjectModel()); }
848 virtual sg::HLAObjectClass* createObjectClass(const std::string& name)
850 if (std::find(_multiplayerObjectClassNames.begin(), _multiplayerObjectClassNames.end(), name)
851 != _multiplayerObjectClassNames.end()) {
852 if (_localAircraftClass.valid()) {
853 return new MultiplayerObjectClass(name, this);
855 _localAircraftClass = new MultiplayerObjectClass(name, this);
856 return _localAircraftClass.get();
863 void updateLocalAircraftInstance()
865 // First push our own data so that others can recieve ...
866 if (!_localAircraftClass.valid())
868 if (!_localAircraftInstance.valid()) {
869 _localAircraftInstance = new MultiplayerObjectInstance(_localAircraftClass.get());
870 _localAircraftInstance->registerInstance();
872 _localAircraftInstance->updateAttributeValues(sg::RTIData("MPAircraft"));
875 virtual bool shutdown()
877 if (_localAircraftInstance.valid()) {
878 // Remove the local object from the rti
879 _localAircraftInstance->deleteInstance(simgear::RTIData("gone"));
880 _localAircraftInstance = 0;
882 return HLAFederate::shutdown();
885 std::list<std::string> _multiplayerObjectClassNames;
886 /// This class is used to register the local instance and to subscribe for others
887 SGSharedPtr<MultiplayerObjectClass> _localAircraftClass;
888 /// The local aircraft instance
889 SGSharedPtr<MultiplayerObjectInstance> _localAircraftInstance;
892 FGHLA::FGHLA(const std::vector<std::string>& tokens) :
893 _hlaFederate(new Federate)
895 if (1 < tokens.size() && !tokens[1].empty())
896 set_direction(tokens[1]);
901 if (2 < tokens.size() && !tokens[2].empty()) {
902 std::stringstream ss(tokens[2]);
907 if (3 < tokens.size() && !tokens[3].empty())
908 _federate = tokens[3];
910 _federate = fgGetString("/sim/user/callsign");
912 if (4 < tokens.size() && !tokens[4].empty())
913 _federation = tokens[4];
915 _federation = "FlightGear";
917 if (5 < tokens.size() && !tokens[5].empty())
918 _objectModelConfig = tokens[5];
920 _objectModelConfig = "mp-aircraft.xml";
931 SG_LOG(SG_IO, SG_ALERT, "This shouldn't happen, but the channel "
932 "is already in use, ignoring");
936 if (_federation.empty()) {
937 SG_LOG(SG_IO, SG_ALERT, "No federation to join given to "
942 if (_federate.empty()) {
943 SG_LOG(SG_IO, SG_ALERT, "No federate name given to the HLA module");
947 if (!SGPath(_objectModelConfig).exists()) {
948 SGPath path(globals->get_fg_root());
950 path.append(_objectModelConfig);
952 _objectModelConfig = path.str();
954 SG_LOG(SG_IO, SG_ALERT, "Federate object model configuration \""
955 << _objectModelConfig << "\" does not exist.");
960 XMLConfigReader configReader;
962 readXML(_objectModelConfig, configReader);
963 } catch (const sg_throwable& e) {
964 SG_LOG(SG_IO, SG_ALERT, "Could not open HLA configuration file: "
968 SG_LOG(SG_IO, SG_ALERT, "Could not open HLA configuration file");
972 // Flightgears hla configuration
973 std::string objectModel = configReader.getFederateObjectModel();
974 if (objectModel.empty()) {
975 SG_LOG(SG_IO, SG_ALERT, "No federate object model given to "
979 if (!SGPath(objectModel).exists()) {
980 SGPath path(SGPath(_objectModelConfig).dir());
981 path.append(objectModel);
983 objectModel = path.str();
985 SG_LOG(SG_IO, SG_ALERT, "Federate object model file \""
986 << objectModel << "\" does not exist.");
991 // We need that to communicate to the rti
992 switch (configReader.getRTIVersion()) {
994 _hlaFederate->setVersion(simgear::HLAFederate::RTI13);
997 _hlaFederate->setVersion(simgear::HLAFederate::RTI1516);
1000 _hlaFederate->setVersion(simgear::HLAFederate::RTI1516E);
1003 _hlaFederate->setConnectArguments(configReader.getRTIArguments());
1004 _hlaFederate->setFederationExecutionName(_federation);
1005 _hlaFederate->setFederationObjectModel(objectModel);
1006 _hlaFederate->setFederateType(_federate);
1008 // Store the multiplayer class name in the federate
1009 XMLConfigReader::ObjectClassConfigList::const_iterator i;
1010 for (i = configReader.getObjectClassConfigList().begin();
1011 i != configReader.getObjectClassConfigList().end(); ++i) {
1013 if (i->_type != "Multiplayer") {
1014 SG_LOG(SG_IO, SG_ALERT, "Ignoring unknown object class type \"" << i->_type << "\"!");
1018 // Register the object class that we need for this simple hla implementation
1019 _hlaFederate->_multiplayerObjectClassNames.push_back(i->_name);
1023 // Now that it is paramtrized, connect/join
1024 if (!_hlaFederate->init()) {
1025 SG_LOG(SG_IO, SG_ALERT, "Could not init the hla/rti connect.");
1029 // bool publish = get_direction() & SG_IO_OUT;
1030 // bool subscribe = get_direction() & SG_IO_IN;
1032 // Interpret the configuration file
1033 for (i = configReader.getObjectClassConfigList().begin();
1034 i != configReader.getObjectClassConfigList().end(); ++i) {
1036 /// already warned about this above
1037 if (i->_type != "Multiplayer")
1040 /// The object class for HLA aircraft
1041 SGSharedPtr<MultiplayerObjectClass> objectClass;
1043 // Register the object class that we need for this simple hla implementation
1044 std::string aircraftClassName = i->_name;
1045 objectClass = dynamic_cast<MultiplayerObjectClass*>(_hlaFederate->getObjectClass(aircraftClassName));
1046 if (!objectClass.valid()) {
1047 SG_LOG(SG_IO, SG_ALERT, "Could not find " << aircraftClassName << " object class!");
1051 SGSharedPtr<MultiplayerObjectClass> mpClassCallback = objectClass;
1053 if (i->_positionConfig._type == "cartesian") {
1054 SGSharedPtr<sg::HLACartesianLocationFactory> locationFactory;
1055 locationFactory = new sg::HLACartesianLocationFactory;
1056 XMLConfigReader::DataElementList::const_iterator j;
1057 for (j = i->_positionConfig._dataElementList.begin();
1058 j != i->_positionConfig._dataElementList.end(); ++j) {
1060 if (j->_type == "position-x")
1061 locationFactory->setPositionIndex(0, objectClass->getDataElementIndex(j->_name));
1062 else if (j->_type == "position-y")
1063 locationFactory->setPositionIndex(1, objectClass->getDataElementIndex(j->_name));
1064 else if (j->_type == "position-z")
1065 locationFactory->setPositionIndex(2, objectClass->getDataElementIndex(j->_name));
1067 else if (j->_type == "orientation-sin-angle-axis-x")
1068 locationFactory->setOrientationIndex(0, objectClass->getDataElementIndex(j->_name));
1069 else if (j->_type == "orientation-sin-angle-axis-y")
1070 locationFactory->setOrientationIndex(1, objectClass->getDataElementIndex(j->_name));
1071 else if (j->_type == "orientation-sin-angle-axis-z")
1072 locationFactory->setOrientationIndex(2, objectClass->getDataElementIndex(j->_name));
1074 else if (j->_type == "angular-velocity-x")
1075 locationFactory->setAngularVelocityIndex(0, objectClass->getDataElementIndex(j->_name));
1076 else if (j->_type == "angular-velocity-y")
1077 locationFactory->setAngularVelocityIndex(1, objectClass->getDataElementIndex(j->_name));
1078 else if (j->_type == "angular-velocity-z")
1079 locationFactory->setAngularVelocityIndex(2, objectClass->getDataElementIndex(j->_name));
1081 else if (j->_type == "linear-velocity-x")
1082 locationFactory->setLinearVelocityIndex(0, objectClass->getDataElementIndex(j->_name));
1083 else if (j->_type == "linear-velocity-y")
1084 locationFactory->setLinearVelocityIndex(1, objectClass->getDataElementIndex(j->_name));
1085 else if (j->_type == "linear-velocity-z")
1086 locationFactory->setLinearVelocityIndex(2, objectClass->getDataElementIndex(j->_name));
1089 SG_LOG(SG_IO, SG_ALERT, "HLA: Unknown position configuration type \""
1090 << j->_type << "\"for object class \"" << aircraftClassName << "\". Ignoring!");
1094 mpClassCallback->setLocationFactory(locationFactory.get());
1095 } else if (i->_positionConfig._type == "geodetic") {
1096 SGSharedPtr<sg::HLAGeodeticLocationFactory> locationFactory;
1097 locationFactory = new sg::HLAGeodeticLocationFactory;
1099 XMLConfigReader::DataElementList::const_iterator j;
1100 for (j = i->_positionConfig._dataElementList.begin();
1101 j != i->_positionConfig._dataElementList.end(); ++j) {
1103 if (j->_type == "latitude-deg")
1104 locationFactory->setIndex(sg::HLAGeodeticLocationFactory::LatitudeDeg,
1105 objectClass->getDataElementIndex(j->_name));
1106 else if (j->_type == "latitude-rad")
1107 locationFactory->setIndex(sg::HLAGeodeticLocationFactory::LatitudeRad,
1108 objectClass->getDataElementIndex(j->_name));
1109 else if (j->_type == "longitude-deg")
1110 locationFactory->setIndex(sg::HLAGeodeticLocationFactory::LongitudeDeg,
1111 objectClass->getDataElementIndex(j->_name));
1112 else if (j->_type == "longitude-rad")
1113 locationFactory->setIndex(sg::HLAGeodeticLocationFactory::LongitudeRad,
1114 objectClass->getDataElementIndex(j->_name));
1115 else if (j->_type == "elevation-m")
1116 locationFactory->setIndex(sg::HLAGeodeticLocationFactory::ElevationM,
1117 objectClass->getDataElementIndex(j->_name));
1118 else if (j->_type == "elevation-m")
1119 locationFactory->setIndex(sg::HLAGeodeticLocationFactory::ElevationFt,
1120 objectClass->getDataElementIndex(j->_name));
1121 else if (j->_type == "heading-deg")
1122 locationFactory->setIndex(sg::HLAGeodeticLocationFactory::HeadingDeg,
1123 objectClass->getDataElementIndex(j->_name));
1124 else if (j->_type == "heading-rad")
1125 locationFactory->setIndex(sg::HLAGeodeticLocationFactory::HeadingRad,
1126 objectClass->getDataElementIndex(j->_name));
1127 else if (j->_type == "pitch-deg")
1128 locationFactory->setIndex(sg::HLAGeodeticLocationFactory::PitchDeg,
1129 objectClass->getDataElementIndex(j->_name));
1130 else if (j->_type == "pitch-rad")
1131 locationFactory->setIndex(sg::HLAGeodeticLocationFactory::PitchRad,
1132 objectClass->getDataElementIndex(j->_name));
1133 else if (j->_type == "roll-deg")
1134 locationFactory->setIndex(sg::HLAGeodeticLocationFactory::RollDeg,
1135 objectClass->getDataElementIndex(j->_name));
1136 else if (j->_type == "roll-rad")
1137 locationFactory->setIndex(sg::HLAGeodeticLocationFactory::RollRad,
1138 objectClass->getDataElementIndex(j->_name));
1139 else if (j->_type == "ground-track-deg")
1140 locationFactory->setIndex(sg::HLAGeodeticLocationFactory::GroundTrackDeg,
1141 objectClass->getDataElementIndex(j->_name));
1142 else if (j->_type == "ground-track-rad")
1143 locationFactory->setIndex(sg::HLAGeodeticLocationFactory::GroundTrackRad,
1144 objectClass->getDataElementIndex(j->_name));
1145 else if (j->_type == "ground-speed-kt")
1146 locationFactory->setIndex(sg::HLAGeodeticLocationFactory::GroundSpeedKnots,
1147 objectClass->getDataElementIndex(j->_name));
1148 else if (j->_type == "ground-speed-ft-per-sec")
1149 locationFactory->setIndex(sg::HLAGeodeticLocationFactory::GroundSpeedFtPerSec,
1150 objectClass->getDataElementIndex(j->_name));
1151 else if (j->_type == "ground-speed-m-per-sec")
1152 locationFactory->setIndex(sg::HLAGeodeticLocationFactory::GroundSpeedMPerSec,
1153 objectClass->getDataElementIndex(j->_name));
1154 else if (j->_type == "vertical-speed-ft-per-sec")
1155 locationFactory->setIndex(sg::HLAGeodeticLocationFactory::VerticalSpeedFtPerSec,
1156 objectClass->getDataElementIndex(j->_name));
1157 else if (j->_type == "vertical-speed-ft-per-min")
1158 locationFactory->setIndex(sg::HLAGeodeticLocationFactory::VerticalSpeedFtPerMin,
1159 objectClass->getDataElementIndex(j->_name));
1160 else if (j->_type == "vertical-speed-m-per-sec")
1161 locationFactory->setIndex(sg::HLAGeodeticLocationFactory::VerticalSpeedMPerSec,
1162 objectClass->getDataElementIndex(j->_name));
1164 SG_LOG(SG_IO, SG_ALERT, "HLA: Unknown position configuration type \""
1165 << j->_type << "\" for object class \"" << aircraftClassName << "\". Ignoring!");
1169 mpClassCallback->setLocationFactory(locationFactory.get());
1172 if (i->_modelConfig._type == "native") {
1173 SGSharedPtr<AttributeModelFactory> attributeModelFactory;
1174 attributeModelFactory = new AttributeModelFactory;
1176 XMLConfigReader::DataElementList::const_iterator j;
1177 for (j = i->_modelConfig._dataElementList.begin();
1178 j != i->_modelConfig._dataElementList.end(); ++j) {
1180 if (j->_type == "model-path")
1181 attributeModelFactory->setModelIndex(objectClass->getDataElementIndex(j->_name));
1184 SG_LOG(SG_IO, SG_ALERT, "HLA: Unknown model configuration type \""
1185 << j->_type << "\" for object class \"" << aircraftClassName << "\". Ignoring!");
1189 mpClassCallback->setModelFactory(attributeModelFactory.get());
1190 } else if (i->_modelConfig._type == "map") {
1192 SGSharedPtr<AttributeMapModelFactory> attributeModelFactory;
1193 attributeModelFactory = new AttributeMapModelFactory;
1195 XMLConfigReader::DataElementList::const_iterator j;
1196 for (j = i->_modelConfig._dataElementList.begin();
1197 j != i->_modelConfig._dataElementList.end(); ++j) {
1199 if (j->_type == "external")
1200 attributeModelFactory->setModelIndex(objectClass->getDataElementIndex(j->_name));
1203 SG_LOG(SG_IO, SG_ALERT, "HLA: Unknown model configuration type \""
1204 << j->_type << "\" for object class \"" << aircraftClassName << "\". Ignoring!");
1208 std::list<std::pair<std::string, std::string> >::const_iterator k;
1209 for (k = i->_modelConfig._modelMap.begin();
1210 k != i->_modelConfig._modelMap.end(); ++k) {
1212 if (k->second.empty())
1213 attributeModelFactory->setExternalDefault(k->first);
1215 attributeModelFactory->addExternalModelPathPair(k->first, k->second);
1218 mpClassCallback->setModelFactory(attributeModelFactory.get());
1221 SG_LOG(SG_IO, SG_ALERT, "HLA: Unknown model configuration type \""
1222 << i->_modelConfig._type << "\" for object class \"" << aircraftClassName << "\". Ignoring!");
1225 if (i->_simTimeConfig._type == "attribute") {
1226 SGSharedPtr<AttributeSimTimeFactory> attributeSimTimeFactory;
1227 attributeSimTimeFactory = new AttributeSimTimeFactory;
1229 XMLConfigReader::DataElementList::const_iterator j;
1230 for (j = i->_simTimeConfig._dataElementList.begin();
1231 j != i->_simTimeConfig._dataElementList.end(); ++j) {
1233 if (j->_type == "local-simtime")
1234 attributeSimTimeFactory->setSimTimeIndex(objectClass->getDataElementIndex(j->_name));
1236 SG_LOG(SG_IO, SG_ALERT, "HLA: Unknown simtime configuration type \""
1237 << j->_type << "\" for object class \"" << aircraftClassName << "\". Ignoring!");
1241 mpClassCallback->setSimTimeFactory(attributeSimTimeFactory.get());
1242 } else if (i->_simTimeConfig._type == "attribute-sec-msec") {
1243 SGSharedPtr<MSecAttributeSimTimeFactory> attributeSimTimeFactory;
1244 attributeSimTimeFactory = new MSecAttributeSimTimeFactory;
1246 XMLConfigReader::DataElementList::const_iterator j;
1247 for (j = i->_simTimeConfig._dataElementList.begin();
1248 j != i->_simTimeConfig._dataElementList.end(); ++j) {
1250 if (j->_type == "local-simtime-sec")
1251 attributeSimTimeFactory->setSecIndex(objectClass->getDataElementIndex(j->_name));
1252 else if (j->_type == "local-simtime-msec")
1253 attributeSimTimeFactory->setMSecIndex(objectClass->getDataElementIndex(j->_name));
1255 SG_LOG(SG_IO, SG_ALERT, "HLA: Unknown simtime configuration type \""
1256 << j->_type << "\" for object class \"" << aircraftClassName << "\". Ignoring!");
1260 mpClassCallback->setSimTimeFactory(attributeSimTimeFactory.get());
1262 SG_LOG(SG_IO, SG_ALERT, "HLA: Unknown simtime configuration type \""
1263 << i->_simTimeConfig._type << "\" for object class \"" << aircraftClassName << "\". Ignoring!");
1266 if (!i->_mpPropertiesConfig._name.empty()) {
1267 mpClassCallback->setMPPropertiesIndex(objectClass->getDataElementIndex(i->_mpPropertiesConfig._name));
1270 // The free configurabel property - dataElement mapping
1271 XMLConfigReader::DataElementList::const_iterator j;
1272 for (j = i->_dataElementList.begin();
1273 j != i->_dataElementList.end(); ++j) {
1275 if (j->_type == "property") {
1276 if (!j->_inProperty.empty())
1277 mpClassCallback->setInputProperty(objectClass->getDataElementIndex(j->_name), j->_inProperty);
1278 if (!j->_outProperty.empty())
1279 mpClassCallback->setOutputProperty(objectClass->getDataElementIndex(j->_name), j->_outProperty);
1281 SG_LOG(SG_IO, SG_ALERT, "HLA: Unknown dataElement configuration type \""
1282 << j->_type << "\" for object class \"" << aircraftClassName << "\". Ignoring!");
1288 return is_enabled();
1297 if (!_hlaFederate.valid())
1300 // First push our own data so that others can recieve ...
1301 if (get_direction() & SG_IO_OUT)
1302 _hlaFederate->updateLocalAircraftInstance();
1304 // Then get news from others and process possible update requests
1305 _hlaFederate->update();
1316 if (!_hlaFederate.valid())
1319 // Leave the federation and try to destroy the federation execution.
1320 _hlaFederate->shutdown();