FlightGear next
atc_mgr.cxx
Go to the documentation of this file.
1/******************************************************************************
2 * atc_mgr.cxx
3 * Written by Durk Talsma, started August 1, 2010.
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of the
8 * License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 *
19 *
20 **************************************************************************/
21
22
23#ifdef HAVE_CONFIG_H
24# include "config.h"
25#endif
26
27#include <Airports/dynamics.hxx>
29#include <Airports/airport.hxx>
30#include <Scenery/scenery.hxx>
31#include <Main/globals.hxx>
32#include <Main/fg_props.hxx>
34#include <AIModel/AIManager.hxx>
35#include <Traffic/Schedule.hxx>
38
39#include "atc_mgr.hxx"
40
41
42using std::string;
43
48 controller(NULL),
49 prevController(NULL),
50 enRouteController(NULL),
51 networkVisible(false),
52 initSucceeded(false)
53{
54}
55
61
68{
69 int leg = 0;
70
71 trans_num = globals->get_props()->getNode("/sim/atc/transmission-num", true);
72
73 // Assign a controller to the user's aircraft.
74 // Three scenarios are considered:
75 // - Starting on ground at a parking position
76 // - Starting on ground at the runway.
77 // - Starting in the air
78 bool onGround = fgGetBool("/sim/presets/onground");
79 string runway = fgGetString("/sim/atc/runway");
80 string curAirport = fgGetString("/sim/presets/airport-id");
81 string parking = fgGetString("/sim/presets/parkpos");
82
83 _routeManagerDestinationAirportNode = globals->get_props()->getNode("/autopilot/route-manager/destination/airport", true);
84 destination = _routeManagerDestinationAirportNode->getStringValue();
85
86 auto aiManager = globals->get_subsystem<FGAIManager>();
87 auto userAircraft = aiManager->getUserAircraft();
88 string callsign = userAircraft->getCallSign();
89
90 enRouteController = new EnRouteController();
91
92 double aircraftRadius = 40; // note that this is currently hardcoded to a one-size-fits all JumboJet value. Should change later.
93
94 // In case a destination is not set yet, make it equal to the current airport
95 if (destination.empty()) {
96 destination = curAirport;
97 }
98
99 // NEXT UP: Create a traffic schedule and fill that with appropriate information. This we can use for flight planning.
100 // Note that these are currently only defaults.
101 userAircraftTrafficRef.reset(new FGAISchedule);
102 userAircraftTrafficRef->setFlightType("gate");
103
104 userAircraftScheduledFlight.reset(new FGScheduledFlight);
105 userAircraftScheduledFlight->setDepartureAirport(curAirport);
106 userAircraftScheduledFlight->setArrivalAirport(destination);
107 userAircraftScheduledFlight->initializeAirports();
108 userAircraftScheduledFlight->setFlightRules("IFR");
109 userAircraftScheduledFlight->setCallSign(callsign);
110
111 userAircraftTrafficRef->assign(userAircraftScheduledFlight.get());
112 std::unique_ptr<FGAIFlightPlan> fp ;
113 userAircraft->setTrafficRef(userAircraftTrafficRef.get());
114
115 // string flightPlanName = curAirport + "-" + _routeManagerDestinationAirportNode->getStringValue() + ".xml";
116 // double cruiseAlt = 100; // Doesn't really matter right now.
117 // double courseToDest = 180; // Just use something neutral; this value might affect the runway that is used though...
118 // time_t deptime = 0; // just make sure how flightplan processing is affected by this...
119
121 if (dcs && onGround) {// && !runway.empty()) {
122
124
125 if (parking == "AVAILABLE") {
126 double radius = fgGetDouble("/sim/dimensions/radius-m");
127 if (radius > 0) {
128 pk = dcs->getAvailableParking(radius, string(), string(), string());
129 if (pk.isValid()) {
130 fgGetString("/sim/presets/parkpos");
131 fgSetString("/sim/presets/parkpos", pk.parking()->getName());
132 }
133 }
134
135 if (!pk.isValid()) {
136 FGParkingList pkl(dcs->getParkings(true, "gate"));
137 if (!pkl.empty()) {
138 std::sort(pkl.begin(), pkl.end(), [](const FGParkingRef& a, const FGParkingRef& b) {
139 return a->getRadius() > b->getRadius();
140 });
141 pk = ParkingAssignment(pkl.front(), dcs);
142 fgSetString("/sim/presets/parkpos", pkl.front()->getName());
143 }
144 }
145 } else if (!parking.empty()) {
146 pk = dcs->getAvailableParkingByName(parking);
147 }
148
149 if (pk.isValid()) {
150 dcs->setParkingAvailable(pk.parking(), false);
151 fp.reset(new FGAIFlightPlan);
152 controller = dcs->getStartupController();
153 int stationFreq = dcs->getGroundFrequency(1);
154 if (stationFreq > 0)
155 {
156 SG_LOG(SG_ATC, SG_DEBUG, "Setting radio frequency to : " << stationFreq);
157 fgSetDouble("/instrumentation/comm[0]/frequencies/selected-mhz", ((double) stationFreq / 100.0));
158 }
160 //double, lat, lon, head; // Unused variables;
161 //int getId = apt->getDynamics()->getParking(gateId, &lat, &lon, &head);
162 aircraftRadius = pk.parking()->getRadius();
163 string fltType = pk.parking()->getType(); // gate / ramp, ga, etc etc.
164 string aircraftType; // Unused.
165 string airline; // Currently used for gate selection, but a fallback mechanism will apply when not specified.
166 fp->setGate(pk);
167 if (!(fp->createPushBack(userAircraft,
168 false,
169 dcs->parent(),
170 aircraftRadius,
171 fltType,
172 aircraftType,
173 airline))) {
174 controller = 0;
175 return;
176 }
177
178
179
180 } else if (!runway.empty()) {
181 // on a runway
182
183 controller = dcs->getTowerController();
184 int stationFreq = dcs->getTowerFrequency(2);
185 if (stationFreq > 0)
186 {
187 SG_LOG(SG_ATC, SG_DEBUG, "Setting radio frequency to inair frequency : " << stationFreq);
188 fgSetDouble("/instrumentation/comm[0]/frequencies/selected-mhz", ((double) stationFreq / 100.0));
189 }
190 fp.reset(new FGAIFlightPlan);
191 leg = AILeg::TAKEOFF;
192 string fltType = "ga";
193 fp->setRunway(runway);
194 fp->createTakeOff(userAircraft, false, dcs->parent(), userAircraft->getGeodPos(), 0, fltType);
195 } else {
196 // We're on the ground somewhere. Handle this case later.
197
198 // important : we are on the ground, so reset the AIFlightPlan back to
199 // a dummy one. Otherwise, in the reposition case, we end up with a
200 // stale flight-plan which confuses other code (eg, PositionInit::finalizeForParking)
201 // see unit test: PosInitTests::testRepositionAtOccupied
203 userAircraft->FGAIBase::setFlightPlan(std::move(fp));
204 controller = nullptr;
205
206 initSucceeded = true; // should be false?
207 return;
208 }
209
210 if (fp && !fp->empty()) {
211 fp->getLastWaypoint()->setName( fp->getLastWaypoint()->getName() + string("legend"));
212 }
213 } else {
214 controller = nullptr;
215 }
216
217 // Create an initial flightplan and assign it to the ai_ac. We won't use this flightplan, but it is necessary to
218 // keep the ATC code happy.
219 // note in the reposition case, 'fp' is only the new FlightPlan; if we didn't create one here.
220 // we will continue using the existing flight plan (and not restart it, for example)
221 if (fp) {
222 fp->restart();
223 fp->setLeg(leg);
224 userAircraft->FGAIBase::setFlightPlan(std::move(fp));
225 }
226
227 if (controller) {
228 FGAIFlightPlan* plan = userAircraft->GetFlightPlan();
229 const int routeIndex = (plan && plan->getCurrentWaypoint()) ? plan->getCurrentWaypoint()->getRouteIndex() : 0;
230 controller->announcePosition(userAircraft->getID(), plan,
231 routeIndex,
232 userAircraft->_getLatitude(),
233 userAircraft->_getLongitude(),
234 userAircraft->_getHeading(),
235 userAircraft->getSpeed(),
236 userAircraft->getAltitude(),
237 aircraftRadius, leg, userAircraft);
238 }
239 initSucceeded = true;
240}
241
248{
249 activeStations.clear();
250 userAircraftTrafficRef.reset();
251 userAircraftScheduledFlight.reset();
252 _routeManagerDestinationAirportNode.clear();
253 delete enRouteController;
254}
255
257{
258 prevController = controller = nullptr;
259
260// remove any parking assignment form the user flight-plan, so it's
261// available again. postinit() will recompute a new value if required
262 auto aiManager = globals->get_subsystem<FGAIManager>();
263 auto userAircraft = aiManager->getUserAircraft();
264 if (userAircraft) {
265 if (userAircraft->GetFlightPlan()) {
266 auto userAIFP = userAircraft->GetFlightPlan();
267 userAIFP->setGate({}); // clear any assignment
268 }
269
270 userAircraft->clearATCController();
271 }
272
273 postinit(); // critical for position-init logic
274}
275
281 activeStations.push_back(controller);
282}
283
288{
290 it = std::find(activeStations.begin(), activeStations.end(), controller);
291 if (it != activeStations.end()) {
292 activeStations.erase(it);
293 }
294}
295
304void FGATCManager::update ( double time ) {
305 // SG_LOG(SG_ATC, SG_BULK, "ATC update code is running at time: " << time);
306
307 // Test code: let my virtual co-pilot handle ATC
308 auto aiManager = globals->get_subsystem<FGAIManager>();
309 FGAIAircraft* user_ai_ac = aiManager->getUserAircraft();
310 FGAIFlightPlan *fp = user_ai_ac->GetFlightPlan();
311
312 // Update destination
313 string result = _routeManagerDestinationAirportNode->getStringValue();
314
315 if (destination != result && result != "") {
316 destination = result;
317 userAircraftScheduledFlight->setArrivalAirport(destination);
318 userAircraftScheduledFlight->initializeAirports();
319 userAircraftTrafficRef->clearAllFlights();
320 userAircraftTrafficRef->assign(userAircraftScheduledFlight.get());
321
322 auto userAircraft = aiManager->getUserAircraft();
323 userAircraft->setTrafficRef(userAircraftTrafficRef.get());
324 }
325
326 /* test code : find out how the routing develops */
327 if (fp) {
328 int size = fp->getNrOfWayPoints();
329 //SG_LOG(SG_ATC, SG_DEBUG, "Setting pos" << pos << " ");
330 //SG_LOG(SG_ATC, SG_DEBUG, "Setting intentions");
331 // This indicates that we have run out of waypoints: Im future versions, the
332 // user should be able to select a new route, but for now just shut down the
333 // system.
334 if (size < 3) {
335 return;
336 }
337#if 0
338 // Test code: Print how far we're progressing along the taxi route.
339 SG_LOG(SG_ATC, SG_DEBUG, "Size of waypoint queue " << size);
340 for (int i = 0; i < size; i++) {
341 int val = fp->getRouteIndex(i);
342 SG_LOG(SG_ATC, SG_BULK, fp->getWayPoint(i)->getName() << " ");
343 //if ((val) && (val != pos)) {
344 // intentions.push_back(val);
345 SG_LOG(SG_ATC, SG_BULK, "[done ]");
346 //}
347 }
348 SG_LOG(SG_ATC, SG_BULK, "[done ]");
349#endif
350 }
351 if (fp) {
352 SG_LOG(SG_ATC, SG_BULK, "User aircraft currently at leg : " << fp->getLeg());
353 }
354
355 //FIXME Should be extracted to a UserAircraft class
356 // Call getATCController method; returns what FGATCController presently controls the user aircraft
357 // - e.g. FGStartupController
358 // controller = user_ai_ac->getATCController();
359 // FIXME the AIAircraft currently doesn't set this for the user aircraft
360
361 // Update the ATC dialog
362 //FGATCDialogNew::instance()->update(time);
363
364 // Controller manager - if controller is set, then will update controller
365 if (controller) {
366// SG_LOG(SG_ATC, SG_DEBUG, "name of previous waypoint : " << fp->getPreviousWaypoint()->getName());
367 SG_LOG(SG_ATC, SG_BULK, "Currently under control of " << controller->getName());
368
369 // update aircraft information (simulates transponder)
370
371 controller->updateAircraftInformation(user_ai_ac->getID(),
372 user_ai_ac->getGeodPos(),
373 user_ai_ac->_getHeading(),
374 user_ai_ac->getSpeed(),
375 user_ai_ac->getAltitude(), time);
376
377 if (fp) {
378 switch (fp->getLeg()) {
379 case AILeg::STARTUP_PUSHBACK: // Startup and Push back
380 if (userAircraftTrafficRef->getDepartureAirport()->getDynamics())
381 controller = userAircraftTrafficRef->getDepartureAirport()->getDynamics()->getStartupController();
382 break;
383 case AILeg::RUNWAY_TAXI: // Taxiing to runway
384 if (userAircraftTrafficRef->getDepartureAirport()->getDynamics()->getGroundController()->exists())
385 controller = userAircraftTrafficRef->getDepartureAirport()->getDynamics()->getGroundController();
386 break;
387 case AILeg::TAKEOFF: //Take off tower controller
388 if (userAircraftTrafficRef->getDepartureAirport()->getDynamics()) {
389 controller = userAircraftTrafficRef->getDepartureAirport()->getDynamics()->getTowerController();
390 } else {
391 SG_LOG(SG_AI, SG_BULK, "Error: Could not find Dynamics at airport : " << userAircraftTrafficRef->getDepartureAirport()->getId());
392 }
393 break;
394 /* TODO: link up with state system?
395 case AILeg::APPROACH:
396 if (userAircraftTrafficRef->getArrivalAirport()->getDynamics()) {
397 controller = trafficRef->getArrivalAirport()->getDynamics()->getApproachController();
398 }
399 break;
400 case AILeg::PARKING_TAXI: // Taxiing for parking
401 if (trafficRef->getArrivalAirport()->getDynamics()->getGroundController()->exists())
402 controller = trafficRef->getArrivalAirport()->getDynamics()->getGroundController();
403 break;
404 */
405 default:
406 if(prevController) {
407 SG_LOG(SG_AI, SG_BULK, "Will be signing off user ai " << user_ai_ac->getID() << " from " << prevController->getName());
408 }
409 controller = nullptr;
410 break;
411 }
412
413 if ((controller != prevController) && prevController && !user_ai_ac->getDie()) {
414 //If we are dead we are automatically erased
415 prevController->signOff(user_ai_ac->getID());
416 }
417 prevController = controller;
418 }
419
420 //string airport = fgGetString("/sim/presets/airport-id");
421 //FGAirport *apt = FGAirport::findByIdent(airport);
422 // AT this stage we should update the flightplan, so that waypoint incrementing is conducted as well as leg loading.
423
424 // Ground network visibility:
425 // a) check to see if the message to toggle visibility was called
426 // b) if so, toggle network visibility and reset the transmission
427 // c) thereafter disable rendering for the old controller (TODO: should this be earlier?)
428 // d) and render if enabled for the new controller
429 int n = trans_num->getIntValue();
430
431 if (n == 1) {
432 SG_LOG(SG_ATC, SG_DEBUG, "Toggling ground network visibility " << networkVisible);
433 networkVisible = !networkVisible;
434 trans_num->setIntValue(-1);
435 }
436
437 // stop rendering the old controller's groundnetwork
438 if ((controller != prevController) && (prevController)) {
439 prevController->render(false);
440 }
441
442 if (controller) {
443 // render the path for the present controller if the ground network is set to visible
444 controller->render(networkVisible);
445 if (networkVisible) {
446 SG_LOG(SG_ATC, SG_BULK, "Adding ground network to the scenegraph::update");
447 }
448 }
449
450 // reset previous controller for next update() iteration
451 prevController = controller;
452 }
453
454 // update the active ATC stations
455 for (AtcVecIterator atc = activeStations.begin(); atc != activeStations.end(); ++atc) {
456 (*atc)->update(time);
457 }
458}
459
461 return enRouteController;
462}
463
464
465// Register the subsystem.
466SGSubsystemMgr::Registrant<FGATCManager> registrantFGATCManager(
467 SGSubsystemMgr::POST_FDM,
468 {{"FGAIManager", SGSubsystemMgr::Dependency::HARD}});
#define i(x)
SGSharedPtr< FGAirportDynamics > FGAirportDynamicsRef
std::vector< FGParkingRef > FGParkingList
SGSharedPtr< FGParking > FGParkingRef
SGSubsystemMgr::Registrant< FGATCManager > registrantFGATCManager(SGSubsystemMgr::POST_FDM, {{"FGAIManager", SGSubsystemMgr::Dependency::HARD}})
std::vector< FGATCController * >::iterator AtcVecIterator
Definition atc_mgr.hxx:28
FGAIWaypoint * getWayPoint(int i)
int getRouteIndex(int i) const
static FGAIFlightPlan * createDummyUserPlan()
create a nearly empty FlightPlan for the user aircraft, based on the current position and route-manag...
int getLeg() const
FGAIWaypoint * getCurrentWaypoint(void) const
FGAIAircraft * getUserAircraft() const
Retrieve the representation of the user's aircraft in the AI manager the position and velocity of thi...
const std::string & getName()
class FGATCController NOTE: this class serves as an abstraction layer for all sorts of ATC controller...
void postinit() override
Sets up ATC subsystem parts depending on other subsystems Override of SGSubsystem::postinit() Will se...
Definition atc_mgr.cxx:67
FGATCController * getEnRouteController()
Definition atc_mgr.cxx:460
void addController(FGATCController *controller)
Adds FGATCController instance to std::vector activeStations.
Definition atc_mgr.cxx:280
void shutdown() override
Shutdown method Clears activeStations vector in preparation for clean shutdown Override of SGSubsyste...
Definition atc_mgr.cxx:247
void reposition()
Definition atc_mgr.cxx:256
FGATCManager()
Constructor, initializes values to private boolean and FGATCController instances.
Definition atc_mgr.cxx:47
virtual ~FGATCManager()
Default destructor.
Definition atc_mgr.cxx:59
void update(double time) override
Update the subsystem.
Definition atc_mgr.cxx:304
void removeController(FGATCController *controller)
Searches for and removes FGATCController instance from std::vector activeStations.
Definition atc_mgr.cxx:287
std::string getType() const
Definition parking.hxx:60
std::string getName() const
Definition parking.hxx:62
double getRadius() const
Definition parking.hxx:58
FGParking * parking() const
Definition dynamics.cxx:135
bool isValid() const
Definition dynamics.cxx:130
static FGAirportDynamicsRef find(const std::string &icao)
std::string fgGetString(const char *name, const char *defaultValue)
Get a string value for a property.
Definition fg_props.cxx:556
FGGlobals * globals
Definition globals.cxx:142
@ RUNWAY_TAXI
@ STARTUP_PUSHBACK
bool fgSetDouble(const char *name, double defaultValue)
Set a double value for a property.
Definition proptest.cpp:31
bool fgGetBool(char const *name, bool def)
Get a bool value for a property.
Definition proptest.cpp:25
double fgGetDouble(const char *name, double defaultValue)
Get a double value for a property.
Definition proptest.cpp:30
bool fgSetString(char const *name, char const *str)
Set a string value for a property.
Definition proptest.cpp:26