source: src/mlx/web.py@ 962:a0b8491a5a12

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

Use the certificates for HTTPS access (re #347).

File size: 54.3 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 url += data
1055 f = urllib.request.urlopen(url, timeout = 10.0, cafile=certifi.where())
1056 try:
1057 result = Result()
1058 result.metars = {}
1059 for line in f.readlines():
1060 line = str(line, "iso-8859-1")
1061 if len(line)>5 and line[4]==' ':
1062 icao = line[0:4]
1063 if icao in self._airports:
1064 result.metars[icao] = line.strip().split(",")[0]
1065 finally:
1066 f.close()
1067
1068 return result
1069
1070#------------------------------------------------------------------------------
1071
1072class SendPIREP(Request):
1073 """A request to send a PIREP to the MAVA website."""
1074 _latin2Encoder = codecs.getencoder("iso-8859-2")
1075
1076 def __init__(self, callback, pirep):
1077 """Construct the sending of the PIREP."""
1078 super(SendPIREP, self).__init__(callback)
1079 self._pirep = pirep
1080
1081 def run(self):
1082 """Perform the sending of the PIREP."""
1083 url = MAVA_BASE_URL + "/malevacars.php"
1084
1085 pirep = self._pirep
1086
1087 data = {}
1088 data["acarsdata"] = SendPIREP._latin2Encoder(pirep.getACARSText())[0]
1089
1090 bookedFlight = pirep.bookedFlight
1091 data["foglalas_id"] = bookedFlight.id
1092 data["repdate"] = bookedFlight.departureTime.date().strftime("%Y-%m-%d")
1093 data["fltnum"] = bookedFlight.callsign
1094 data["depap"] = bookedFlight.departureICAO
1095 data["arrap"] = bookedFlight.arrivalICAO
1096 data["pass"] = str(pirep.numPassengers)
1097 data["crew"] = str(pirep.numCrew)
1098 data["cargo"] = str(pirep.cargoWeight)
1099 data["bag"] = str(pirep.bagWeight)
1100 data["mail"] = str(pirep.mailWeight)
1101
1102 data["flttype"] = pirep.flightTypeText
1103 data["onoff"] = "1" if pirep.online else "0"
1104 data["bt_dep"] = util.getTimestampString(pirep.blockTimeStart)
1105 data["bt_arr"] = util.getTimestampString(pirep.blockTimeEnd)
1106 data["bt_dur"] = util.getTimeIntervalString(pirep.blockTimeEnd -
1107 pirep.blockTimeStart)
1108 data["ft_dep"] = util.getTimestampString(pirep.flightTimeStart)
1109 data["ft_arr"] = util.getTimestampString(pirep.flightTimeEnd)
1110 data["ft_dur"] = util.getTimeIntervalString(pirep.flightTimeEnd -
1111 pirep.flightTimeStart)
1112 data["timecomm"] = pirep.getTimeComment()
1113 data["fuel"] = "%.2f" % (pirep.fuelUsed,)
1114 data["dep_rwy"] = pirep.departureRunway
1115 data["arr_rwy"] = pirep.arrivalRunway
1116 data["wea_dep"] = pirep.departureMETAR
1117 data["wea_arr"] = pirep.arrivalMETAR
1118 data["alt"] = "FL%.0f" % (pirep.filedCruiseAltitude/100.0,)
1119 if pirep.filedCruiseAltitude!=pirep.cruiseAltitude:
1120 data["mod_alt"] = "FL%.0f" % (pirep.cruiseAltitude/100.0,)
1121 else:
1122 data["mod_alt"] = ""
1123 data["sid"] = pirep.sid
1124 data["navroute"] = pirep.route
1125 data["star"] = pirep.getSTAR()
1126 data["aprtype"] = pirep.approachType
1127 data["diff"] = "2"
1128 data["comment"] = SendPIREP._latin2Encoder(pirep.comments)[0]
1129 data["flightdefect"] = SendPIREP._latin2Encoder(pirep.flightDefects)[0]
1130 data["kritika"] = pirep.getRatingText()
1131 data["flightrating"] = "%.1f" % (max(0.0, pirep.rating),)
1132 data["distance"] = "%.3f" % (pirep.flownDistance,)
1133 data["insdate"] = datetime.date.today().strftime("%Y-%m-%d")
1134
1135 postData = urllib.parse.urlencode(data)
1136 f = urllib.request.urlopen(url, postData, timeout = 10.0)
1137 try:
1138 result = Result()
1139 line = f.readline().strip()
1140 print("PIREP result from website:", line)
1141 result.success = line=="OK"
1142 result.alreadyFlown = line=="MARVOLT"
1143 result.notAvailable = line=="NOMORE"
1144 finally:
1145 f.close()
1146
1147 return result
1148#------------------------------------------------------------------------------
1149
1150class SendPIREPRPC(RPCRequest):
1151 """A request to send a PIREP to the MAVA website via the RPC interface."""
1152
1153 def __init__(self, client, callback, pirep, update):
1154 """Construct the sending of the PIREP."""
1155 super(SendPIREPRPC, self).__init__(client, callback)
1156 self._pirep = pirep
1157 self._update = update
1158
1159 def run(self):
1160 """Perform the sending of the PIREP."""
1161 pirep = self._pirep
1162 resultCode = self._client.addPIREP(pirep.bookedFlight.id, pirep,
1163 self._update)
1164
1165 result = Result()
1166 result.success = resultCode==rpc.Client.RESULT_OK
1167 result.alreadyFlown = resultCode==rpc.Client.RESULT_FLIGHT_ALREADY_REPORTED
1168 result.notAvailable = resultCode==rpc.Client.RESULT_FLIGHT_NOT_EXISTS
1169
1170 return result
1171
1172#------------------------------------------------------------------------------
1173
1174class SendACARS(Request):
1175 """A request to send an ACARS to the MAVA website."""
1176 _latin2Encoder = codecs.getencoder("iso-8859-2")
1177
1178 def __init__(self, callback, acars):
1179 """Construct the request for the given PIREP."""
1180 super(SendACARS, self).__init__(callback)
1181 self._acars = acars
1182
1183 def run(self):
1184 """Perform the sending of the ACARS."""
1185 print("Sending the online ACARS")
1186
1187 url = MAVA_BASE_URL + "/acars2/acarsonline.php"
1188
1189 acars = self._acars
1190 bookedFlight = acars.bookedFlight
1191
1192 data = {}
1193 data["pid"] = acars.pid
1194 data["pilot"] = SendACARS._latin2Encoder(acars.pilotName)[0]
1195
1196 data["pass"] = str(bookedFlight.numPassengers)
1197 data["callsign"] = bookedFlight.callsign
1198 data["airplane"] = bookedFlight.aircraftTypeName
1199 data["from"] = bookedFlight.departureICAO
1200 data["to"] = bookedFlight.arrivalICAO
1201 data["lajstrom"] = bookedFlight.tailNumber
1202
1203 data["block_time"] = acars.getBlockTimeText()
1204 data["longitude"] = str(acars.state.longitude)
1205 data["latitude"] = str(acars.state.latitude)
1206 data["altitude"] = str(acars.state.altitude)
1207 data["speed"] = str(acars.state.groundSpeed)
1208
1209 data["event"] = acars.getEventText()
1210
1211 f = urllib.request.urlopen(url, urllib.parse.urlencode(data), timeout = 10.0)
1212 try:
1213 result = Result()
1214 finally:
1215 f.close()
1216
1217 return result
1218
1219#------------------------------------------------------------------------------
1220
1221class SendACARSRPC(RPCRequest):
1222 """A request to send an ACARS to the MAVA website via JSON-RPC."""
1223 def __init__(self, client, callback, acars):
1224 """Construct the request for the given PIREP."""
1225 super(SendACARSRPC, self).__init__(client, callback)
1226 self._acars = acars
1227
1228 def run(self):
1229 """Perform the sending of the ACARS."""
1230 print("Sending the online ACARS via JSON-RPC")
1231
1232 self._client.updateOnlineACARS(self._acars)
1233 return Result()
1234
1235#------------------------------------------------------------------------------
1236
1237class SendBugReport(Request):
1238 """A request to send a bug report to the project homepage."""
1239 _latin2Encoder = codecs.getencoder("iso-8859-2")
1240
1241 def __init__(self, callback, summary, description, email):
1242 """Construct the request for the given bug report."""
1243 super(SendBugReport, self).__init__(callback)
1244 self._summary = summary
1245 self._description = description
1246 self._email = email
1247
1248 def run(self):
1249 """Perform the sending of the bug report."""
1250 serverProxy = xmlrpc.client.ServerProxy("http://mlx.varadiistvan.hu/rpc")
1251
1252 result = Result()
1253 result.success = False
1254
1255 attributes = {}
1256 if self._email:
1257 attributes["reporter"] = self._email
1258
1259 result.ticketID = serverProxy.ticket.create(self._summary, self._description,
1260 attributes, True)
1261 print("Created ticket with ID:", result.ticketID)
1262 result.success = True
1263
1264 return result
1265
1266#------------------------------------------------------------------------------
1267
1268class SetCheckFlightPassed(RPCRequest):
1269 """A request to mark the user as one having passed the check flight."""
1270 def __init__(self, client, callback, aircraftType):
1271 """Construct the request for the given type."""
1272 super(SetCheckFlightPassed, self).__init__(client, callback)
1273 self._aircraftType = aircraftType
1274
1275 def run(self):
1276 """Perform the update."""
1277 aircraftType = BookedFlight.TYPE2TYPECODE[self._aircraftType]
1278 self._client.setCheckFlightPassed(aircraftType)
1279 return Result()
1280
1281#------------------------------------------------------------------------------
1282
1283class GetPIREP(RPCRequest):
1284 """A request to retrieve the PIREP of a certain flight."""
1285 def __init__(self, client, callback, flightID):
1286 """Construct the request."""
1287 super(GetPIREP, self).__init__(client, callback)
1288 self._flightID = flightID
1289
1290 def run(self):
1291 """Perform the update."""
1292 result = Result()
1293
1294 pirepData = self._client.getPIREP(self._flightID)
1295 print("pirepData:", pirepData)
1296
1297 bookedFlight = BookedFlight(self._flightID)
1298 bookedFlight.setupFromPIREPData(pirepData)
1299
1300 result.pirep = PIREP(None)
1301 result.pirep.setupFromPIREPData(pirepData, bookedFlight)
1302
1303 return result
1304
1305#------------------------------------------------------------------------------
1306
1307class ReflyFlights(RPCRequest):
1308 """A request to mark certain flights for reflying."""
1309 def __init__(self, client, callback, flightIDs):
1310 """Construct the request."""
1311 super(ReflyFlights, self).__init__(client, callback)
1312 self._flightIDs = flightIDs
1313
1314 def run(self):
1315 """Perform the update."""
1316 self._client.reflyFlights(self._flightIDs)
1317 return Result()
1318
1319#------------------------------------------------------------------------------
1320
1321class DeleteFlights(RPCRequest):
1322 """A request to delete certain flights."""
1323 def __init__(self, client, callback, flightIDs):
1324 """Construct the request."""
1325 super(DeleteFlights, self).__init__(client, callback)
1326 self._flightIDs = flightIDs
1327
1328 def run(self):
1329 """Perform the update."""
1330 self._client.deleteFlights(self._flightIDs)
1331 return Result()
1332
1333#------------------------------------------------------------------------------
1334
1335class GetAcceptedFlights(RPCRequest):
1336 """Request to get the accepted flights."""
1337 def __init__(self, client, callback):
1338 """Construct the request with the given client and callback function."""
1339 super(GetAcceptedFlights, self).__init__(client, callback)
1340
1341 def run(self):
1342 """Perform the login request."""
1343 result = Result()
1344
1345 result.flights = self._client.getAcceptedFlights()
1346
1347 return result
1348
1349#------------------------------------------------------------------------------
1350
1351class GetTimetable(RPCRequest):
1352 """Request to get the timetable."""
1353 def __init__(self, client, callback, date, types):
1354 """Construct the request with the given client and callback function."""
1355 super(GetTimetable, self).__init__(client, callback)
1356 self._date = date
1357 self._types = types
1358
1359 def run(self):
1360 """Perform the login request."""
1361 result = Result()
1362
1363 result.flightPairs = self._client.getTimetable(self._date, self._types)
1364
1365 return result
1366
1367#------------------------------------------------------------------------------
1368
1369class BookFlights(RPCRequest):
1370 """Request to book flights."""
1371 def __init__(self, client, callback, flightIDs, date, tailNumber):
1372 """Construct the request with the given client and callback function."""
1373 super(BookFlights, self).__init__(client, callback)
1374 self._flightIDs = flightIDs
1375 self._date = date
1376 self._tailNumber = tailNumber
1377
1378 def run(self):
1379 """Perform the login request."""
1380 result = Result()
1381
1382 result.bookedFlights = self._client.bookFlights(self._flightIDs,
1383 self._date,
1384 self._tailNumber)
1385
1386 return result
1387
1388#------------------------------------------------------------------------------
1389
1390class Handler(threading.Thread):
1391 """The handler for the web services.
1392
1393 It can process one request at a time. The results are passed to a callback
1394 function."""
1395 def __init__(self, config, getCredentialsFn):
1396 """Construct the handler."""
1397 super(Handler, self).__init__()
1398
1399 self._requests = []
1400 self._requestCondition = threading.Condition()
1401
1402 self.daemon = True
1403 self._config = config
1404 self._rpcClient = rpc.Client(getCredentialsFn)
1405 if config.rememberPassword:
1406 self._rpcClient.setCredentials(config.pilotID, config.password)
1407
1408 def register(self, callback, registrationData):
1409 """Enqueue a registration request."""
1410 self._addRequest(Register(self._rpcClient, callback, registrationData))
1411
1412 def login(self, callback, pilotID, password):
1413 """Enqueue a login request."""
1414 request = \
1415 LoginRPC(self._rpcClient, callback, pilotID, password) \
1416 if self._config.useRPC else Login(callback, pilotID, password)
1417
1418 self._addRequest(request)
1419
1420 def getEntryExamStatus(self, callback):
1421 """Get the entry exam status."""
1422 self._addRequest(GetEntryExamStatus(self._rpcClient, callback))
1423
1424 def getFleet(self, callback):
1425 """Enqueue a fleet retrieval request."""
1426 request = \
1427 GetFleetRPC(self._rpcClient, callback,) if self._config.useRPC \
1428 else GetFleet(callback)
1429 self._addRequest(request)
1430
1431 def updatePlane(self, callback, tailNumber, status, gateNumber = None):
1432 """Update the status of the given plane."""
1433 request = \
1434 UpdatePlaneRPC(self._rpcClient, callback,
1435 tailNumber, status, gateNumber) \
1436 if self._config.useRPC \
1437 else UpdatePlane(callback, tailNumber, status, gateNumber)
1438 self._addRequest(request)
1439
1440 def getNOTAMs(self, callback, departureICAO, arrivalICAO):
1441 """Get the NOTAMs for the given two airports."""
1442 self._addRequest(GetNOTAMs(callback, departureICAO, arrivalICAO))
1443
1444 def getMETARs(self, callback, airports):
1445 """Get the METARs for the given airports."""
1446 self._addRequest(GetMETARs(callback, airports))
1447
1448 def sendPIREP(self, callback, pirep, update = False):
1449 """Send the given PIREP."""
1450 request = \
1451 SendPIREPRPC(self._rpcClient, callback, pirep, update) \
1452 if self._config.useRPC else SendPIREP(callback, pirep)
1453 self._addRequest(request)
1454
1455 def sendACARS(self, callback, acars):
1456 """Send the given ACARS"""
1457 request = \
1458 SendACARSRPC(self._rpcClient, callback, acars) if self._config.useRPC \
1459 else SendACARS(callback, acars)
1460 self._addRequest(request)
1461
1462 def sendBugReport(self, callback, summary, description, email):
1463 """Send a bug report with the given data."""
1464 self._addRequest(SendBugReport(callback, summary, description, email))
1465
1466 def setCheckFlightPassed(self, callback, aircraftType):
1467 """Mark the check flight as passed."""
1468 self._addRequest(SetCheckFlightPassed(self._rpcClient,
1469 callback, aircraftType))
1470
1471 def getPIREP(self, callback, flightID):
1472 """Query the PIREP for the given flight."""
1473 self._addRequest(GetPIREP(self._rpcClient, callback, flightID))
1474
1475 def reflyFlights(self, callback, flightIDs):
1476 """Mark the flights with the given IDs for reflying."""
1477 self._addRequest(ReflyFlights(self._rpcClient, callback, flightIDs))
1478
1479 def deleteFlights(self, callback, flightIDs):
1480 """Delete the flights with the given IDs."""
1481 self._addRequest(DeleteFlights(self._rpcClient, callback, flightIDs))
1482
1483 def getAcceptedFlights(self, callback):
1484 """Enqueue a request to get the accepted flights."""
1485 self._addRequest(GetAcceptedFlights(self._rpcClient, callback))
1486
1487 def getTimetable(self, callback, date, types):
1488 """Enqueue a request to get the timetable."""
1489 self._addRequest(GetTimetable(self._rpcClient, callback, date, types))
1490
1491 def bookFlights(self, callback, flightIDs, date, tailNumber):
1492 """Enqueue a request to book some flights."""
1493 self._addRequest(BookFlights(self._rpcClient, callback,
1494 flightIDs, date, tailNumber))
1495
1496 def run(self):
1497 """Process the requests."""
1498 while True:
1499 with self._requestCondition:
1500 while not self._requests:
1501 self._requestCondition.wait()
1502 request = self._requests[0]
1503 del self._requests[0]
1504
1505 request.perform()
1506
1507 def _addRequest(self, request):
1508 """Add the given request to the queue."""
1509 with self._requestCondition:
1510 self._requests.append(request)
1511 self._requestCondition.notify()
1512
1513#------------------------------------------------------------------------------
1514
1515if __name__ == "__main__":
1516 import time
1517
1518 def callback(returned, result):
1519 print(returned, str(result))
1520
1521 handler = Handler()
1522 handler.start()
1523
1524 #handler.login(callback, "P096", "V5fwj")
1525 #handler.getFleet(callback)
1526 # Plane: HA-LEG home (gate 67)
1527 #handler.updatePlane(callback, "HA-LQC", const.PLANE_AWAY, "72")
1528 #time.sleep(3)
1529 #handler.getFleet(callback)
1530 #time.sleep(3)
1531
1532 #handler.getNOTAMs(callback, "LHBP", "EPWA")
1533 #handler.getMETARs(callback, ["LHBP", "EPWA"])
1534 #time.sleep(5)
1535
1536 handler.updatePlane(callback, "HA-LON", const.PLANE_AWAY, "")
1537 time.sleep(3)
1538
1539#------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.