]> git.mxchange.org Git - flightgear.git/blob - src/Traffic/Schedule.cxx
merging in topic/makej
[flightgear.git] / src / Traffic / Schedule.cxx
1 /******************************************************************************
2  * Schedule.cxx
3  * Written by Durk Talsma, started May 5, 2004.
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
24 #ifdef HAVE_CONFIG_H
25 #  include "config.h"
26 #endif
27
28 #define BOGUS 0xFFFF
29
30 #include <stdlib.h>
31 #include <time.h>
32 #include <iostream>
33 #include <fstream>
34
35
36 #include <string>
37 #include <vector>
38 #include <algorithm>
39
40 #include <simgear/compiler.h>
41 #include <simgear/sg_inlines.h>
42 #include <simgear/math/sg_geodesy.hxx>
43 #include <simgear/props/props.hxx>
44 #include <simgear/route/waypoint.hxx>
45 #include <simgear/structure/subsystem_mgr.hxx>
46 #include <simgear/xml/easyxml.hxx>
47
48 #include <AIModel/AIFlightPlan.hxx>
49 #include <AIModel/AIManager.hxx>
50 #include <AIModel/AIAircraft.hxx>
51 #include <Airports/simple.hxx>
52 #include <Main/fg_init.hxx>   // That's pretty ugly, but I need fgFindAirportID
53
54
55 #include "SchedFlight.hxx"
56 #include "TrafficMgr.hxx"
57
58 using std::sort;
59
60 /******************************************************************************
61  * the FGAISchedule class contains data members and code to maintain a
62  * schedule of Flights for an articically controlled aircraft. 
63  *****************************************************************************/
64 FGAISchedule::FGAISchedule()
65 {
66   firstRun     = true;
67   AIManagerRef = 0;
68
69   heavy = false;
70   lat = 0;
71   lon = 0;
72   radius = 0;
73   groundOffset = 0;
74   distanceToUser = 0;
75   //score = 0;
76 }
77
78 /*
79 FGAISchedule::FGAISchedule(string    mdl, 
80                            string    liv, 
81                            string    reg, 
82                            bool      hvy, 
83                            string act, 
84                            string arln, 
85                            string mclass, 
86                            string fltpe,
87                            double rad,
88                            double grnd,
89                            int    scre,
90                            FGScheduledFlightVec flt)*/
91 FGAISchedule::FGAISchedule(string model, 
92                            string lvry,
93                            string port, 
94                            string reg, 
95                            string flightId,
96                            bool   hvy, 
97                            string act, 
98                            string arln, 
99                            string mclass, 
100                            string fltpe, 
101                            double rad, 
102                            double grnd)
103 {
104   modelPath        = model; 
105   livery           = lvry; 
106   homePort         = port;
107   registration     = reg;
108   flightIdentifier = flightId;
109   acType           = act;
110   airline          = arln;
111   m_class          = mclass;
112   flightType       = fltpe;
113   lat              = 0;
114   lon              = 0;
115   radius           = rad;
116   groundOffset     = grnd;
117   distanceToUser   = 0;
118   heavy            = hvy;
119   /*for (FGScheduledFlightVecIterator i = flt.begin();
120        i != flt.end();
121        i++)
122     flights.push_back(new FGScheduledFlight((*(*i))));*/
123   AIManagerRef     = 0;
124   //score    = scre;
125   firstRun         = true;
126 }
127
128 FGAISchedule::FGAISchedule(const FGAISchedule &other)
129 {
130   modelPath          = other.modelPath;
131   homePort           = other.homePort;
132   livery             = other.livery;
133   registration       = other.registration;
134   heavy              = other.heavy;
135   flightIdentifier   = other.flightIdentifier;
136   flights            = other.flights;
137   lat                = other.lat;
138   lon                = other.lon;
139   AIManagerRef       = other.AIManagerRef;
140   acType             = other.acType;
141   airline            = other.airline;
142   m_class            = other.m_class;
143   firstRun           = other.firstRun;
144   radius             = other.radius;
145   groundOffset       = other.groundOffset;
146   flightType         = other.flightType;
147   //score            = other.score;
148   distanceToUser     = other.distanceToUser;
149   currentDestination = other.currentDestination;
150   firstRun           = other.firstRun;
151 }
152
153
154 FGAISchedule::~FGAISchedule()
155 {
156 /*  for (FGScheduledFlightVecIterator flt = flights.begin(); flt != flights.end(); flt++)
157     {
158       delete (*flt);
159     }
160   flights.clear();*/
161
162
163 bool FGAISchedule::init()
164 {
165   //tm targetTimeDate;
166   //SGTime* currTimeDate = globals->get_time_params();
167
168   //tm *temp = currTimeDate->getGmt();
169   //char buffer[512];
170   //sgTimeFormatTime(&targetTimeDate, buffer);
171   //cout << "Scheduled Time " << buffer << endl; 
172   //cout << "Time :" << time(NULL) << " SGTime : " << sgTimeGetGMT(temp) << endl;
173   /*for (FGScheduledFlightVecIterator i = flights.begin(); 
174        i != flights.end(); 
175        i++)
176     {
177       //i->adjustTime(now);
178       if (!((*i)->initializeAirports()))
179         return false;
180     } */
181   //sort(flights.begin(), flights.end());
182   // Since time isn't initialized yet when this function is called,
183   // Find the closest possible airport.
184   // This should give a reasonable initialization order. 
185   //setClosestDistanceToUser();
186   return true;
187 }
188
189 bool FGAISchedule::update(time_t now)
190
191   FGAirport *dep;
192   FGAirport *arr;
193   double angle;
194
195   FGAIManager *aimgr;
196   string airport;
197   
198   double courseToUser,   courseToDest;
199   double distanceToDest;
200   double speed;
201
202   time_t 
203     totalTimeEnroute, 
204     elapsedTimeEnroute,
205     remainingTimeEnroute, deptime = 0;
206   double
207     userLatitude,
208     userLongitude;
209
210   if (fgGetBool("/sim/traffic-manager/enabled") == false)
211     return true;
212   
213   aimgr = (FGAIManager *) globals-> get_subsystem("ai_model");  
214     // Out-of-work aircraft seeks employment. Willing to work irregular hours ...
215     //cerr << "About to find a flight " << endl;
216     if (flights.empty()) {
217         //execute this loop at least once. 
218         SG_LOG(SG_GENERAL, SG_INFO, "Scheduling for : " << modelPath << " " <<  registration << " " << homePort);
219         FGScheduledFlight *flight = 0;
220          do {
221             flight = findAvailableFlight(currentDestination, flightIdentifier);
222             if (flight) {
223                 currentDestination = flight->getArrivalAirport()->getId();
224                 time_t arr, dep;
225                 dep = flight->getDepartureTime();
226                 arr = flight->getArrivalTime();
227                 string depT = asctime(gmtime(&dep));
228                 string arrT = asctime(gmtime(&arr));
229
230                 depT = depT.substr(0,24);
231                 arrT = arrT.substr(0,24);
232                 SG_LOG(SG_GENERAL, SG_INFO, "  " << flight->getCallSign() << ":" 
233                                          << "  " << flight->getDepartureAirport()->getId() << ":"
234                                          << "  " << depT << ":"
235                                          << " \"" << flight->getArrivalAirport()->getId() << "\"" << ":"
236                                          << "  " << arrT << ":");
237             flights.push_back(flight);
238             }
239         } while ((currentDestination != homePort) && (flight != 0));
240         SG_LOG(SG_GENERAL, SG_INFO, cerr << " Done " << endl);
241     }
242     //cerr << " Done " << endl;
243    // No flights available for this aircraft
244   if (flights.size() == 0) {
245       return false;
246   }
247   // Sort all the scheduled flights according to scheduled departure time.
248   // Because this is done at every update, we only need to check the status
249   // of the first listed flight. 
250   //sort(flights.begin(), flights.end(), compareScheduledFlights);
251  if (firstRun) {
252      if (fgGetBool("/sim/traffic-manager/instantaneous-action") == true) {
253          deptime = now + rand() % 300; // Wait up to 5 minutes until traffic starts moving to prevent too many aircraft 
254                                    // from cluttering the gate areas.
255          cerr << "Scheduling " << registration << " for instantaneous action flight " << endl;
256      }
257      firstRun = false;
258   }
259   if (!deptime)
260     deptime = (*flights.begin())->getDepartureTime();
261   FGScheduledFlightVecIterator i = flights.begin();
262   SG_LOG (SG_GENERAL, SG_DEBUG,"Traffic Manager: Processing registration " << registration << " with callsign " << (*i)->getCallSign());
263   if (AIManagerRef)
264     {
265       // Check if this aircraft has been released. 
266       FGTrafficManager *tmgr = (FGTrafficManager *) globals->get_subsystem("Traffic Manager");
267       if (tmgr->isReleased(AIManagerRef))
268         AIManagerRef = 0;
269     }
270
271   if (!AIManagerRef)
272     {
273       userLatitude  = fgGetDouble("/position/latitude-deg");
274       userLongitude = fgGetDouble("/position/longitude-deg");
275
276       //cerr << "Estimated minimum distance to user: " << distanceToUser << endl;
277       // This flight entry is entirely in the past, do we need to 
278       // push it forward in time to the next scheduled departure. 
279       if (((*i)->getDepartureTime() < now) && ((*i)->getArrivalTime() < now))
280         {
281           SG_LOG (SG_GENERAL, SG_DEBUG, "Traffic Manager:      Flight is in the Past");
282           //cerr << modelPath << " " <<  registration << ": Flights from the past belong to the past :-)" << endl;
283           //exit(1);
284           // Don't just update: check whether we need to load a new leg. etc.
285           // This update occurs for distant aircraft, so we can update the current leg
286           // and detach it from the current list of aircraft. 
287           (*i)->update();
288           i = flights.erase(i);
289           return true;
290         }
291
292       // Departure time in the past and arrival time in the future.
293       // This flight is in progress, so we need to calculate it's
294       // approximate position and -if in range- create an AIAircraft
295       // object for it. 
296       //if ((i->getDepartureTime() < now) && (i->getArrivalTime() > now))
297       
298       // Part of this flight is in the future.
299       if ((*i)->getArrivalTime() > now)
300         {
301           
302           dep = (*i)->getDepartureAirport();
303           arr = (*i)->getArrivalAirport  ();
304           if (!(dep && arr))
305             return false;
306           
307           SGVec3d a = SGVec3d::fromGeoc(SGGeoc::fromDegM(dep->getLongitude(),
308                                                 dep->getLatitude(), 1));
309           SGVec3d b = SGVec3d::fromGeoc(SGGeoc::fromDegM(arr->getLongitude(),
310                                                 arr->getLatitude(), 1));
311           SGVec3d _cross = cross(b, a);
312           
313           angle = sgACos(dot(a, b));
314           
315           // Okay, at this point we have the angle between departure and 
316           // arrival airport, in degrees. From here we can interpolate the
317           // position of the aircraft by calculating the ratio between 
318           // total time enroute and elapsed time enroute. 
319  
320           totalTimeEnroute     = (*i)->getArrivalTime() - (*i)->getDepartureTime();
321           if (now > (*i)->getDepartureTime())
322             {
323               //err << "Lat = " << lat << ", lon = " << lon << endl;
324               //cerr << "Time diff: " << now-i->getDepartureTime() << endl;
325               elapsedTimeEnroute   = now - (*i)->getDepartureTime();
326               remainingTimeEnroute = (*i)->getArrivalTime()   - now;  
327               SG_LOG (SG_GENERAL, SG_DEBUG, "Traffic Manager:      Flight is in progress.");
328             }
329           else
330             {
331               lat = dep->getLatitude();
332               lon = dep->getLongitude();
333               elapsedTimeEnroute = 0;
334               remainingTimeEnroute = totalTimeEnroute;
335               SG_LOG (SG_GENERAL, SG_DEBUG, "Traffic Manager:      Flight is pending.");
336             }
337                   
338           angle *= ( (double) elapsedTimeEnroute/ (double) totalTimeEnroute);
339           
340           
341           //cout << "a = " << a[0] << " " << a[1] << " " << a[2] 
342           //     << "b = " << b[0] << " " << b[1] << " " << b[2] << endl;  
343           sgdMat4 matrix;
344           sgdMakeRotMat4(matrix, angle, _cross.sg()); 
345           SGVec3d newPos(0, 0, 0);
346           for(int j = 0; j < 3; j++)
347             {
348               for (int k = 0; k<3; k++)
349                 {
350                   newPos[j] += matrix[j][k]*a[k];
351                 }
352             }
353           
354           if (now > (*i)->getDepartureTime())
355             {
356               SGGeoc geoc = SGGeoc::fromCart(newPos);
357               lat = geoc.getLatitudeDeg();
358               lon = geoc.getLongitudeDeg(); 
359             }
360           else
361             {
362               lat = dep->getLatitude();
363               lon = dep->getLongitude();
364             }
365           
366           
367           SGWayPoint current  (lon,
368                                lat,
369                                (*i)->getCruiseAlt(), 
370                                SGWayPoint::SPHERICAL);
371           SGWayPoint user (   userLongitude,
372                               userLatitude,
373                               (*i)->getCruiseAlt(), 
374                               SGWayPoint::SPHERICAL);
375           SGWayPoint dest (   arr->getLongitude(),
376                               arr->getLatitude(),
377                               (*i)->getCruiseAlt(), 
378                               SGWayPoint::SPHERICAL);
379           // We really only need distance to user
380           // and course to destination 
381           user.CourseAndDistance(current, &courseToUser, &distanceToUser);
382           dest.CourseAndDistance(current, &courseToDest, &distanceToDest);
383           speed =  (distanceToDest*SG_METER_TO_NM) / 
384             ((double) remainingTimeEnroute/3600.0);
385           
386
387           // If distance between user and simulated aircaft is less
388           // then 500nm, create this flight. At jet speeds 500 nm is roughly
389           // one hour flight time, so that would be a good approximate point
390           // to start a more detailed simulation of this aircraft.
391           SG_LOG (SG_GENERAL, SG_DEBUG, "Traffic manager: " << registration << " is scheduled for a flight from " 
392              << dep->getId() << " to " << arr->getId() << ". Current distance to user: " 
393              << distanceToUser*SG_METER_TO_NM);
394           if ((distanceToUser*SG_METER_TO_NM) < TRAFFICTOAIDISTTOSTART)
395             {
396               string flightPlanName = dep->getId() + string("-") + arr->getId() + 
397                 string(".xml");
398               SG_LOG (SG_GENERAL, SG_DEBUG, "Traffic manager: Creating AIModel");
399               //int alt;
400               //if  ((i->getDepartureTime() < now))
401               //{
402               //          alt = i->getCruiseAlt() *100;
403               //        }
404               //else
405               //{
406               //          alt = dep->_elevation+19;
407               //        }
408
409               // Only allow traffic to be created when the model path (or the AI version of mp) exists
410               SGPath mp(globals->get_fg_root());
411               SGPath mp_ai = mp;
412
413               mp.append(modelPath);
414               mp_ai.append("AI");
415               mp_ai.append(modelPath);
416
417               if (mp.exists() || mp_ai.exists())
418               {
419                   FGAIAircraft *aircraft = new FGAIAircraft(this);
420                   aircraft->setPerformance(m_class); //"jet_transport";
421                   aircraft->setCompany(airline); //i->getAirline();
422                   aircraft->setAcType(acType); //i->getAcType();
423                   aircraft->setPath(modelPath.c_str());
424                   //aircraft->setFlightPlan(flightPlanName);
425                   aircraft->setLatitude(lat);
426                   aircraft->setLongitude(lon);
427                   aircraft->setAltitude((*i)->getCruiseAlt()*100); // convert from FL to feet
428                   aircraft->setSpeed(speed);
429                   aircraft->setBank(0);
430                   aircraft->SetFlightPlan(new FGAIFlightPlan(aircraft, flightPlanName, courseToDest, deptime, 
431                                                              dep, arr,true, radius, 
432                                                              (*i)->getCruiseAlt()*100, 
433                                                              lat, lon, speed, flightType, acType, 
434                                                              airline));
435                   aimgr->attach(aircraft);
436                   
437                   
438                   AIManagerRef = aircraft->getID();
439                   //cerr << "Class: " << m_class << ". acType: " << acType << ". Airline: " << airline << ". Speed = " << speed << ". From " << dep->getId() << " to " << arr->getId() << ". Time Fraction = " << (remainingTimeEnroute/(double) totalTimeEnroute) << endl;
440                   //cerr << "Latitude : " << lat << ". Longitude : " << lon << endl;
441                   //cerr << "Dep      : " << dep->getLatitude()<< ", "<< dep->getLongitude() << endl;
442                   //cerr << "Arr      : " << arr->getLatitude()<< ", "<< arr->getLongitude() << endl;
443                   //cerr << "Time remaining = " << (remainingTimeEnroute/3600.0) << endl;
444                   //cerr << "Total time     = " << (totalTimeEnroute/3600.0) << endl;
445                   //cerr << "Distance remaining = " << distanceToDest*SG_METER_TO_NM << endl;
446                   }
447               else
448                 {
449                   SG_LOG(SG_INPUT, SG_WARN, "TrafficManager: Could not load model " << mp.str());
450                 }
451             }
452           return true;
453     }
454
455       // Both departure and arrival time are in the future, so this
456       // the aircraft is parked at the departure airport.
457       // Currently this status is mostly ignored, but in future
458       // versions, code should go here that -if within user range-
459       // positions these aircraft at parking locations at the airport.
460       if (((*i)->getDepartureTime() > now) && ((*i)->getArrivalTime() > now))
461         { 
462           dep = (*i)->getDepartureAirport();
463           return true;
464         } 
465     }
466   //cerr << "Traffic schedule got to beyond last clause" << endl;
467     // EMH: prevent a warning, should this be 'true' instead?
468     // DT: YES. Originally, this code couldn't be reached, but
469     // when the "if(!(AIManagerManager))" clause is false we
470     // fall through right to the end. This is a valid flow.
471     // the actual value is pretty innocent, only it triggers
472     // warning in TrafficManager::update().
473     // (which was added as a sanity check for myself in the first place. :-)
474     return true;
475 }
476
477
478 bool FGAISchedule::next()
479 {
480   FGScheduledFlightVecIterator i = flights.begin();
481   (*i)->release();
482   //FIXME: remove first entry, 
483   // load new flights until back at home airport
484   // Lock loaded flights
485   //sort(flights.begin(), flights.end(), compareScheduledFlights);
486   // until that time
487   i = flights.erase(i);
488   //cerr << "Next: scheduling for : " << modelPath << " " <<  registration << endl;
489   FGScheduledFlight *flight = findAvailableFlight(currentDestination, flightIdentifier);
490   if (flight) {
491       currentDestination = flight->getArrivalAirport()->getId();
492       time_t arr, dep;
493       dep = flight->getDepartureTime();
494       arr = flight->getArrivalTime();
495       string depT = asctime(gmtime(&dep));
496       string arrT = asctime(gmtime(&arr));
497
498       depT = depT.substr(0,24);
499       arrT = arrT.substr(0,24);
500       //cerr << "  " << flight->getCallSign() << ":" 
501       //     << "  " << flight->getDepartureAirport()->getId() << ":"
502       //     << "  " << depT << ":"
503       //     << " \"" << flight->getArrivalAirport()->getId() << "\"" << ":"
504       //     << "  " << arrT << ":" << endl;
505
506        flights.push_back(flight);
507        return true;
508   } else {
509        return false;
510   }
511   //cerr << "FGAISchedule :: next needs updating" << endl;
512   //exit(1);
513 }
514
515 FGScheduledFlight* FGAISchedule::findAvailableFlight (const string &currentDestination,
516                                                       const string &req)
517 {
518     time_t now = time(NULL) + fgGetLong("/sim/time/warp");
519
520     FGTrafficManager *tmgr = (FGTrafficManager *) globals->get_subsystem("Traffic Manager");
521     FGScheduledFlightVecIterator fltBegin, fltEnd;
522     fltBegin = tmgr->getFirstFlight(req);
523     fltEnd   = tmgr->getLastFlight(req);
524
525
526      //cerr << "Finding available flight " << endl;
527      // For Now:
528      // Traverse every registered flight
529      if (fltBegin == fltEnd) {
530           //cerr << "No Flights Scheduled for " << req << endl;
531      }
532      int counter = 0;
533      for (FGScheduledFlightVecIterator i = fltBegin; i != fltEnd; i++) {
534           (*i)->adjustTime(now);
535            //sort(fltBegin, fltEnd, compareScheduledFlights);
536            //cerr << counter++ << endl;
537      }
538      sort(fltBegin, fltEnd, compareScheduledFlights);
539      for (FGScheduledFlightVecIterator i = fltBegin; i != fltEnd; i++) {
540           //bool valid = true;
541           counter++;
542           if (!(*i)->isAvailable()) {
543                //cerr << (*i)->getCallSign() << "is no longer available" << endl;
544                continue;
545           }
546           if (!((*i)->getRequirement() == req)) {
547                continue;
548           }
549           if (!(((*i)->getArrivalAirport()) && ((*i)->getDepartureAirport()))) {
550               continue;
551           }
552           if (!(currentDestination.empty())) {
553               if (currentDestination != (*i)->getDepartureAirport()->getId()) {
554                    //cerr << (*i)->getCallSign() << "Doesn't match destination" << endl;
555                    //cerr << "Current Destination " << currentDestination << "Doesnt match flight's " <<
556                    //          (*i)->getArrivalAirport()->getId() << endl;
557                    continue;
558               }
559           }
560           //TODO: check time
561           // So, if we actually get here, we have a winner
562           //cerr << "found flight: " << req << " : " << currentDestination << " : " <<       
563           //         (*i)->getArrivalAirport()->getId() << endl;
564           (*i)->lock();
565           return (*i);
566      }
567      // matches req?
568      // if currentDestination has a value, does it match departure of next flight?
569      // is departure time later than planned arrival?
570      // is departure port valid?
571      // is arrival port valid?
572      //cerr << "Ack no flight found: " << endl;
573      return 0;
574 }
575
576 double FGAISchedule::getSpeed()
577 {
578   FGScheduledFlightVecIterator i = flights.begin();
579  
580   FGAirport* dep = (*i)->getDepartureAirport(),
581    *arr = (*i)->getArrivalAirport();
582   double dist = SGGeodesy::distanceNm(dep->geod(), arr->geod());
583   double remainingTimeEnroute = (*i)->getArrivalTime() - (*i)->getDepartureTime();
584
585   double speed = dist / (remainingTimeEnroute/3600.0);
586   SG_CLAMP_RANGE(speed, 300.0, 500.0);
587   return speed;
588 }
589 /*
590 bool compareSchedules(FGAISchedule*a, FGAISchedule*b)
591
592   //return (*a) < (*b); 
593
594 */
595
596 // void FGAISchedule::setClosestDistanceToUser()
597 // {
598   
599   
600 //   double course;
601 //   double dist;
602
603 //   Point3D temp;
604 //   time_t 
605 //     totalTimeEnroute, 
606 //     elapsedTimeEnroute;
607  
608 //   double userLatitude  = fgGetDouble("/position/latitude-deg");
609 //   double userLongitude = fgGetDouble("/position/longitude-deg");
610
611 //   FGAirport *dep;
612   
613 // #if defined( __CYGWIN__) || defined( __MINGW32__)
614 //   #define HUGE HUGE_VAL
615 // #endif
616 //   distanceToUser = HUGE;
617 //   FGScheduledFlightVecIterator i = flights.begin();
618 //   while (i != flights.end())
619 //     {
620 //       dep = i->getDepartureAirport();
621 //       //if (!(dep))
622 //       //return HUGE;
623       
624 //       SGWayPoint user (   userLongitude,
625 //                        userLatitude,
626 //                        i->getCruiseAlt());
627 //       SGWayPoint current (dep->getLongitude(),
628 //                        dep->getLatitude(),
629 //                        0);
630 //       user.CourseAndDistance(current, &course, &dist);
631 //       if (dist < distanceToUser)
632 //      {
633 //        distanceToUser = dist;
634 //        //cerr << "Found closest distance to user for " << registration << " to be " << distanceToUser << " at airport " << dep->getId() << endl;
635 //      }
636 //       i++;
637 //     }
638 //   //return distToUser;
639 // }
640