source: src/mlx/web.py@ 1036:bc8c41f33fba

python3
Last change on this file since 1036:bc8c41f33fba was 1034:4836f52b49cd, checked in by István Váradi <ivaradi@…>, 3 years ago

Updated the flight type handling (re #357)

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