1 // autopilotgroup.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 #include "autopilotgroup.hxx"
30
31 #include <string>
32 #include <vector>
33
34 #include <simgear/debug/ErrorReportingCallback.hxx>
35 #include <simgear/props/props_io.hxx>
36 #include <simgear/structure/exception.hxx>
37 #include <simgear/structure/subsystem_mgr.hxx>
38
39 #include <Main/fg_props.hxx>
40 #include <Main/sentryIntegration.hxx>
41
42 using std::vector;
43 using simgear::PropertyList;
44 using FGXMLAutopilot::Autopilot;
45
46 class FGXMLAutopilotGroupImplementation : public FGXMLAutopilotGroup
47 {
48 public:
FGXMLAutopilotGroupImplementation(const std::string & nodeName)49 FGXMLAutopilotGroupImplementation(const std::string& nodeName):
50 FGXMLAutopilotGroup(),
51 _nodeName(nodeName)
52 {}
53
54 // Subsystem API.
55 void init() override;
56 InitStatus incrementalInit() override;
57 void reinit() override;
58
59 // Subsystem identification.
staticSubsystemClassId()60 static const char* staticSubsystemClassId() { return "xml-autopilot-group"; }
61
62 virtual void addAutopilot( const std::string& name,
63 SGPropertyNode_ptr apNode,
64 SGPropertyNode_ptr config );
65 virtual void removeAutopilot( const std::string & name );
66
67 private:
68 void initFrom( SGPropertyNode_ptr rootNode, const char * childName );
69 std::string _nodeName;
70 };
71
72 //------------------------------------------------------------------------------
addAutopilot(const std::string & name,SGPropertyNode_ptr apNode,SGPropertyNode_ptr config)73 void FGXMLAutopilotGroupImplementation::addAutopilot( const std::string& name,
74 SGPropertyNode_ptr apNode,
75 SGPropertyNode_ptr config )
76 {
77 if( has_subsystem(name) )
78 {
79 SG_LOG( SG_AUTOPILOT,
80 SG_ALERT,
81 "NOT adding duplicate " << _nodeName << " name '" << name << "'");
82 return;
83 }
84
85 Autopilot* ap = new Autopilot(apNode, config);
86 ap->set_name( name );
87
88 double updateInterval = config->getDoubleValue("update-interval-secs", 0.0);
89 set_subsystem( name, ap, updateInterval );
90 }
91
92 //------------------------------------------------------------------------------
removeAutopilot(const std::string & name)93 void FGXMLAutopilotGroupImplementation::removeAutopilot(const std::string& name)
94 {
95 Autopilot* ap = static_cast<Autopilot*>(get_subsystem(name));
96 if( !ap )
97 {
98 SG_LOG( SG_AUTOPILOT,
99 SG_ALERT,
100 "CAN NOT remove unknown " << _nodeName << " '" << name << "'");
101 return;
102 }
103
104 remove_subsystem(name);
105 }
106
107 //------------------------------------------------------------------------------
reinit()108 void FGXMLAutopilotGroupImplementation::reinit()
109 {
110 SGSubsystemGroup::unbind();
111 clearSubsystems();
112
113 // ensure we bind again, so the SGSubsystemGroup state is correct before
114 // we call init. Since there's no actual group members at this point (we
115 // cleared them just above) this is purely to ensure SGSubsystemGroup::_state
116 // is BIND, so that ::init doesn't assert
117 SGSubsystemGroup::bind();
118 init();
119 }
120
121 //------------------------------------------------------------------------------
incrementalInit()122 SGSubsystem::InitStatus FGXMLAutopilotGroupImplementation::incrementalInit()
123 {
124 init();
125 return INIT_DONE;
126 }
127
128 //------------------------------------------------------------------------------
init()129 void FGXMLAutopilotGroupImplementation::init()
130 {
131 initFrom(fgGetNode("/sim/systems"), _nodeName.c_str());
132 SGSubsystemGroup::init();
133 }
134
135 //------------------------------------------------------------------------------
initFrom(SGPropertyNode_ptr rootNode,const char * childName)136 void FGXMLAutopilotGroupImplementation::initFrom( SGPropertyNode_ptr rootNode,
137 const char* childName )
138 {
139 if( !rootNode )
140 return;
141
142 for( auto autopilotNode : rootNode->getChildren(childName) )
143 {
144 SGPropertyNode_ptr pathNode = autopilotNode->getNode("path");
145 if( !pathNode )
146 {
147 SG_LOG
148 (
149 SG_AUTOPILOT,
150 SG_WARN,
151 "No configuration file specified for this " << childName << "!"
152 );
153 continue;
154 }
155
156 std::string apName;
157 SGPropertyNode_ptr nameNode = autopilotNode->getNode( "name" );
158 if( nameNode != NULL ) {
159 apName = nameNode->getStringValue();
160 } else {
161 std::ostringstream buf;
162 buf << "unnamed_autopilot_" << autopilotNode->getIndex();
163 apName = buf.str();
164 }
165
166 {
167 // check for duplicate names
168 std::string name = apName;
169 for( unsigned i = 0; get_subsystem( apName.c_str() ) != NULL; i++ ) {
170 std::ostringstream buf;
171 buf << name << "_" << i;
172 apName = buf.str();
173 }
174 if( apName != name )
175 SG_LOG
176 (
177 SG_AUTOPILOT,
178 SG_DEV_WARN,
179 "Duplicate " << childName << " configuration name " << name
180 << ", renamed to " << apName
181 );
182 }
183
184 addAutopilotFromFile(apName, autopilotNode, pathNode->getStringValue());
185 }
186 }
187
addAutopilotFromFile(const std::string & name,SGPropertyNode_ptr apNode,const char * path)188 void FGXMLAutopilotGroup::addAutopilotFromFile( const std::string& name,
189 SGPropertyNode_ptr apNode,
190 const char* path )
191 {
192 SGPath config = globals->resolve_maybe_aircraft_path(path);
193 if( config.isNull() )
194 {
195 simgear::reportFailure(simgear::LoadFailure::NotFound, simgear::ErrorCode::AircraftSystems,
196 string{"Autopilot XML not found:"} + path, sg_location{path});
197 SG_LOG(
198 SG_AUTOPILOT,
199 SG_ALERT,
200 "Cannot find property-rule configuration file '" << path << "'.");
201 return;
202 }
203 SG_LOG
204 (
205 SG_AUTOPILOT,
206 SG_INFO,
207 "Reading property-rule configuration from " << config
208 );
209
210 try
211 {
212 SGPropertyNode_ptr configNode = new SGPropertyNode();
213 readProperties(config, configNode);
214
215 SG_LOG(SG_AUTOPILOT, SG_INFO, "adding property-rule subsystem " << name);
216 addAutopilot(name, apNode, configNode);
217 }
218 catch (const sg_exception& e)
219 {
220 SG_LOG
221 (
222 SG_AUTOPILOT,
223 SG_ALERT,
224 "Failed to load property-rule configuration: " << config
225 << ": " << e.getMessage()
226 );
227 simgear::reportFailure(simgear::LoadFailure::BadData, simgear::ErrorCode::AircraftSystems,
228 string{"Autopilot XML faield to load:"} + e.getFormattedMessage(), e.getLocation());
229 return;
230 }
231 }
232
233 //------------------------------------------------------------------------------
234 FGXMLAutopilotGroup*
createInstance(const std::string & nodeName)235 FGXMLAutopilotGroup::createInstance(const std::string& nodeName)
236 {
237 return new FGXMLAutopilotGroupImplementation(nodeName);
238 }
239