1 // This program is free software; you can redistribute it and/or
2 // modify it under the terms of the GNU General Public License as
3 // published by the Free Software Foundation; either version 2 of the
4 // License, or (at your option) any later version.
5 //
6 // This program is distributed in the hope that it will be useful, but
7 // WITHOUT ANY WARRANTY; without even the implied warranty of
8 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
9 // General Public License for more details.
10 //
11 // You should have received a copy of the GNU General Public License
12 // along with this program; if not, write to the Free Software
13 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
14 //
15 
16 #ifdef HAVE_CONFIG_H
17 #  include <config.h>
18 #endif
19 
20 #include <string>
21 #include <cstdlib>
22 #include <cstring> // for strcmp
23 
24 #include "dynamicloader.hxx"
25 
26 #include <Navaids/NavDataCache.hxx>
27 #include <Airports/airport.hxx>
28 #include <Airports/dynamics.hxx>
29 #include <Airports/groundnetwork.hxx>
30 #include <simgear/misc/strutils.hxx>
31 #include <simgear/debug/logstream.hxx>
32 #include <simgear/structure/exception.hxx>
33 
34 using std::string;
35 
36 /*****************************************************************************
37  * Helper function for parsing position string
38  ****************************************************************************/
processPosition(const string & pos)39 static double processPosition(const string &pos)
40 {
41   string prefix;
42   string subs;
43   string degree;
44   string decimal;
45   int sign = 1;
46   double value;
47   subs = pos;
48   prefix= subs.substr(0,1);
49   if (prefix == string("S") || (prefix == string("W")))
50     sign = -1;
51   subs    = subs.substr(1, subs.length());
52   degree  = subs.substr(0, subs.find(" ",0));
53   decimal = subs.substr(subs.find(" ",0), subs.length());
54 
55   value = sign * (atof(degree.c_str()) + atof(decimal.c_str())/60.0);
56   return value;
57 }
58 
FGGroundNetXMLLoader(FGGroundNetwork * net)59 FGGroundNetXMLLoader::FGGroundNetXMLLoader(FGGroundNetwork* net):
60     XMLVisitor(),
61     _groundNetwork(net)
62 {}
63 
startXML()64 void  FGGroundNetXMLLoader::startXML () {
65   //cout << "FGAirportDynamicsLoader::Start XML" << endl;
66 }
67 
endXML()68 void  FGGroundNetXMLLoader::endXML ()
69 {
70   ParkingPushbackIndex::const_iterator it;
71 
72   for (it = _parkingPushbacks.begin(); it != _parkingPushbacks.end(); ++it) {
73     NodeIndexMap::const_iterator j = _indexMap.find(it->second);
74     if (j == _indexMap.end()) {
75         _hasErrors = true;
76         SG_LOG(SG_NAVAID, SG_DEV_WARN, "bad groundnet, no node for index:" << it->first);
77         continue;
78     }
79 
80     it->first->setPushBackPoint(j->second);
81 
82   }
83 
84   if (!_unreferencedNodes.empty()) {
85       _hasErrors = true;
86   }
87 
88   for (const FGTaxiNodeRef& node: _unreferencedNodes) {
89     SG_LOG(SG_NAVAID, SG_DEV_WARN,
90            "unreferenced groundnet node: " << node->getIndex());
91   }
92 
93 }
94 
startParking(const XMLAttributes & atts)95 void FGGroundNetXMLLoader::startParking(const XMLAttributes &atts)
96 {
97   string type;
98   int index = 0;
99   string gateName, gateNumber;
100   string lat, lon;
101   double heading = 0.0;
102   double radius = 1.0;
103   string airlineCodes;
104   int pushBackRoute = -1;       // signals unseen attribute
105 
106   savePosition();               // allows to retrieve the line number
107 
108   for (int i = 0; i < atts.size(); i++)
109   {
110     string attname(atts.getName(i));
111 	  if (attname == "index") {
112       index = std::atoi(atts.getValue(i));
113     } else if (attname == "type")
114 	    type = atts.getValue(i);
115     else if (attname == "name")
116       gateName = atts.getValue(i);
117 	  else if (attname == "number")
118 	    gateNumber = atts.getValue(i);
119 	  else if (attname == "lat")
120       lat = atts.getValue(i);
121 	  else if (attname == "lon")
122 	    lon = atts.getValue(i);
123 	  else if (attname == "heading")
124 	    heading = std::atof(atts.getValue(i));
125 	  else if (attname == "radius") {
126 	    string radiusStr = atts.getValue(i);
127 	    if (radiusStr.find("M") != string::npos)
128 	      radiusStr = radiusStr.substr(0, radiusStr.find("M",0));
129 	    radius = std::atof(radiusStr.c_str());
130 	  }
131 	  else if (attname == "airlineCodes")
132       airlineCodes = atts.getValue(i);
133     else if (attname == "pushBackRoute") {
134       const string attrVal = atts.getValue(i);
135       try {
136           if (attrVal.empty() || (attrVal == "None")) {
137               pushBackRoute = -2;
138           } else {
139               pushBackRoute = simgear::strutils::readNonNegativeInt<int>(attrVal);
140           }
141       } catch (const sg_exception& e) {
142         SG_LOG(SG_NAVAID, SG_DEV_WARN,
143                getPath() << ":" << getLine() << ": " <<
144                "invalid value for 'pushBackRoute': " << e.what());
145         _hasErrors = true;
146         pushBackRoute = -2;
147       }
148     }
149   }
150 
151   SGGeod pos(SGGeod::fromDeg(processPosition(lon), processPosition(lat)));
152 
153   FGParkingRef parking(new FGParking(index,
154                                      pos, heading, radius,
155                                      gateName + gateNumber,
156                                      type, airlineCodes));
157   if (pushBackRoute >= 0) {
158     _parkingPushbacks[parking] = pushBackRoute;
159   }
160 
161   _indexMap[index] = parking;
162   _groundNetwork->addParking(parking);
163 }
164 
startNode(const XMLAttributes & atts)165 void FGGroundNetXMLLoader::startNode(const XMLAttributes &atts)
166 {
167   int index = 0;
168   string lat, lon;
169   bool onRunway = false;
170   int holdPointType = 0;
171 
172   for (int i = 0; i < atts.size() ; i++)
173 	{
174 	  string attname(atts.getName(i));
175 	  if (attname == "index")
176 	    index = std::atoi(atts.getValue(i));
177 	  else if (attname == "lat")
178       lat = atts.getValue(i);
179 	  else if (attname == "lon")
180 	    lon = atts.getValue(i);
181     else if (attname == "isOnRunway")
182       onRunway = std::atoi(atts.getValue(i)) != 0;
183 	  else if (attname == "holdPointType") {
184       string attval = atts.getValue(i);
185       if (attval=="none") {
186         holdPointType=0;
187       } else if (attval=="normal") {
188         holdPointType=1;
189       } else if (attval=="CAT II/III") {
190         holdPointType=3;
191       } else if (attval=="PushBack") {
192         holdPointType=3;
193       } else {
194         holdPointType=0;
195       }
196     }
197 	}
198 
199   if (_indexMap.find(index) != _indexMap.end()) {
200     SG_LOG(SG_NAVAID, SG_DEV_WARN, "duplicate ground-net index:" << index);
201     _hasErrors = true;
202   }
203 
204   SGGeod pos(SGGeod::fromDeg(processPosition(lon), processPosition(lat)));
205   FGTaxiNodeRef node(new FGTaxiNode(FGPositioned::TAXI_NODE, index, pos, onRunway, holdPointType));
206   _indexMap[index] = node;
207   _unreferencedNodes.insert(node);
208 }
209 
startArc(const XMLAttributes & atts)210 void FGGroundNetXMLLoader::startArc(const XMLAttributes &atts)
211 {
212   int begin = 0, end = 0;
213   bool isPushBackRoute = false;
214 
215   for (int i = 0; i < atts.size() ; i++)
216 	{
217 	  string attname = atts.getName(i);
218 	  if (attname == "begin")
219 	    begin = std::atoi(atts.getValue(i));
220 	  else if (attname == "end")
221 	    end = std::atoi(atts.getValue(i));
222     else if (attname == "isPushBackRoute")
223 	    isPushBackRoute = std::atoi(atts.getValue(i)) != 0;
224 	}
225 
226   IntPair e(begin, end);
227   if (_arcSet.find(e) != _arcSet.end()) {
228     SG_LOG(SG_NAVAID, SG_DEV_WARN, _groundNetwork->airport()->ident() << " ground-net: skipping duplicate edge:" << begin << "->" << end);
229     _hasErrors = true;
230     return;
231   }
232 
233   NodeIndexMap::const_iterator it;
234   FGTaxiNodeRef fromNode, toNode;
235   it = _indexMap.find(begin);
236   if (it == _indexMap.end()) {
237       SG_LOG(SG_NAVAID, SG_DEV_WARN, "ground-net: bad edge:" << begin << "->" << end << ", begin index unknown");
238       _hasErrors = true;
239       return;
240   } else {
241       _unreferencedNodes.erase(it->second);
242       fromNode = it->second;
243   }
244 
245   it = _indexMap.find(end);
246   if (it == _indexMap.end()) {
247       SG_LOG(SG_NAVAID, SG_DEV_WARN, "ground-net: bad edge:" << begin << "->" << end << ", end index unknown");
248       _hasErrors = true;
249       return;
250   } else {
251       _unreferencedNodes.erase(it->second);
252       toNode = it->second;
253   }
254 
255   _arcSet.insert(e);
256   _groundNetwork->addSegment(fromNode, toNode);
257   if (isPushBackRoute) {
258 //    toNode->setIsPushback();
259   }
260 }
261 
startElement(const char * name,const XMLAttributes & atts)262 void FGGroundNetXMLLoader::startElement (const char * name, const XMLAttributes &atts)
263 {
264   if (!strcmp("Parking", name)) {
265     startParking(atts);
266   } else if (!strcmp("node", name)) {
267     startNode(atts);
268   } else if (!strcmp("arc", name)) {
269     startArc(atts);
270   }
271 }
272 
endElement(const char * name)273 void  FGGroundNetXMLLoader::endElement (const char * name)
274 {
275   int valueAsInt = atoi(value.c_str());
276   if (!strcmp("version", name)) {
277     _groundNetwork->addVersion(valueAsInt);
278   } else if (!strcmp("AWOS", name)) {
279     _groundNetwork->addAwosFreq(valueAsInt);
280   } else if (!strcmp("UNICOM", name)) {
281     _groundNetwork->addUnicomFreq(valueAsInt);
282   } else if (!strcmp("CLEARANCE", name)) {
283     _groundNetwork->addClearanceFreq(valueAsInt);
284   } else if (!strcmp("GROUND", name)) {
285     _groundNetwork->addGroundFreq(valueAsInt);
286   } else if (!strcmp("TOWER", name)) {
287     _groundNetwork->addTowerFreq(valueAsInt);
288   } else if (!strcmp("APPROACH", name)) {
289     _groundNetwork->addApproachFreq(valueAsInt);
290   }
291 }
292 
data(const char * s,int len)293 void  FGGroundNetXMLLoader::data (const char * s, int len) {
294   string token = string(s,len);
295   //cout << "Character data " << string(s,len) << endl;
296   if ((token.find(" ") == string::npos && (token.find('\n')) == string::npos))
297     value += token;
298   else
299     value = string("");
300 }
301 
pi(const char * target,const char * data)302 void  FGGroundNetXMLLoader::pi (const char * target, const char * data) {
303   //cout << "Processing instruction " << target << ' ' << data << endl;
304 }
305 
warning(const char * message,int line,int column)306 void  FGGroundNetXMLLoader::warning (const char * message, int line, int column) {
307     SG_LOG(SG_IO, SG_DEV_WARN, "Warning: " << message << " (" << line << ',' << column << ')');
308 }
309 
error(const char * message,int line,int column)310 void  FGGroundNetXMLLoader::error (const char * message, int line, int column) {
311     SG_LOG(SG_IO, SG_DEV_ALERT, "Error: " << message << " (" << line << ',' << column << ')');
312 }
313