source: src/mlx/web.py@ 1033:330058d37574

python3
Last change on this file since 1033:330058d37574 was 1033:330058d37574, checked in by István Váradi <ivaradi@…>, 2 years ago

Updates for the new crew and passenger handling (re #357)

File size: 54.9 KB
Line 
1
2from . import const
3from . import util
4from .rpc import Registration
5from . import rpc
6from . import rpccommon
7
8from .common import MAVA_BASE_URL
9from .pirep import PIREP
10
11import threading
12import sys
13import urllib.request, urllib.parse, urllib.error
14import urllib.request, urllib.error, urllib.parse
15import hashlib
16import time
17import re
18import datetime
19import codecs
20import traceback
21import xml.sax
22import xmlrpc.client
23import html.parser
24import certifi
25
26#---------------------------------------------------------------------------------------
27
28## @package mlx.web
29#
30# Web interface.
31#
32# This module implements a thread that can perform (HTTP) requests
33# asynchronously. When the request is performed, a callback is called. The main
34# interface is the \ref Handler class. Each of its functions creates a \ref
35# Request subclass instance and puts it to the request queue. The handler
36# thread then takes the requests one by one, and executes them.
37#
38# This module also defines some data classes the contents of which are
39# retrieved or sent via HTTP. \ref BookedFlight contains data of a flight
40# booked on the MAVA website, \ref Fleet and \ref Plane represents the MAVA
41# fleet and the gates at Ferihegy and \ref NOTAM is a NOTAM.
42
43#---------------------------------------------------------------------------------------
44
45def readline(f):
46 """Read a line from the given file.
47
48 The line is stripped and empty lines are discarded."""
49 while True:
50 line = f.readline()
51 if not line: return ""
52 line = line.strip()
53 if line:
54 return line
55
56#---------------------------------------------------------------------------------------
57
58class BookedFlight(object):
59 """A flight that was booked."""
60 TYPECODE2TYPE = { "B736" : const.AIRCRAFT_B736,
61 "B737" : const.AIRCRAFT_B737,
62 "B738" : const.AIRCRAFT_B738,
63 "B73H" : const.AIRCRAFT_B738C,
64 "B732" : const.AIRCRAFT_B732,
65 "B733" : const.AIRCRAFT_B733,
66 "B734" : const.AIRCRAFT_B734,
67 "B735" : const.AIRCRAFT_B735,
68 "DH8D" : const.AIRCRAFT_DH8D,
69 "B762" : const.AIRCRAFT_B762,
70 "B763" : const.AIRCRAFT_B763,
71 "CRJ2" : const.AIRCRAFT_CRJ2,
72 "F70" : const.AIRCRAFT_F70,
73 "LI2" : const.AIRCRAFT_DC3,
74 "T134" : const.AIRCRAFT_T134,
75 "T154" : const.AIRCRAFT_T154,
76 "YK40" : const.AIRCRAFT_YK40,
77 "B462" : const.AIRCRAFT_B462,
78 "IL62" : const.AIRCRAFT_IL62 }
79
80 TYPE2TYPECODE = { const.AIRCRAFT_B736 : "B736",
81 const.AIRCRAFT_B737 : "B737",
82 const.AIRCRAFT_B738 : "B738",
83 const.AIRCRAFT_B738C : "B73H",
84 const.AIRCRAFT_B732 : "B732",
85 const.AIRCRAFT_B733 : "B733",
86 const.AIRCRAFT_B734 : "B734",
87 const.AIRCRAFT_B735 : "B735",
88 const.AIRCRAFT_DH8D : "DH8D",
89 const.AIRCRAFT_B762 : "B762",
90 const.AIRCRAFT_B763 : "B763",
91 const.AIRCRAFT_CRJ2 : "CRJ2",
92 const.AIRCRAFT_F70 : "F70",
93 const.AIRCRAFT_DC3 : "LI2",
94 const.AIRCRAFT_T134 : "T134",
95 const.AIRCRAFT_T154 : "T155",
96 const.AIRCRAFT_YK40 : "YK40",
97 const.AIRCRAFT_B462 : "B462",
98 const.AIRCRAFT_IL62 : "IL62" }
99
100 checkFlightTypes = [ const.AIRCRAFT_B736, const.AIRCRAFT_B737,
101 const.AIRCRAFT_B738, const.AIRCRAFT_DH8D ]
102
103 STATUS_BOOKED = 1
104
105 STATUS_REPORTED = 2
106
107 STATUS_ACCEPTED = 3
108
109 STATUS_REJECTED = 4
110
111 @staticmethod
112 def getDateTime(date, time):
113 """Get a datetime object from the given textual date and time."""
114 return datetime.datetime.strptime(date + " " + time,
115 "%Y-%m-%d %H:%M:%S")
116
117 @staticmethod
118 def forCheckFlight(aircraftType):
119 """Create a booked flight for a check flight with the given aircraft
120 type."""
121 flight = BookedFlight()
122
123 flight.departureICAO = "LHBP"
124 flight.arrivalICAO = "LHBP"
125
126 flight.aircraftType = aircraftType
127 flight.aircraftTypeName = BookedFlight.TYPE2TYPECODE[aircraftType]
128
129 # FIXME: perhaps find one for the type
130 flight.tailNumber = "HA-CHK"
131 flight.callsign = "HA-CHK"
132
133 flight.numPassengers = 0
134 flight.numCrew = 2
135 flight.bagWeight = 0
136 flight.cargoWeight = 0
137 flight.mailWeight = 0
138 flight.route = "DCT"
139
140 t = datetime.datetime.now() + datetime.timedelta(minutes = 20)
141 flight.departureTime = datetime.datetime(t.year, t.month, t.day,
142 t.hour, t.minute)
143 t = flight.departureTime + datetime.timedelta(minutes = 30)
144 flight.arrivalTime = datetime.datetime(t.year, t.month, t.day,
145 t.hour, t.minute)
146
147 return flight
148
149 def __init__(self, id = None):
150 """Construct a booked flight with the given ID."""
151 self.id = id
152
153 @property
154 def status(self):
155 """Get the status of the flight.
156
157 For web-based flights this is always STATUS_BOOKED."""
158 return BookedFlight.STATUS_BOOKED
159
160 def readFromWeb(self, f):
161 """Read the data of the flight from the web via the given file
162 object."""
163 self.callsign = readline(f)
164
165 date = readline(f)
166 print("web.BookedFlight.readFromWeb: date:", date)
167 if date=="0000-00-00": date = "0001-01-01"
168
169 self.departureICAO = readline(f)
170 self.arrivalICAO = readline(f)
171
172 self._readAircraftType(f)
173 self.tailNumber = readline(f)
174 self.numPassengers = int(readline(f))
175 self.numCrew = int(readline(f))
176 self.bagWeight = int(readline(f))
177 self.cargoWeight = int(readline(f))
178 self.mailWeight = int(readline(f))
179 self.route = readline(f)
180
181 departureTime = readline(f)
182 self.departureTime = BookedFlight.getDateTime(date, departureTime)
183
184 arrivalTime = readline(f)
185 self.arrivalTime = BookedFlight.getDateTime(date, arrivalTime)
186 if self.arrivalTime<self.departureTime:
187 self.arrivalTime += datetime.timedelta(days = 1)
188
189 if not readline(f)==".NEXT.":
190 raise Exception("Invalid line in flight data")
191
192 def readFromFile(self, f):
193 """Read the data of the flight from a file via the given file
194 object."""
195 date = None
196 departureTime = None
197 arrivalTime = None
198
199 line = f.readline()
200 lineNumber = 0
201 while line:
202 lineNumber += 1
203 line = line.strip()
204
205 hashIndex = line.find("#")
206 if hashIndex>=0: line = line[:hashIndex]
207 if line:
208 equalIndex = line.find("=")
209 lineOK = equalIndex>0
210
211 if lineOK:
212 key = line[:equalIndex].strip()
213 value = line[equalIndex+1:].strip().replace("\:", ":")
214
215 lineOK = key and value
216
217 if lineOK:
218 if key=="callsign": self.callsign = value
219 elif key=="date": date = value
220 elif key=="dep_airport": self.departureICAO = value
221 elif key=="dest_airport": self.arrivalICAO = value
222 elif key=="planecode": self.aircraftType = \
223 self._decodeAircraftType(value)
224 elif key=="planetype": self.aircraftTypeName = value
225 elif key=="tail_nr": self.tailNumber = value
226 elif key=="passenger": self.numPassengers = int(value)
227 elif key=="crew": self.numCrew = int(value)
228 elif key=="bag": self.bagWeight = int(value)
229 elif key=="cargo": self.cargoWeight = int(value)
230 elif key=="mail": self.mailWeight = int(value)
231 elif key=="flight_route": self.route = value
232 elif key=="departure_time": departureTime = value
233 elif key=="arrival_time": arrivalTime = value
234 elif key=="foglalas_id":
235 self.id = None if value=="0" else value
236 else: lineOK = False
237
238 if not lineOK:
239 print("web.BookedFlight.readFromFile: line %d is invalid" % \
240 (lineNumber,))
241
242 line = f.readline()
243
244 if date is not None:
245 if departureTime is not None:
246 self.departureTime = BookedFlight.getDateTime(date,
247 departureTime)
248 if arrivalTime is not None:
249 self.arrivalTime = BookedFlight.getDateTime(date,
250 arrivalTime)
251
252 d = dir(self)
253 for attribute in ["callsign", "departureICAO", "arrivalICAO",
254 "aircraftType", "tailNumber",
255 "numPassengers", "numCrew",
256 "bagWeight", "cargoWeight", "mailWeight",
257 "route", "departureTime", "arrivalTime"]:
258 if attribute not in d:
259 raise Exception("Attribute %s could not be read" % (attribute,))
260
261 if "aircraftTypeName" not in d:
262 self.aircraftTypeName = \
263 BookedFlight.TYPE2TYPECODE[self.aircraftType]
264
265 def setupFromPIREPData(self, pirepData):
266 """Setup the booked flight from the given PIREP data."""
267 bookedFlightData = pirepData["bookedFlight"]
268
269 self.callsign = bookedFlightData["callsign"]
270
271 date = bookedFlightData["date"]
272
273 departureTime = bookedFlightData["departureTime"]
274 self.departureTime = BookedFlight.getDateTime(date, departureTime)
275
276 arrivalTime = bookedFlightData["arrivalTime"]
277 self.arrivalTime = BookedFlight.getDateTime(date, arrivalTime)
278 if self.arrivalTime<self.departureTime:
279 self.arrivalTime += datetime.timedelta(days = 1)
280
281 self.departureICAO = bookedFlightData["departureICAO"]
282 self.arrivalICAO = bookedFlightData["arrivalICAO"]
283
284 self.aircraftType = \
285 self._decodeAircraftType(bookedFlightData["aircraftType"])
286 self.tailNumber = bookedFlightData["tailNumber"]
287 self.numPassengers = int(bookedFlightData["numPassengers"])
288 self.numChildren = int(bookedFlightData["numChildren"])
289 self.numInfants = int(bookedFlightData["numInfants"])
290 self.maxPassengers = int(bookedFlightData["maxPassengers"])
291 self.numCockpitCrew = int(bookedFlightData["numCockpitCrew"])
292 self.numCabinCrew = int(bookedFlightData["numCabinCrew"])
293 self.bagWeight = int(bookedFlightData["bagWeight"])
294 self.cargoWeight = int(bookedFlightData["cargoWeight"])
295 self.mailWeight = int(bookedFlightData["mailWeight"])
296 self.route = bookedFlightData["route"]
297
298 def writeIntoFile(self, f):
299 """Write the flight into a file."""
300 print("callsign=%s" % (self.callsign,), file=f)
301 date = self.departureTime.date()
302 print("date=%04d-%02d-%0d" % (date.year, date.month, date.day), file=f)
303 print("dep_airport=%s" % (self.departureICAO,), file=f)
304 print("dest_airport=%s" % (self.arrivalICAO,), file=f)
305 print("planecode=%s" % \
306 (BookedFlight.TYPE2TYPECODE[self.aircraftType],), file=f)
307 print("planetype=%s" % (self.aircraftTypeName,), file=f)
308 print("tail_nr=%s" % (self.tailNumber,), file=f)
309 print("passenger=%d" % (self.numPassengers,), file=f)
310 print("crew=%d" % (self.numCrew,), file=f)
311 print("bag=%d" % (self.bagWeight,), file=f)
312 print("cargo=%d" % (self.cargoWeight,), file=f)
313 print("mail=%d" % (self.mailWeight,), file=f)
314 print("flight_route=%s" % (self.route,), file=f)
315 departureTime = self.departureTime
316 print("departure_time=%02d\\:%02d\\:%02d" % \
317 (departureTime.hour, departureTime.minute, departureTime.second), file=f)
318 arrivalTime = self.arrivalTime
319 print("arrival_time=%02d\\:%02d\\:%02d" % \
320 (arrivalTime.hour, arrivalTime.minute, arrivalTime.second), file=f)
321 print("foglalas_id=%s" % ("0" if self.id is None else self.id,), file=f)
322
323 def _readAircraftType(self, f):
324 """Read the aircraft type from the given file."""
325 line = readline(f)
326 typeCode = line[:3]
327 self.aircraftType = self._decodeAircraftType(typeCode)
328 self.aircraftTypeName = line[3:]
329
330 def _decodeAircraftType(self, typeCode):
331 """Decode the aircraft type from the given typeCode."""
332 if typeCode in self.TYPECODE2TYPE:
333 return self.TYPECODE2TYPE[typeCode]
334 else:
335 raise Exception("Invalid aircraft type code: '" + typeCode + "'")
336
337 def __repr__(self):
338 """Get a representation of the flight."""
339 s = "<Flight: %s-%s, %s, %s-%s," % (self.departureICAO,
340 self.arrivalICAO,
341 self.route,
342 self.departureTime, self.arrivalTime)
343 s += " %d %s," % (self.aircraftType, self.tailNumber)
344 s += " pax=%d, crew=%d, bag=%d, cargo=%d, mail=%d" % \
345 (self.numPassengers, self.numCrew,
346 self.bagWeight, self.cargoWeight, self.mailWeight)
347 s += ">"
348 return s
349
350#------------------------------------------------------------------------------
351
352class Plane(rpccommon.Plane):
353 """Information about an airplane in the fleet."""
354 def __init__(self, s):
355 """Build a plane info based on the given string.
356
357 The string consists of three, space-separated fields.
358 The first field is the tail number, the second field is the gate
359 number, the third field is the plane's status as a character."""
360 super(Plane, self).__init__()
361
362 try:
363 words = s.split(" ")
364 tailNumber = words[0]
365 self.tailNumber = tailNumber
366
367 status = words[2] if len(words)>2 else None
368 self._setStatus(status)
369
370 gateNumber = words[1] if len(words)>1 else ""
371 self.gateNumber = gateNumber if gateNumber else None
372
373 except:
374 print("Plane string is invalid: '" + s + "'", file=sys.stderr)
375 self.tailNumber = None
376
377#------------------------------------------------------------------------------
378
379class Fleet(rpccommon.Fleet):
380 """Information about the whole fleet."""
381 def __init__(self, f):
382 """Construct the fleet information by reading the given file object."""
383 super(Fleet, self).__init__()
384
385 while True:
386 line = readline(f)
387 if not line or line == "#END": break
388
389 plane = Plane(line)
390 self._addPlane(plane)
391
392#------------------------------------------------------------------------------
393
394class NOTAM(object):
395 """A NOTAM for an airport."""
396 def __init__(self, ident, basic,
397 begin, notice, end = None, permanent = False,
398 repeatCycle = None):
399 """Construct the NOTAM."""
400 self.ident = ident
401 self.basic = basic
402 self.begin = begin
403 self.notice = notice
404 self.end = end
405 self.permanent = permanent
406 self.repeatCycle = repeatCycle
407
408 def __repr__(self):
409 """Get the representation of the NOTAM."""
410 s = "<NOTAM " + str(self.begin)
411 if self.end:
412 s += " - " + str(self.end)
413 elif self.permanent:
414 s += " - PERMANENT"
415 if self.repeatCycle:
416 s += " (" + self.repeatCycle + ")"
417 s += ": " + self.notice
418 s += ">"
419 return s
420
421 def __str__(self):
422 """Get the string representation of the NOTAM."""
423 s = ""
424 s += str(self.ident) + " " + str(self.basic) + "\n"
425 s += str(self.begin)
426 if self.end is not None:
427 s += " - " + str(self.end)
428 elif self.permanent:
429 s += " - PERMANENT"
430 s += "\n"
431 if self.repeatCycle:
432 s += "Repeat cycle: " + self.repeatCycle + "\n"
433 s += self.notice + "\n"
434 return s
435
436#------------------------------------------------------------------------------
437
438class NOTAMHandler(xml.sax.handler.ContentHandler):
439 """A handler for the NOTAM database."""
440 def __init__(self, airportICAOs):
441 """Construct the handler for the airports with the given ICAO code."""
442 self._notams = {}
443 for icao in airportICAOs:
444 self._notams[icao] = []
445
446 def startElement(self, name, attrs):
447 """Start an element."""
448 if name!="notam" or \
449 "ident" not in attrs or not attrs["ident"] or \
450 "Q" not in attrs or not attrs["Q"] or \
451 "A" not in attrs or not attrs["A"] or \
452 "B" not in attrs or not attrs["B"] or \
453 "E" not in attrs or not attrs["E"]:
454 return
455
456 icao = attrs["A"]
457 if icao not in self._notams:
458 return
459
460 begin = datetime.datetime.strptime(attrs["B"], "%Y-%m-%d %H:%M:%S")
461
462 c = attrs["C"] if "C" in attrs else None
463 end = datetime.datetime.strptime(c, "%Y-%m-%d %H:%M:%S") if c else None
464
465 permanent = attrs["C_flag"]=="PERM" if "C_flag" in attrs else False
466
467 repeatCycle = attrs["D"] if "D" in attrs else None
468
469 self._notams[icao].append(NOTAM(attrs["ident"], attrs["Q"],
470 begin, attrs["E"], end = end,
471 permanent = permanent,
472 repeatCycle = repeatCycle))
473
474 def get(self, icao):
475 """Get the NOTAMs for the given ICAO code."""
476 return self._notams[icao] if icao in self._notams else []
477
478#------------------------------------------------------------------------------
479
480class PilotsWebNOTAMsParser(html.parser.HTMLParser):
481 """XML handler for the NOTAM query results on the PilotsWeb website."""
482 def __init__(self):
483 """Construct the handler."""
484 html.parser.HTMLParser.__init__(self)
485
486 self._notams = []
487 self._currentNOTAM = ""
488 self._stage = 0
489
490 def handle_starttag(self, name, attrs):
491 """Start an element."""
492 if (self._stage==0 and name=="div" and ("id", "notamRight") in attrs) or \
493 (self._stage==1 and name=="span") or \
494 (self._stage==2 and name=="pre"):
495 self._stage += 1
496 if self._stage==1:
497 self._currentNOTAM = ""
498
499 def handle_data(self, content):
500 """Handle characters"""
501 if self._stage==3:
502 self._currentNOTAM += content
503
504 def handle_endtag(self, name):
505 """End an element."""
506 if (self._stage==3 and name=="pre") or \
507 (self._stage==2 and name=="span") or \
508 (self._stage==1 and name=="div"):
509 self._stage -= 1
510 if self._stage==0:
511 self._processCurrentNOTAM()
512
513 def getNOTAMs(self):
514 """Get the NOTAMs collected"""
515 return self._notams
516
517 def _processCurrentNOTAM(self):
518 """Parse the current NOTAM and append its contents to the list of
519 NOTAMS."""
520 notam = None
521 try:
522 notam = self._parseCurrentNOTAM2()
523 except Exception as e:
524 print("Error parsing current NOTAM: " + str(e))
525
526 if notam is None:
527 print("Could not parse NOTAM: " + self._currentNOTAM)
528 if self._currentNOTAM:
529 self._notams.append(self._currentNOTAM + "\n")
530 else:
531 self._notams.append(notam)
532
533 def _parseCurrentNOTAM(self):
534 """Parse the current NOTAM, if possible, and return a NOTAM object."""
535 lines = self._currentNOTAM.splitlines()
536 lines = [line.strip() for line in lines]
537
538 if len(lines)<4:
539 return None
540
541 if not lines[1].startswith("Q)") or \
542 not lines[2].startswith("A)") or \
543 not (lines[3].startswith("E)") or
544 (lines[3].startswith("D)") and lines[4].startswith("E)"))):
545 return None
546
547 ident = lines[0].split()[0]
548 basic = lines[1][2:].strip()
549
550 words = lines[2].split()
551 if len(words)<4 or words[0]!="A)" or words[2]!="B)":
552 return None
553
554 begin = datetime.datetime.strptime(words[3], "%y%m%d%H%M")
555 end = None
556 permanent = False
557 if words[4]=="C)" and len(words)>=6:
558 if words[5] in ["PERM", "UFN"]:
559 permanent = True
560 else:
561 end = datetime.datetime.strptime(words[5], "%y%m%d%H%M")
562 else:
563 permanent = True
564
565 repeatCycle = None
566 noticeStartIndex = 3
567 if lines[3].startswith("D)"):
568 repeatCycle = lines[3][2:].strip()
569 noticeStartIndex = 4
570
571 notice = ""
572 for index in range(noticeStartIndex, len(lines)):
573 line = lines[index][2:] if index==noticeStartIndex else lines[index]
574 line = line.strip()
575
576 if line.lower().startswith("created:") or \
577 line.lower().startswith("source:"):
578 break
579
580 if notice: notice += " "
581 notice += line
582
583 return NOTAM(ident, basic, begin, notice, end = end,
584 permanent = permanent, repeatCycle = repeatCycle)
585
586 def _parseCurrentNOTAM2(self):
587 """Parse the current NOTAM with a second, more flexible method."""
588 self._currentNOTAM = self._currentNOTAM.replace("\\n", "\n")
589 lines = self._currentNOTAM.splitlines()
590 if len(lines)==1:
591 lines = lines[0].splitlines()
592 lines = [line.strip() for line in lines]
593
594 if not lines:
595 return None
596
597 ident = lines[0].split()[0]
598
599 lines = lines[1:]
600 for i in range(0, 2):
601 l = lines[-1].lower()
602 if l.startswith("created:") or l.startswith("source:"):
603 lines = lines[:-1]
604
605 lines = [line.strip() for line in lines]
606 contents = " ".join(lines).split()
607
608 items = {}
609 for i in ["Q)", "A)", "B)", "C)", "D)", "E)"]:
610 items[i] = ""
611
612 currentItem = None
613 for word in contents:
614 if word in items:
615 currentItem = word
616 elif currentItem in items:
617 s = items[currentItem]
618 if s: s+= " "
619 s += word
620 items[currentItem] = s
621
622 if not items["Q)"] or not items["A)"] or not items["B)"] or \
623 not items["E)"]:
624 return None
625
626 def parseTime(item):
627 item = re.sub("([0-9]+).*", "\\1", item)
628 try:
629 return datetime.datetime.strptime(item, "%y%m%d%H%M")
630 except ValueError:
631 return datetime.datetime.strptime(item, "%Y%m%d%H%M")
632
633 basic = items["Q)"]
634 begin = parseTime(items["B)"])
635
636 end = None
637 permanent = False
638 if items["C)"]:
639 endItem = items["C)"]
640 if endItem in ["PERM", "UFN"]:
641 permanent = True
642 else:
643 end = parseTime(items["C)"])
644 else:
645 permanent = True
646
647 repeatCycle = None
648 if items["D)"]:
649 repeatCycle = items["D)"]
650
651 notice = items["E)"]
652
653 return NOTAM(ident, basic, begin, notice, end = end,
654 permanent = permanent, repeatCycle = repeatCycle)
655
656#------------------------------------------------------------------------------
657
658class Result(object):
659 """A result object.
660
661 An instance of this filled with the appropriate data is passed to the
662 callback function on each request."""
663
664 def __repr__(self):
665 """Get a representation of the result."""
666 s = "<Result:"
667 for (key, value) in self.__dict__.items():
668 s += " " + key + "=" + str(value)
669 s += ">"
670 return s
671
672#------------------------------------------------------------------------------
673
674class Request(object):
675 """Base class for requests.
676
677 It handles any exceptions and the calling of the callback.
678
679 If an exception occurs during processing, the callback is called with
680 the two parameters: a boolean value of False, and the exception object.
681
682 If no exception occurs, the callback is called with True and the return
683 value of the run() function.
684
685 If the callback function throws an exception, that is caught and logged
686 to the debug log."""
687 def __init__(self, callback):
688 """Construct the request."""
689 self._callback = callback
690
691 def perform(self):
692 """Perform the request.
693
694 The object's run() function is called. If it throws an exception,
695 the callback is called with False, and the exception. Otherwise the
696 callback is called with True and the return value of the run()
697 function. Any exceptions thrown by the callback are caught and
698 reported."""
699 try:
700 result = self.run()
701 returned = True
702 except Exception as e:
703 traceback.print_exc()
704 result = e
705 returned = False
706
707 try:
708 self._callback(returned, result)
709 except Exception as e:
710 print("web.Handler.Request.perform: callback throwed an exception: " + util.utf2unicode(str(e)), file=sys.stderr)
711 #traceback.print_exc()
712
713#------------------------------------------------------------------------------
714
715class RPCRequest(Request):
716 """Common base class for RPC requests.
717
718 It stores the RPC client received from the handler."""
719 def __init__(self, client, callback):
720 """Construct the request."""
721 super(RPCRequest, self).__init__(callback)
722 self._client = client
723
724#------------------------------------------------------------------------------
725
726class Register(RPCRequest):
727 """A registration request."""
728 def __init__(self, client, callback, registrationData):
729 """Construct the request."""
730 super(Register, self).__init__(client, callback)
731 self._registrationData = registrationData
732
733 def run(self):
734 """Perform the registration."""
735
736 registrationData = self._registrationData
737
738 (resultCode, pilotID) = self._client.register(registrationData)
739 result = Result()
740 result.registered = resultCode==rpc.Client.RESULT_OK
741 if result.registered:
742 result.pilotID = pilotID
743
744 self._client.setCredentials(pilotID, registrationData.password)
745 LoginRPC.setupLoginResult(result, self._client, pilotID,
746 registrationData.password)
747
748 result.invalidData = \
749 resultCode==rpc.Client.RESULT_INVALID_DATA
750 result.emailAlreadyRegistered = \
751 resultCode==rpc.Client.RESULT_EMAIL_ALREADY_REGISTERED
752
753 return result
754
755#------------------------------------------------------------------------------
756
757class Login(Request):
758 """A login request."""
759 iso88592decoder = codecs.getdecoder("iso-8859-2")
760
761 def __init__(self, callback, pilotID, password):
762 """Construct the login request with the given pilot ID and
763 password."""
764 super(Login, self).__init__(callback)
765
766 self._pilotID = pilotID
767 self._password = password
768
769 def run(self):
770 """Perform the login request."""
771 md5 = hashlib.md5()
772 md5.update(self._pilotID)
773 pilotID = md5.hexdigest()
774
775 md5 = hashlib.md5()
776 md5.update(self._password)
777 password = md5.hexdigest()
778
779 url = MAVA_BASE_URL + "/leker2.php?pid=%s&psw=%s" % (pilotID, password)
780
781 result = Result()
782
783 f = urllib.request.urlopen(url, timeout = 10.0)
784
785 status = readline(f)
786 result.loggedIn = status == ".OK."
787
788 if result.loggedIn:
789 result.pilotID = self._pilotID
790 result.password = self._password
791 result.rank = "FO"
792 result.flights = []
793
794 result.pilotName = self.iso88592decoder(readline(f))[0]
795 result.exams = readline(f)
796
797 while True:
798 line = readline(f)
799 if not line or line == "#ENDPIREP": break
800
801 flight = BookedFlight(line)
802 flight.readFromWeb(f)
803 result.flights.append(flight)
804
805 result.flights.sort(key = lambda flight: flight.departureTime)
806
807 f.close()
808
809 return result
810
811#------------------------------------------------------------------------------
812
813class LoginRPC(RPCRequest):
814 """An RPC-based login request."""
815 @staticmethod
816 def setupLoginResult(result, client, pilotID, password):
817 """Setup the login result with the given client, pilot ID and
818 password."""
819 loginResult = client.login()
820 result.loggedIn = loginResult is not None
821 if result.loggedIn:
822 result.pilotID = pilotID
823 result.pilotName = loginResult[0]
824 result.rank = loginResult[1]
825 result.types = loginResult[2]
826 result.password = password
827 flights = client.getFlights()
828 result.flights = flights[0]
829 result.reportedFlights = flights[1]
830 result.rejectedFlights = flights[2]
831 if result.rank=="STU":
832 reply = client.getEntryExamStatus()
833 result.entryExamPassed = reply[0]
834 result.entryExamLink = reply[1]
835 result.checkFlightStatus = reply[2]
836 if reply[3]:
837 result.rank = "FO"
838
839
840 def __init__(self, client, callback, pilotID, password):
841 """Construct the login request with the given pilot ID and
842 password."""
843 super(LoginRPC, self).__init__(client, callback)
844
845 self._pilotID = pilotID
846 self._password = password
847
848 def run(self):
849 """Perform the login request."""
850 result = Result()
851
852 self._client.setCredentials(self._pilotID, self._password)
853 LoginRPC.setupLoginResult(result, self._client,
854 self._pilotID, self._password)
855
856 return result
857
858#------------------------------------------------------------------------------
859
860class GetEntryExamStatus(RPCRequest):
861 """A request to get the entry exam status."""
862 def __init__(self, client, callback):
863 """Construct the request."""
864 super(GetEntryExamStatus, self).__init__(client, callback)
865
866 def run(self):
867 """Perform the query."""
868 result = Result()
869
870 reply = self._client.getEntryExamStatus()
871
872 result.entryExamPassed = reply[0]
873 result.entryExamLink = reply[1]
874 result.checkFlightStatus = reply[2]
875 result.madeFO = reply[3]
876
877 return result
878
879#------------------------------------------------------------------------------
880
881class GetFleet(Request):
882 """Request to get the fleet from the website."""
883
884 def __init__(self, callback):
885 """Construct the fleet request."""
886 super(GetFleet, self).__init__(callback)
887
888 def run(self):
889 """Perform the login request."""
890 url = MAVA_BASE_URL + "/onlinegates_get.php"
891
892 f = urllib.request.urlopen(url, timeout = 10.0)
893 result = Result()
894 result.fleet = Fleet(f)
895 f.close()
896
897 return result
898
899#------------------------------------------------------------------------------
900
901class GetFleetRPC(RPCRequest):
902 """Request to get the fleet from the website using RPC."""
903 def __init__(self, client, callback):
904 """Construct the request with the given client and callback function."""
905 super(GetFleetRPC, self).__init__(client, callback)
906
907 def run(self):
908 """Perform the login request."""
909 result = Result()
910
911 result.fleet = self._client.getFleet()
912
913 return result
914
915#------------------------------------------------------------------------------
916
917class UpdatePlane(Request):
918 """Update the status of one of the planes in the fleet."""
919 def __init__(self, callback, tailNumber, status, gateNumber = None):
920 """Construct the request."""
921 super(UpdatePlane, self).__init__(callback)
922 self._tailNumber = tailNumber
923 self._status = status
924 self._gateNumber = gateNumber
925
926 def run(self):
927 """Perform the plane update."""
928 url = MAVA_BASE_URL + "/onlinegates_set.php"
929
930 status = Plane.status2str(self._status)
931
932 gateNumber = self._gateNumber if self._gateNumber else ""
933
934 data = urllib.parse.urlencode([("lajstrom", self._tailNumber),
935 ("status", status),
936 ("kapu", gateNumber)])
937
938 f = urllib.request.urlopen(url, data, timeout = 10.0)
939 line = readline(f)
940
941 result = Result()
942 result.success = line == "OK"
943
944 return result
945
946#------------------------------------------------------------------------------
947
948class UpdatePlaneRPC(RPCRequest):
949 """RPC request to update the status and the position of a plane in the
950 fleet."""
951 def __init__(self, client, callback, tailNumber, status, gateNumber = None):
952 """Construct the request."""
953 super(UpdatePlaneRPC, self).__init__(client, callback)
954 self._tailNumber = tailNumber
955 self._status = status
956 self._gateNumber = gateNumber
957
958 def run(self):
959 """Perform the plane update."""
960 self._client.updatePlane(self._tailNumber, self._status, self._gateNumber)
961
962 # Otherwise an exception is thrown
963 result = Result()
964 result.success = True
965
966 return result
967
968#------------------------------------------------------------------------------
969
970class GetNOTAMs(Request):
971 """Get the NOTAMs from EURoutePro and select the ones we are interested
972 in."""
973 def __init__(self, callback, departureICAO, arrivalICAO):
974 """Construct the request for the given airports."""
975 super(GetNOTAMs, self).__init__(callback)
976 self._departureICAO = departureICAO
977 self._arrivalICAO = arrivalICAO
978
979 def run(self):
980 """Perform the retrieval of the NOTAMs."""
981 departureNOTAMs = self.getPilotsWebNOTAMs(self._departureICAO)
982 arrivalNOTAMs = self.getPilotsWebNOTAMs(self._arrivalICAO)
983
984 icaos = []
985 if not departureNOTAMs: icaos.append(self._departureICAO)
986 if not arrivalNOTAMs: icaos.append(self._arrivalICAO)
987
988 if icaos:
989 xmlParser = xml.sax.make_parser()
990 notamHandler = NOTAMHandler(icaos)
991 xmlParser.setContentHandler(notamHandler)
992
993 url = "http://notams.euroutepro.com/notams.xml"
994
995 f = urllib.request.urlopen(url, timeout = 10.0)
996 try:
997 xmlParser.parse(f)
998 finally:
999 f.close()
1000
1001 for icao in icaos:
1002 if icao==self._departureICAO:
1003 departureNOTAMs = notamHandler.get(icao)
1004 else:
1005 arrivalNOTAMs = notamHandler.get(icao)
1006
1007 result = Result()
1008 result.departureNOTAMs = departureNOTAMs
1009 result.arrivalNOTAMs = arrivalNOTAMs
1010
1011 return result
1012
1013 def getPilotsWebNOTAMs(self, icao):
1014 """Try to get the NOTAMs from FAA's PilotsWeb site for the given ICAO
1015 code.
1016
1017 Returns a list of PilotsWEBNOTAM objects, or None in case of an error."""
1018 try:
1019 parser = PilotsWebNOTAMsParser()
1020
1021 url = "https://pilotweb.nas.faa.gov/PilotWeb/notamRetrievalByICAOAction.do?method=displayByICAOs&formatType=ICAO&retrieveLocId=%s&reportType=RAW&actionType=notamRetrievalByICAOs" % \
1022 (icao.upper(),)
1023
1024 f = urllib.request.urlopen(url, timeout = 10.0, cafile=certifi.where())
1025 try:
1026 data = f.read(16384)
1027 while data:
1028 parser.feed(str(data))
1029 data = f.read(16384)
1030 finally:
1031 f.close()
1032
1033 return parser.getNOTAMs()
1034
1035 except Exception as e:
1036 traceback.print_exc()
1037 print("mlx.web.GetNOTAMs.getPilotsWebNOTAMs: failed to get NOTAMs for '%s': %s" % \
1038 (icao, str(e)))
1039 return None
1040
1041#------------------------------------------------------------------------------
1042
1043class GetMETARs(Request):
1044 """Get the METARs from the NOAA website for certain airport ICAOs."""
1045
1046 def __init__(self, callback, airports):
1047 """Construct the request for the given airports."""
1048 super(GetMETARs, self).__init__(callback)
1049 self._airports = airports
1050
1051 def run(self):
1052 """Perform the retrieval opf the METARs."""
1053 url = "http://www.aviationweather.gov/adds/dataserver_current/httpparam?"
1054 data = urllib.parse.urlencode([ ("dataSource" , "metars"),
1055 ("requestType", "retrieve"),
1056 ("format", "csv"),
1057 ("stationString", " ".join(self._airports)),
1058 ("hoursBeforeNow", "24"),
1059 ("mostRecentForEachStation", "constraint")])
1060 result = Result()
1061 result.metars = {}
1062 try:
1063 url += data
1064 f = urllib.request.urlopen(url, timeout = 10.0, cafile = certifi.where())
1065 try:
1066 for line in f.readlines():
1067 line = str(line, "iso-8859-1")
1068 if len(line)>5 and line[4]==' ':
1069 icao = line[0:4]
1070 if icao in self._airports:
1071 result.metars[icao] = line.strip().split(",")[0]
1072 finally:
1073 f.close()
1074 except Exception as e:
1075 traceback.print_exc()
1076 print("mlx.web.GetMETARs.run: failed to get METARs for %s: %s" % \
1077 (self._airports, str(e)))
1078
1079 return result
1080
1081#------------------------------------------------------------------------------
1082
1083class SendPIREP(Request):
1084 """A request to send a PIREP to the MAVA website."""
1085 _latin2Encoder = codecs.getencoder("iso-8859-2")
1086
1087 def __init__(self, callback, pirep):
1088 """Construct the sending of the PIREP."""
1089 super(SendPIREP, self).__init__(callback)
1090 self._pirep = pirep
1091
1092 def run(self):
1093 """Perform the sending of the PIREP."""
1094 url = MAVA_BASE_URL + "/malevacars.php"
1095
1096 pirep = self._pirep
1097
1098 data = {}
1099 data["acarsdata"] = SendPIREP._latin2Encoder(pirep.getACARSText())[0]
1100
1101 bookedFlight = pirep.bookedFlight
1102 data["foglalas_id"] = bookedFlight.id
1103 data["repdate"] = bookedFlight.departureTime.date().strftime("%Y-%m-%d")
1104 data["fltnum"] = bookedFlight.callsign
1105 data["depap"] = bookedFlight.departureICAO
1106 data["arrap"] = bookedFlight.arrivalICAO
1107 data["pass"] = str(pirep.numPassengers)
1108 data["crew"] = str(pirep.numCrew)
1109 data["cargo"] = str(pirep.cargoWeight)
1110 data["bag"] = str(pirep.bagWeight)
1111 data["mail"] = str(pirep.mailWeight)
1112
1113 data["flttype"] = pirep.flightTypeText
1114 data["onoff"] = "1" if pirep.online else "0"
1115 data["bt_dep"] = util.getTimestampString(pirep.blockTimeStart)
1116 data["bt_arr"] = util.getTimestampString(pirep.blockTimeEnd)
1117 data["bt_dur"] = util.getTimeIntervalString(pirep.blockTimeEnd -
1118 pirep.blockTimeStart)
1119 data["ft_dep"] = util.getTimestampString(pirep.flightTimeStart)
1120 data["ft_arr"] = util.getTimestampString(pirep.flightTimeEnd)
1121 data["ft_dur"] = util.getTimeIntervalString(pirep.flightTimeEnd -
1122 pirep.flightTimeStart)
1123 data["timecomm"] = pirep.getTimeComment()
1124 data["fuel"] = "%.2f" % (pirep.fuelUsed,)
1125 data["dep_rwy"] = pirep.departureRunway
1126 data["arr_rwy"] = pirep.arrivalRunway
1127 data["wea_dep"] = pirep.departureMETAR
1128 data["wea_arr"] = pirep.arrivalMETAR
1129 data["alt"] = "FL%.0f" % (pirep.filedCruiseAltitude/100.0,)
1130 if pirep.filedCruiseAltitude!=pirep.cruiseAltitude:
1131 data["mod_alt"] = "FL%.0f" % (pirep.cruiseAltitude/100.0,)
1132 else:
1133 data["mod_alt"] = ""
1134 data["sid"] = pirep.sid
1135 data["navroute"] = pirep.route
1136 data["star"] = pirep.getSTAR()
1137 data["aprtype"] = pirep.approachType
1138 data["diff"] = "2"
1139 data["comment"] = SendPIREP._latin2Encoder(pirep.comments)[0]
1140 data["flightdefect"] = SendPIREP._latin2Encoder(pirep.flightDefects)[0]
1141 data["kritika"] = pirep.getRatingText()
1142 data["flightrating"] = "%.1f" % (max(0.0, pirep.rating),)
1143 data["distance"] = "%.3f" % (pirep.flownDistance,)
1144 data["insdate"] = datetime.date.today().strftime("%Y-%m-%d")
1145
1146 postData = urllib.parse.urlencode(data)
1147 f = urllib.request.urlopen(url, postData, timeout = 10.0)
1148 try:
1149 result = Result()
1150 line = f.readline().strip()
1151 print("PIREP result from website:", line)
1152 result.success = line=="OK"
1153 result.alreadyFlown = line=="MARVOLT"
1154 result.notAvailable = line=="NOMORE"
1155 finally:
1156 f.close()
1157
1158 return result
1159#------------------------------------------------------------------------------
1160
1161class SendPIREPRPC(RPCRequest):
1162 """A request to send a PIREP to the MAVA website via the RPC interface."""
1163
1164 def __init__(self, client, callback, pirep, update):
1165 """Construct the sending of the PIREP."""
1166 super(SendPIREPRPC, self).__init__(client, callback)
1167 self._pirep = pirep
1168 self._update = update
1169
1170 def run(self):
1171 """Perform the sending of the PIREP."""
1172 pirep = self._pirep
1173 resultCode = self._client.addPIREP(pirep.bookedFlight.id, pirep,
1174 self._update)
1175
1176 result = Result()
1177 result.success = resultCode==rpc.Client.RESULT_OK
1178 result.alreadyFlown = resultCode==rpc.Client.RESULT_FLIGHT_ALREADY_REPORTED
1179 result.notAvailable = resultCode==rpc.Client.RESULT_FLIGHT_NOT_EXISTS
1180
1181 return result
1182
1183#------------------------------------------------------------------------------
1184
1185class SendACARS(Request):
1186 """A request to send an ACARS to the MAVA website."""
1187 _latin2Encoder = codecs.getencoder("iso-8859-2")
1188
1189 def __init__(self, callback, acars):
1190 """Construct the request for the given PIREP."""
1191 super(SendACARS, self).__init__(callback)
1192 self._acars = acars
1193
1194 def run(self):
1195 """Perform the sending of the ACARS."""
1196 print("Sending the online ACARS")
1197
1198 url = MAVA_BASE_URL + "/acars2/acarsonline.php"
1199
1200 acars = self._acars
1201 bookedFlight = acars.bookedFlight
1202
1203 data = {}
1204 data["pid"] = acars.pid
1205 data["pilot"] = SendACARS._latin2Encoder(acars.pilotName)[0]
1206
1207 data["pass"] = str(bookedFlight.numPassengers)
1208 data["callsign"] = bookedFlight.callsign
1209 data["airplane"] = bookedFlight.aircraftTypeName
1210 data["from"] = bookedFlight.departureICAO
1211 data["to"] = bookedFlight.arrivalICAO
1212 data["lajstrom"] = bookedFlight.tailNumber
1213
1214 data["block_time"] = acars.getBlockTimeText()
1215 data["longitude"] = str(acars.state.longitude)
1216 data["latitude"] = str(acars.state.latitude)
1217 data["altitude"] = str(acars.state.altitude)
1218 data["speed"] = str(acars.state.groundSpeed)
1219
1220 data["event"] = acars.getEventText()
1221
1222 f = urllib.request.urlopen(url, urllib.parse.urlencode(data), timeout = 10.0)
1223 try:
1224 result = Result()
1225 finally:
1226 f.close()
1227
1228 return result
1229
1230#------------------------------------------------------------------------------
1231
1232class SendACARSRPC(RPCRequest):
1233 """A request to send an ACARS to the MAVA website via JSON-RPC."""
1234 def __init__(self, client, callback, acars):
1235 """Construct the request for the given PIREP."""
1236 super(SendACARSRPC, self).__init__(client, callback)
1237 self._acars = acars
1238
1239 def run(self):
1240 """Perform the sending of the ACARS."""
1241 print("Sending the online ACARS via JSON-RPC")
1242
1243 self._client.updateOnlineACARS(self._acars)
1244 return Result()
1245
1246#------------------------------------------------------------------------------
1247
1248class SendBugReport(Request):
1249 """A request to send a bug report to the project homepage."""
1250 _latin2Encoder = codecs.getencoder("iso-8859-2")
1251
1252 def __init__(self, callback, summary, description, email):
1253 """Construct the request for the given bug report."""
1254 super(SendBugReport, self).__init__(callback)
1255 self._summary = summary
1256 self._description = description
1257 self._email = email
1258
1259 def run(self):
1260 """Perform the sending of the bug report."""
1261 serverProxy = xmlrpc.client.ServerProxy("http://mlx.varadiistvan.hu/rpc")
1262
1263 result = Result()
1264 result.success = False
1265
1266 attributes = {}
1267 if self._email:
1268 attributes["reporter"] = self._email
1269
1270 result.ticketID = serverProxy.ticket.create(self._summary, self._description,
1271 attributes, True)
1272 print("Created ticket with ID:", result.ticketID)
1273 result.success = True
1274
1275 return result
1276
1277#------------------------------------------------------------------------------
1278
1279class SetCheckFlightPassed(RPCRequest):
1280 """A request to mark the user as one having passed the check flight."""
1281 def __init__(self, client, callback, aircraftType):
1282 """Construct the request for the given type."""
1283 super(SetCheckFlightPassed, self).__init__(client, callback)
1284 self._aircraftType = aircraftType
1285
1286 def run(self):
1287 """Perform the update."""
1288 aircraftType = BookedFlight.TYPE2TYPECODE[self._aircraftType]
1289 self._client.setCheckFlightPassed(aircraftType)
1290 return Result()
1291
1292#------------------------------------------------------------------------------
1293
1294class GetPIREP(RPCRequest):
1295 """A request to retrieve the PIREP of a certain flight."""
1296 def __init__(self, client, callback, flightID):
1297 """Construct the request."""
1298 super(GetPIREP, self).__init__(client, callback)
1299 self._flightID = flightID
1300
1301 def run(self):
1302 """Perform the update."""
1303 result = Result()
1304
1305 pirepData = self._client.getPIREP(self._flightID)
1306 print("pirepData:", pirepData)
1307
1308 bookedFlight = BookedFlight(self._flightID)
1309 bookedFlight.setupFromPIREPData(pirepData)
1310
1311 result.pirep = PIREP(None)
1312 result.pirep.setupFromPIREPData(pirepData, bookedFlight)
1313
1314 return result
1315
1316#------------------------------------------------------------------------------
1317
1318class ReflyFlights(RPCRequest):
1319 """A request to mark certain flights for reflying."""
1320 def __init__(self, client, callback, flightIDs):
1321 """Construct the request."""
1322 super(ReflyFlights, self).__init__(client, callback)
1323 self._flightIDs = flightIDs
1324
1325 def run(self):
1326 """Perform the update."""
1327 self._client.reflyFlights(self._flightIDs)
1328 return Result()
1329
1330#------------------------------------------------------------------------------
1331
1332class DeleteFlights(RPCRequest):
1333 """A request to delete certain flights."""
1334 def __init__(self, client, callback, flightIDs):
1335 """Construct the request."""
1336 super(DeleteFlights, self).__init__(client, callback)
1337 self._flightIDs = flightIDs
1338
1339 def run(self):
1340 """Perform the update."""
1341 self._client.deleteFlights(self._flightIDs)
1342 return Result()
1343
1344#------------------------------------------------------------------------------
1345
1346class GetAcceptedFlights(RPCRequest):
1347 """Request to get the accepted flights."""
1348 def __init__(self, client, callback):
1349 """Construct the request with the given client and callback function."""
1350 super(GetAcceptedFlights, self).__init__(client, callback)
1351
1352 def run(self):
1353 """Perform the login request."""
1354 result = Result()
1355
1356 result.flights = self._client.getAcceptedFlights()
1357
1358 return result
1359
1360#------------------------------------------------------------------------------
1361
1362class GetTimetable(RPCRequest):
1363 """Request to get the timetable."""
1364 def __init__(self, client, callback, date, types):
1365 """Construct the request with the given client and callback function."""
1366 super(GetTimetable, self).__init__(client, callback)
1367 self._date = date
1368 self._types = types
1369
1370 def run(self):
1371 """Perform the login request."""
1372 result = Result()
1373
1374 result.flightPairs = self._client.getTimetable(self._date, self._types)
1375
1376 return result
1377
1378#------------------------------------------------------------------------------
1379
1380class BookFlights(RPCRequest):
1381 """Request to book flights."""
1382 def __init__(self, client, callback, flightIDs, date, tailNumber):
1383 """Construct the request with the given client and callback function."""
1384 super(BookFlights, self).__init__(client, callback)
1385 self._flightIDs = flightIDs
1386 self._date = date
1387 self._tailNumber = tailNumber
1388
1389 def run(self):
1390 """Perform the login request."""
1391 result = Result()
1392
1393 result.bookedFlights = self._client.bookFlights(self._flightIDs,
1394 self._date,
1395 self._tailNumber)
1396
1397 return result
1398
1399#------------------------------------------------------------------------------
1400
1401class Handler(threading.Thread):
1402 """The handler for the web services.
1403
1404 It can process one request at a time. The results are passed to a callback
1405 function."""
1406 def __init__(self, config, getCredentialsFn):
1407 """Construct the handler."""
1408 super(Handler, self).__init__()
1409
1410 self._requests = []
1411 self._requestCondition = threading.Condition()
1412
1413 self.daemon = True
1414 self._config = config
1415 self._rpcClient = rpc.Client(getCredentialsFn)
1416 if config.rememberPassword:
1417 self._rpcClient.setCredentials(config.pilotID, config.password)
1418
1419 def register(self, callback, registrationData):
1420 """Enqueue a registration request."""
1421 self._addRequest(Register(self._rpcClient, callback, registrationData))
1422
1423 def login(self, callback, pilotID, password):
1424 """Enqueue a login request."""
1425 request = \
1426 LoginRPC(self._rpcClient, callback, pilotID, password) \
1427 if self._config.useRPC else Login(callback, pilotID, password)
1428
1429 self._addRequest(request)
1430
1431 def getEntryExamStatus(self, callback):
1432 """Get the entry exam status."""
1433 self._addRequest(GetEntryExamStatus(self._rpcClient, callback))
1434
1435 def getFleet(self, callback):
1436 """Enqueue a fleet retrieval request."""
1437 request = \
1438 GetFleetRPC(self._rpcClient, callback,) if self._config.useRPC \
1439 else GetFleet(callback)
1440 self._addRequest(request)
1441
1442 def updatePlane(self, callback, tailNumber, status, gateNumber = None):
1443 """Update the status of the given plane."""
1444 request = \
1445 UpdatePlaneRPC(self._rpcClient, callback,
1446 tailNumber, status, gateNumber) \
1447 if self._config.useRPC \
1448 else UpdatePlane(callback, tailNumber, status, gateNumber)
1449 self._addRequest(request)
1450
1451 def getNOTAMs(self, callback, departureICAO, arrivalICAO):
1452 """Get the NOTAMs for the given two airports."""
1453 self._addRequest(GetNOTAMs(callback, departureICAO, arrivalICAO))
1454
1455 def getMETARs(self, callback, airports):
1456 """Get the METARs for the given airports."""
1457 self._addRequest(GetMETARs(callback, airports))
1458
1459 def sendPIREP(self, callback, pirep, update = False):
1460 """Send the given PIREP."""
1461 request = \
1462 SendPIREPRPC(self._rpcClient, callback, pirep, update) \
1463 if self._config.useRPC else SendPIREP(callback, pirep)
1464 self._addRequest(request)
1465
1466 def sendACARS(self, callback, acars):
1467 """Send the given ACARS"""
1468 request = \
1469 SendACARSRPC(self._rpcClient, callback, acars) if self._config.useRPC \
1470 else SendACARS(callback, acars)
1471 self._addRequest(request)
1472
1473 def sendBugReport(self, callback, summary, description, email):
1474 """Send a bug report with the given data."""
1475 self._addRequest(SendBugReport(callback, summary, description, email))
1476
1477 def setCheckFlightPassed(self, callback, aircraftType):
1478 """Mark the check flight as passed."""
1479 self._addRequest(SetCheckFlightPassed(self._rpcClient,
1480 callback, aircraftType))
1481
1482 def getPIREP(self, callback, flightID):
1483 """Query the PIREP for the given flight."""
1484 self._addRequest(GetPIREP(self._rpcClient, callback, flightID))
1485
1486 def reflyFlights(self, callback, flightIDs):
1487 """Mark the flights with the given IDs for reflying."""
1488 self._addRequest(ReflyFlights(self._rpcClient, callback, flightIDs))
1489
1490 def deleteFlights(self, callback, flightIDs):
1491 """Delete the flights with the given IDs."""
1492 self._addRequest(DeleteFlights(self._rpcClient, callback, flightIDs))
1493
1494 def getAcceptedFlights(self, callback):
1495 """Enqueue a request to get the accepted flights."""
1496 self._addRequest(GetAcceptedFlights(self._rpcClient, callback))
1497
1498 def getTimetable(self, callback, date, types):
1499 """Enqueue a request to get the timetable."""
1500 self._addRequest(GetTimetable(self._rpcClient, callback, date, types))
1501
1502 def bookFlights(self, callback, flightIDs, date, tailNumber):
1503 """Enqueue a request to book some flights."""
1504 self._addRequest(BookFlights(self._rpcClient, callback,
1505 flightIDs, date, tailNumber))
1506
1507 def run(self):
1508 """Process the requests."""
1509 while True:
1510 with self._requestCondition:
1511 while not self._requests:
1512 self._requestCondition.wait()
1513 request = self._requests[0]
1514 del self._requests[0]
1515
1516 request.perform()
1517
1518 def _addRequest(self, request):
1519 """Add the given request to the queue."""
1520 with self._requestCondition:
1521 self._requests.append(request)
1522 self._requestCondition.notify()
1523
1524#------------------------------------------------------------------------------
1525
1526if __name__ == "__main__":
1527 import time
1528
1529 def callback(returned, result):
1530 print(returned, str(result))
1531
1532 handler = Handler()
1533 handler.start()
1534
1535 #handler.login(callback, "P096", "V5fwj")
1536 #handler.getFleet(callback)
1537 # Plane: HA-LEG home (gate 67)
1538 #handler.updatePlane(callback, "HA-LQC", const.PLANE_AWAY, "72")
1539 #time.sleep(3)
1540 #handler.getFleet(callback)
1541 #time.sleep(3)
1542
1543 #handler.getNOTAMs(callback, "LHBP", "EPWA")
1544 #handler.getMETARs(callback, ["LHBP", "EPWA"])
1545 #time.sleep(5)
1546
1547 handler.updatePlane(callback, "HA-LON", const.PLANE_AWAY, "")
1548 time.sleep(3)
1549
1550#------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.