source: src/mlx/web.py@ 958:25df1de263cd

python3
Last change on this file since 958:25df1de263cd was 930:6da5e7a7ca9b, checked in by István Váradi <ivaradi@…>, 6 years ago

More robust NOTAM parsing

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