source: src/mlx/web.py@ 963:35b2e35e9e07

python3
Last change on this file since 963:35b2e35e9e07 was 963:35b2e35e9e07, checked in by István Váradi <ivaradi@…>, 5 years ago

METAR downloading and processing is more robust

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