source: src/mlx/web.py@ 923:fe2a902681bc

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

Eliminated calls to cmp() (re #347).

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