FlightGear next
autopilot.cxx
Go to the documentation of this file.
1// autopilot.cxx - an even more flexible, generic way to build autopilots
2//
3// Written by Torsten Dreyer
4// Based heavily on work created by Curtis Olson, started January 2004.
5//
6// Copyright (C) 2004 Curtis L. Olson - http://www.flightgear.org/~curt
7// Copyright (C) 2010 Torsten Dreyer - Torsten (at) t3r (dot) de
8//
9// This program is free software; you can redistribute it and/or
10// modify it under the terms of the GNU General Public License as
11// published by the Free Software Foundation; either version 2 of the
12// License, or (at your option) any later version.
13//
14// This program is distributed in the hope that it will be useful, but
15// WITHOUT ANY WARRANTY; without even the implied warranty of
16// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17// General Public License for more details.
18//
19// You should have received a copy of the GNU General Public License
20// along with this program; if not, write to the Free Software
21// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22//
23
24#ifdef HAVE_CONFIG_H
25# include <config.h>
26#endif
27
28#include "autopilot.hxx"
29
30#include <simgear/structure/StateMachine.hxx>
31#include <simgear/sg_inlines.h>
32
33#include "component.hxx"
34#include "functor.hxx"
35#include "predictor.hxx"
36#include "digitalfilter.hxx"
38#include "pidcontroller.hxx"
39#include "logic.hxx"
40#include "flipflop.hxx"
41
42#include "Main/fg_props.hxx"
43
44using std::map;
45using std::string;
46
47using namespace FGXMLAutopilot;
48
50{
51public:
52 StateMachineComponent( SGPropertyNode& props_root,
53 SGPropertyNode& cfg ) {
54 inner = simgear::StateMachine::createFromPlist(&cfg, &props_root);
55 }
56
57 // Subsystem identification.
58 static const char* staticSubsystemClassId() { return "state-machine"; }
59
60 virtual bool configure( const std::string & nodeName, SGPropertyNode_ptr config) {
61 return false;
62 }
63
64 virtual void update( bool firstTime, double dt ) {
65 SG_UNUSED(firstTime);
66 inner->update(dt);
67 }
68
69private:
70 simgear::StateMachine_ptr inner;
71};
72
73
74// Register the subsystem.
75#if 0
76SGSubsystemMgr::Registrant<StateMachineComponent> registrantStateMachineComponent;
77#endif
78
79
80class StateMachineFunctor : public FunctorBase<Component>
81{
82public:
84 virtual Component* operator()( SGPropertyNode& cfg,
85 SGPropertyNode& prop_root )
86 {
87 return new StateMachineComponent(cfg, prop_root);
88 }
89};
90
91
92class ComponentForge : public map<string,FunctorBase<Component> *> {
93public:
94 virtual ~ ComponentForge();
95};
96
98{
99 for( iterator it = begin(); it != end(); ++it )
100 delete it->second;
101}
102
103void readInterfaceProperties( SGPropertyNode_ptr prop_root,
104 SGPropertyNode_ptr cfg )
105{
106 simgear::PropertyList cfg_props = cfg->getChildren("property");
107 for( simgear::PropertyList::iterator it = cfg_props.begin();
108 it != cfg_props.end();
109 ++it )
110 {
111 SGPropertyNode_ptr prop = prop_root->getNode((*it)->getStringValue(), true);
112 SGPropertyNode* val = (*it)->getNode("_attr_/value");
113
114 if( val )
115 {
116 prop->setDoubleValue( val->getDoubleValue() );
117
118 // TODO: should we keep the _attr_ node, as soon as the property browser is
119 // able to cope with it?
120 (*it)->removeChild("_attr_", 0);
121 }
122 }
123}
124
126
127Autopilot::Autopilot( SGPropertyNode_ptr rootNode, SGPropertyNode_ptr configNode ) :
128 _name("unnamed autopilot"),
129 _serviceable(true),
130 _rootNode(rootNode)
131{
132 if (componentForge.empty())
133 {
134 componentForge["pid-controller"] = new CreateAndConfigureFunctor<PIDController,Component>();
135 componentForge["pi-simple-controller"] = new CreateAndConfigureFunctor<PISimpleController,Component>();
136 componentForge["predict-simple"] = new CreateAndConfigureFunctor<Predictor,Component>();
137 componentForge["filter"] = new CreateAndConfigureFunctor<DigitalFilter,Component>();
138 componentForge["logic"] = new CreateAndConfigureFunctor<Logic,Component>();
139 componentForge["flipflop"] = new CreateAndConfigureFunctor<FlipFlop,Component>();
140 componentForge["state-machine"] = new StateMachineFunctor();
141 }
142
143 if( !configNode )
144 configNode = rootNode;
145
146 // property-root can be set in config file and overridden in the local system
147 // node. This allows using the same autopilot multiple times but with
148 // different paths (with all relative property paths being relative to the
149 // node specified with property-root)
150 SGPropertyNode_ptr prop_root_node = rootNode->getChild("property-root");
151 if( !prop_root_node )
152 prop_root_node = configNode->getChild("property-root");
153
154 SGPropertyNode_ptr prop_root =
155 fgGetNode(prop_root_node ? prop_root_node->getStringValue() : "/", true);
156
157 // Just like the JSBSim interface properties for systems, create properties
158 // given in the autopilot file and set to given (default) values.
159 readInterfaceProperties(prop_root, configNode);
160
161 // Afterwards read the properties specified in local system node to allow
162 // overriding initial or default values. This allows reusing components with
163 // just different "parameter" values.
164 readInterfaceProperties(prop_root, rootNode);
165
166 int count = configNode->nChildren();
167 for( int i = 0; i < count; ++i )
168 {
169 SGPropertyNode_ptr node = configNode->getChild(i);
170 string childName = node->getNameString();
171 if( childName == "property"
172 || childName == "property-root" )
173 continue;
174 if( componentForge.count(childName) == 0 )
175 {
176 SG_LOG(SG_AUTOPILOT, SG_BULK, "unhandled element <" << childName << ">");
177 continue;
178 }
179
180 Component * component = (*componentForge[childName])(*prop_root, *node);
181 if( component->subsystemId().length() == 0 ) {
182 std::ostringstream buf;
183 buf << "unnamed_component_" << i;
184 }
185
186 double updateInterval = node->getDoubleValue( "update-interval-secs", 0.0 );
187
188 SG_LOG( SG_AUTOPILOT, SG_DEBUG, "adding autopilot component \"" << childName << "\" as \"" << component->subsystemId() << "\" with interval=" << updateInterval );
189 add_component(component,updateInterval);
190 }
191}
192
196
198{
199 fgTie( _rootNode->getNode("serviceable", true)->getPath().c_str(), this,
201 SGSubsystemGroup::bind();
202}
203
205{
206 _rootNode->untie( "serviceable" );
207 SGSubsystemGroup::unbind();
208}
209
210void Autopilot::add_component( Component * component, double updateInterval )
211{
212 if( component == NULL ) return;
213
214 // check for duplicate name
215 const auto originalName = string{component->subsystemId()};
216 std::string name = originalName;
217 if (name.empty()) {
218 name = "unnamed_autopilot";
219 }
220
221 for( unsigned int i = 0; get_subsystem( name) != nullptr; i++ ) {
222 std::ostringstream buf;
223 buf << component->subsystemId() << "_" << i;
224 name = buf.str();
225 }
226
227 if (!originalName.empty() && (name != originalName)) {
228 SG_LOG( SG_AUTOPILOT, SG_DEV_WARN, "Duplicate autopilot component " << originalName << ", renamed to " << name );
229 }
230
231 set_subsystem( name, component, updateInterval );
232}
233
234void Autopilot::update( double dt )
235{
236 if( !_serviceable || dt <= SGLimitsd::min() )
237 return;
238 SGSubsystemGroup::update( dt );
239}
#define i(x)
void readInterfaceProperties(SGPropertyNode_ptr prop_root, SGPropertyNode_ptr cfg)
static ComponentForge componentForge
Autopilot(SGPropertyNode_ptr rootNode, SGPropertyNode_ptr configNode=NULL)
virtual ~ComponentForge()
Definition autopilot.cxx:97
void set_serviceable(bool value)
Definition autopilot.hxx:51
void update(double dt) override
bool is_serviceable() const
Definition autopilot.hxx:52
void add_component(Component *component, double updateInterval)
Base class for other autopilot components.
Definition component.hxx:34
Component()
A constructor for an empty Component.
Definition component.cxx:30
virtual void update(bool firstTime, double dt)
pure virtual function to be implemented by the derived classes.
Definition autopilot.cxx:64
virtual bool configure(const std::string &nodeName, SGPropertyNode_ptr config)
Definition autopilot.cxx:60
StateMachineComponent(SGPropertyNode &props_root, SGPropertyNode &cfg)
Definition autopilot.cxx:52
static const char * staticSubsystemClassId()
Definition autopilot.cxx:58
virtual ~StateMachineFunctor()
Definition autopilot.cxx:83
virtual Component * operator()(SGPropertyNode &cfg, SGPropertyNode &prop_root)
Definition autopilot.cxx:84
const char * name
void fgTie(const char *name, V(*getter)(), void(*setter)(V)=0, bool useDefault=true)
Tie a property to a pair of simple functions.
Definition fg_props.hxx:751
SGPropertyNode * fgGetNode(const char *path, bool create)
Get a property node.
Definition proptest.cpp:27