source: src/mlx/web.py@ 896:0b4cfaafdd24

Last change on this file since 896:0b4cfaafdd24 was 895:929448cde3f5, checked in by István Váradi <ivaradi@…>, 6 years ago

The Ilyushin Il-62 is supported to some degree.

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