source: src/mlx/web.py@ 1032:36f152c70cd7

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

Updated aircraft type constants (re #357).

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