source: src/mlx/web.py@ 929:9586f81f7870

python3
Last change on this file since 929:9586f81f7870 was 928:3b16c095d166, checked in by István Váradi <ivaradi@…>, 5 years ago

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