source: src/mlx/web.py@ 829:0c8f22f0667a

Last change on this file since 829:0c8f22f0667a was 829:0c8f22f0667a, checked in by István Váradi <ivaradi@…>, 7 years ago

A PIREP can be queried from the server (re #307)

File size: 51.2 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
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 >> f, "callsign=%s" % (self.callsign,)
293 date = self.departureTime.date()
294 print >> f, "date=%04d-%02d-%0d" % (date.year, date.month, date.day)
295 print >> f, "dep_airport=%s" % (self.departureICAO,)
296 print >> f, "dest_airport=%s" % (self.arrivalICAO,)
297 print >> f, "planecode=%s" % \
298 (BookedFlight.TYPE2TYPECODE[self.aircraftType],)
299 print >> f, "planetype=%s" % (self.aircraftTypeName,)
300 print >> f, "tail_nr=%s" % (self.tailNumber,)
301 print >> f, "passenger=%d" % (self.numPassengers,)
302 print >> f, "crew=%d" % (self.numCrew,)
303 print >> f, "bag=%d" % (self.bagWeight,)
304 print >> f, "cargo=%d" % (self.cargoWeight,)
305 print >> f, "mail=%d" % (self.mailWeight,)
306 print >> f, "flight_route=%s" % (self.route,)
307 departureTime = self.departureTime
308 print >> f, "departure_time=%02d\\:%02d\\:%02d" % \
309 (departureTime.hour, departureTime.minute, departureTime.second)
310 arrivalTime = self.arrivalTime
311 print >> f, "arrival_time=%02d\\:%02d\\:%02d" % \
312 (arrivalTime.hour, arrivalTime.minute, arrivalTime.second)
313 print >> f, "foglalas_id=%s" % ("0" if self.id is None else self.id,)
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 >> sys.stderr, "Plane string is invalid: '" + s + "'"
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(HTMLParser.HTMLParser):
473 """XML handler for the NOTAM query results on the PilotsWeb website."""
474 def __init__(self):
475 """Construct the handler."""
476 HTMLParser.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, 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 = map(lambda line: line.strip(), 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 = map(lambda line: line.strip(), 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 = map(lambda line: line.strip(), 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__.iteritems():
656 s += " " + key + "=" + unicode(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, e:
691 traceback.print_exc()
692 result = e
693 returned = False
694
695 try:
696 self._callback(returned, result)
697 except Exception, e:
698 print >> sys.stderr, "web.Handler.Request.perform: callback throwed an exception: " + util.utf2unicode(str(e))
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 = urllib2.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(cmp = lambda flight1, flight2:
794 cmp(flight1.departureTime,
795 flight2.departureTime))
796
797 f.close()
798
799 return result
800
801#------------------------------------------------------------------------------
802
803class LoginRPC(RPCRequest):
804 """An RPC-based login request."""
805 @staticmethod
806 def setupLoginResult(result, client, pilotID, password):
807 """Setup the login result with the given client, pilot ID and
808 password."""
809 loginResult = client.login()
810 result.loggedIn = loginResult is not None
811 if result.loggedIn:
812 result.pilotID = pilotID
813 result.pilotName = loginResult[0]
814 result.rank = loginResult[1]
815 result.password = password
816 flights = client.getFlights()
817 result.flights = flights[0]
818 result.reportedFlights = flights[1]
819 result.rejectedFlights = flights[2]
820 if result.rank=="STU":
821 reply = client.getEntryExamStatus()
822 result.entryExamPassed = reply[0]
823 result.entryExamLink = reply[1]
824 result.checkFlightStatus = reply[2]
825 if reply[3]:
826 result.rank = "FO"
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 = urllib2.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.urlencode([("lajstrom", self._tailNumber),
923 ("status", status),
924 ("kapu", gateNumber)])
925
926 f = urllib2.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 = urllib2.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 = urllib2.urlopen(url, timeout = 10.0)
1013 try:
1014 data = f.read(16384)
1015 while data:
1016 parser.feed(data)
1017 data = f.read(16384)
1018 finally:
1019 f.close()
1020
1021 return parser.getNOTAMs()
1022
1023 except Exception, 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.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 = urllib2.urlopen(url, timeout = 10.0)
1050 try:
1051 result = Result()
1052 result.metars = {}
1053 for line in iter(f.readline, ""):
1054 if len(line)>5 and line[4]==' ':
1055 icao = line[0:4]
1056 if icao in self._airports:
1057 result.metars[icao] = line.strip().split(",")[0]
1058 finally:
1059 f.close()
1060
1061 return result
1062
1063#------------------------------------------------------------------------------
1064
1065class SendPIREP(Request):
1066 """A request to send a PIREP to the MAVA website."""
1067 _latin2Encoder = codecs.getencoder("iso-8859-2")
1068
1069 def __init__(self, callback, pirep):
1070 """Construct the sending of the PIREP."""
1071 super(SendPIREP, self).__init__(callback)
1072 self._pirep = pirep
1073
1074 def run(self):
1075 """Perform the sending of the PIREP."""
1076 url = MAVA_BASE_URL + "/malevacars.php"
1077
1078 pirep = self._pirep
1079
1080 data = {}
1081 data["acarsdata"] = SendPIREP._latin2Encoder(pirep.getACARSText())[0]
1082
1083 bookedFlight = pirep.bookedFlight
1084 data["foglalas_id"] = bookedFlight.id
1085 data["repdate"] = bookedFlight.departureTime.date().strftime("%Y-%m-%d")
1086 data["fltnum"] = bookedFlight.callsign
1087 data["depap"] = bookedFlight.departureICAO
1088 data["arrap"] = bookedFlight.arrivalICAO
1089 data["pass"] = str(pirep.numPassengers)
1090 data["crew"] = str(pirep.numCrew)
1091 data["cargo"] = str(pirep.cargoWeight)
1092 data["bag"] = str(pirep.bagWeight)
1093 data["mail"] = str(pirep.mailWeight)
1094
1095 data["flttype"] = pirep.flightTypeText
1096 data["onoff"] = "1" if pirep.online else "0"
1097 data["bt_dep"] = util.getTimestampString(pirep.blockTimeStart)
1098 data["bt_arr"] = util.getTimestampString(pirep.blockTimeEnd)
1099 data["bt_dur"] = util.getTimeIntervalString(pirep.blockTimeEnd -
1100 pirep.blockTimeStart)
1101 data["ft_dep"] = util.getTimestampString(pirep.flightTimeStart)
1102 data["ft_arr"] = util.getTimestampString(pirep.flightTimeEnd)
1103 data["ft_dur"] = util.getTimeIntervalString(pirep.flightTimeEnd -
1104 pirep.flightTimeStart)
1105 data["timecomm"] = pirep.getTimeComment()
1106 data["fuel"] = "%.2f" % (pirep.fuelUsed,)
1107 data["dep_rwy"] = pirep.departureRunway
1108 data["arr_rwy"] = pirep.arrivalRunway
1109 data["wea_dep"] = pirep.departureMETAR
1110 data["wea_arr"] = pirep.arrivalMETAR
1111 data["alt"] = "FL%.0f" % (pirep.filedCruiseAltitude/100.0,)
1112 if pirep.filedCruiseAltitude!=pirep.cruiseAltitude:
1113 data["mod_alt"] = "FL%.0f" % (pirep.cruiseAltitude/100.0,)
1114 else:
1115 data["mod_alt"] = ""
1116 data["sid"] = pirep.sid
1117 data["navroute"] = pirep.route
1118 data["star"] = pirep.getSTAR()
1119 data["aprtype"] = pirep.approachType
1120 data["diff"] = "2"
1121 data["comment"] = SendPIREP._latin2Encoder(pirep.comments)[0]
1122 data["flightdefect"] = SendPIREP._latin2Encoder(pirep.flightDefects)[0]
1123 data["kritika"] = pirep.getRatingText()
1124 data["flightrating"] = "%.1f" % (max(0.0, pirep.rating),)
1125 data["distance"] = "%.3f" % (pirep.flownDistance,)
1126 data["insdate"] = datetime.date.today().strftime("%Y-%m-%d")
1127
1128 postData = urllib.urlencode(data)
1129 f = urllib2.urlopen(url, postData, timeout = 10.0)
1130 try:
1131 result = Result()
1132 line = f.readline().strip()
1133 print "PIREP result from website:", line
1134 result.success = line=="OK"
1135 result.alreadyFlown = line=="MARVOLT"
1136 result.notAvailable = line=="NOMORE"
1137 finally:
1138 f.close()
1139
1140 return result
1141#------------------------------------------------------------------------------
1142
1143class SendPIREPRPC(RPCRequest):
1144 """A request to send a PIREP to the MAVA website via the RPC interface."""
1145
1146 def __init__(self, client, callback, pirep):
1147 """Construct the sending of the PIREP."""
1148 super(SendPIREPRPC, self).__init__(client, callback)
1149 self._pirep = pirep
1150
1151 def run(self):
1152 """Perform the sending of the PIREP."""
1153 pirep = self._pirep
1154 resultCode = self._client.addPIREP(pirep.bookedFlight.id, pirep)
1155
1156 result = Result()
1157 result.success = resultCode==rpc.Client.RESULT_OK
1158 result.alreadyFlown = resultCode==rpc.Client.RESULT_FLIGHT_ALREADY_REPORTED
1159 result.notAvailable = resultCode==rpc.Client.RESULT_FLIGHT_NOT_EXISTS
1160
1161 return result
1162
1163#------------------------------------------------------------------------------
1164
1165class SendACARS(Request):
1166 """A request to send an ACARS to the MAVA website."""
1167 _latin2Encoder = codecs.getencoder("iso-8859-2")
1168
1169 def __init__(self, callback, acars):
1170 """Construct the request for the given PIREP."""
1171 super(SendACARS, self).__init__(callback)
1172 self._acars = acars
1173
1174 def run(self):
1175 """Perform the sending of the ACARS."""
1176 print "Sending the online ACARS"
1177
1178 url = MAVA_BASE_URL + "/acars2/acarsonline.php"
1179
1180 acars = self._acars
1181 bookedFlight = acars.bookedFlight
1182
1183 data = {}
1184 data["pid"] = acars.pid
1185 data["pilot"] = SendACARS._latin2Encoder(acars.pilotName)[0]
1186
1187 data["pass"] = str(bookedFlight.numPassengers)
1188 data["callsign"] = bookedFlight.callsign
1189 data["airplane"] = bookedFlight.aircraftTypeName
1190 data["from"] = bookedFlight.departureICAO
1191 data["to"] = bookedFlight.arrivalICAO
1192 data["lajstrom"] = bookedFlight.tailNumber
1193
1194 data["block_time"] = acars.getBlockTimeText()
1195 data["longitude"] = str(acars.state.longitude)
1196 data["latitude"] = str(acars.state.latitude)
1197 data["altitude"] = str(acars.state.altitude)
1198 data["speed"] = str(acars.state.groundSpeed)
1199
1200 data["event"] = acars.getEventText()
1201
1202 f = urllib2.urlopen(url, urllib.urlencode(data), timeout = 10.0)
1203 try:
1204 result = Result()
1205 finally:
1206 f.close()
1207
1208 return result
1209
1210#------------------------------------------------------------------------------
1211
1212class SendACARSRPC(RPCRequest):
1213 """A request to send an ACARS to the MAVA website via JSON-RPC."""
1214 def __init__(self, client, callback, acars):
1215 """Construct the request for the given PIREP."""
1216 super(SendACARSRPC, self).__init__(client, callback)
1217 self._acars = acars
1218
1219 def run(self):
1220 """Perform the sending of the ACARS."""
1221 print "Sending the online ACARS via JSON-RPC"
1222
1223 self._client.updateOnlineACARS(self._acars)
1224 return Result()
1225
1226#------------------------------------------------------------------------------
1227
1228class SendBugReport(Request):
1229 """A request to send a bug report to the project homepage."""
1230 _latin2Encoder = codecs.getencoder("iso-8859-2")
1231
1232 def __init__(self, callback, summary, description, email):
1233 """Construct the request for the given bug report."""
1234 super(SendBugReport, self).__init__(callback)
1235 self._summary = summary
1236 self._description = description
1237 self._email = email
1238
1239 def run(self):
1240 """Perform the sending of the bug report."""
1241 serverProxy = xmlrpclib.ServerProxy("http://mlx.varadiistvan.hu/rpc")
1242
1243 result = Result()
1244 result.success = False
1245
1246 attributes = {}
1247 if self._email:
1248 attributes["reporter"] = self._email
1249
1250 result.ticketID = serverProxy.ticket.create(self._summary, self._description,
1251 attributes, True)
1252 print "Created ticket with ID:", result.ticketID
1253 result.success = True
1254
1255 return result
1256
1257#------------------------------------------------------------------------------
1258
1259class SetCheckFlightPassed(RPCRequest):
1260 """A request to mark the user as one having passed the check flight."""
1261 def __init__(self, client, callback, aircraftType):
1262 """Construct the request for the given type."""
1263 super(SetCheckFlightPassed, self).__init__(client, callback)
1264 self._aircraftType = aircraftType
1265
1266 def run(self):
1267 """Perform the update."""
1268 aircraftType = BookedFlight.TYPE2TYPECODE[self._aircraftType]
1269 self._client.setCheckFlightPassed(aircraftType)
1270 return Result()
1271
1272#------------------------------------------------------------------------------
1273
1274class GetPIREP(RPCRequest):
1275 """A request to retrieve the PIREP of a certain flight."""
1276 def __init__(self, client, callback, flightID):
1277 """Construct the request."""
1278 super(GetPIREP, self).__init__(client, callback)
1279 self._flightID = flightID
1280
1281 def run(self):
1282 """Perform the update."""
1283 result = Result()
1284
1285 pirepData = self._client.getPIREP(self._flightID)
1286 print "pirepData:", pirepData
1287
1288 bookedFlight = BookedFlight()
1289 bookedFlight.setupFromPIREPData(pirepData)
1290
1291 result.pirep = PIREP(None)
1292 result.pirep.setupFromPIREPData(pirepData, bookedFlight)
1293
1294 return result
1295
1296#------------------------------------------------------------------------------
1297
1298class ReflyFlights(RPCRequest):
1299 """A request to mark certain flights for reflying."""
1300 def __init__(self, client, callback, flightIDs):
1301 """Construct the request."""
1302 super(ReflyFlights, self).__init__(client, callback)
1303 self._flightIDs = flightIDs
1304
1305 def run(self):
1306 """Perform the update."""
1307 self._client.reflyFlights(self._flightIDs)
1308 return Result()
1309
1310#------------------------------------------------------------------------------
1311
1312class DeleteFlights(RPCRequest):
1313 """A request to delete certain flights."""
1314 def __init__(self, client, callback, flightIDs):
1315 """Construct the request."""
1316 super(DeleteFlights, self).__init__(client, callback)
1317 self._flightIDs = flightIDs
1318
1319 def run(self):
1320 """Perform the update."""
1321 self._client.deleteFlights(self._flightIDs)
1322 return Result()
1323
1324#------------------------------------------------------------------------------
1325
1326class Handler(threading.Thread):
1327 """The handler for the web services.
1328
1329 It can process one request at a time. The results are passed to a callback
1330 function."""
1331 def __init__(self, config, getCredentialsFn):
1332 """Construct the handler."""
1333 super(Handler, self).__init__()
1334
1335 self._requests = []
1336 self._requestCondition = threading.Condition()
1337
1338 self.daemon = True
1339 self._config = config
1340 self._rpcClient = rpc.Client(getCredentialsFn)
1341 if config.rememberPassword:
1342 self._rpcClient.setCredentials(config.pilotID, config.password)
1343
1344 def register(self, callback, registrationData):
1345 """Enqueue a registration request."""
1346 self._addRequest(Register(self._rpcClient, callback, registrationData))
1347
1348 def login(self, callback, pilotID, password):
1349 """Enqueue a login request."""
1350 request = \
1351 LoginRPC(self._rpcClient, callback, pilotID, password) \
1352 if self._config.useRPC else Login(callback, pilotID, password)
1353
1354 self._addRequest(request)
1355
1356 def getEntryExamStatus(self, callback):
1357 """Get the entry exam status."""
1358 self._addRequest(GetEntryExamStatus(self._rpcClient, callback))
1359
1360 def getFleet(self, callback):
1361 """Enqueue a fleet retrieval request."""
1362 request = \
1363 GetFleetRPC(self._rpcClient, callback,) if self._config.useRPC \
1364 else GetFleet(callback)
1365 self._addRequest(request)
1366
1367 def updatePlane(self, callback, tailNumber, status, gateNumber = None):
1368 """Update the status of the given plane."""
1369 request = \
1370 UpdatePlaneRPC(self._rpcClient, callback,
1371 tailNumber, status, gateNumber) \
1372 if self._config.useRPC \
1373 else UpdatePlane(callback, tailNumber, status, gateNumber)
1374 self._addRequest(request)
1375
1376 def getNOTAMs(self, callback, departureICAO, arrivalICAO):
1377 """Get the NOTAMs for the given two airports."""
1378 self._addRequest(GetNOTAMs(callback, departureICAO, arrivalICAO))
1379
1380 def getMETARs(self, callback, airports):
1381 """Get the METARs for the given airports."""
1382 self._addRequest(GetMETARs(callback, airports))
1383
1384 def sendPIREP(self, callback, pirep):
1385 """Send the given PIREP."""
1386 request = \
1387 SendPIREPRPC(self._rpcClient, callback, pirep) if self._config.useRPC \
1388 else SendPIREP(callback, pirep)
1389 self._addRequest(request)
1390
1391 def sendACARS(self, callback, acars):
1392 """Send the given ACARS"""
1393 request = \
1394 SendACARSRPC(self._rpcClient, callback, acars) if self._config.useRPC \
1395 else SendACARS(callback, acars)
1396 self._addRequest(request)
1397
1398 def sendBugReport(self, callback, summary, description, email):
1399 """Send a bug report with the given data."""
1400 self._addRequest(SendBugReport(callback, summary, description, email))
1401
1402 def setCheckFlightPassed(self, callback, aircraftType):
1403 """Mark the check flight as passed."""
1404 self._addRequest(SetCheckFlightPassed(self._rpcClient,
1405 callback, aircraftType))
1406
1407 def getPIREP(self, callback, flightID):
1408 """Query the PIREP for the given flight."""
1409 self._addRequest(GetPIREP(self._rpcClient, callback, flightID))
1410
1411 def reflyFlights(self, callback, flightIDs):
1412 """Mark the flights with the given IDs for reflying."""
1413 self._addRequest(ReflyFlights(self._rpcClient, callback, flightIDs))
1414
1415 def deleteFlights(self, callback, flightIDs):
1416 """Delete the flights with the given IDs."""
1417 self._addRequest(DeleteFlights(self._rpcClient, callback, flightIDs))
1418
1419 def run(self):
1420 """Process the requests."""
1421 while True:
1422 with self._requestCondition:
1423 while not self._requests:
1424 self._requestCondition.wait()
1425 request = self._requests[0]
1426 del self._requests[0]
1427
1428 request.perform()
1429
1430 def _addRequest(self, request):
1431 """Add the given request to the queue."""
1432 with self._requestCondition:
1433 self._requests.append(request)
1434 self._requestCondition.notify()
1435
1436#------------------------------------------------------------------------------
1437
1438if __name__ == "__main__":
1439 import time
1440
1441 def callback(returned, result):
1442 print returned, unicode(result)
1443
1444 handler = Handler()
1445 handler.start()
1446
1447 #handler.login(callback, "P096", "V5fwj")
1448 #handler.getFleet(callback)
1449 # Plane: HA-LEG home (gate 67)
1450 #handler.updatePlane(callback, "HA-LQC", const.PLANE_AWAY, "72")
1451 #time.sleep(3)
1452 #handler.getFleet(callback)
1453 #time.sleep(3)
1454
1455 #handler.getNOTAMs(callback, "LHBP", "EPWA")
1456 #handler.getMETARs(callback, ["LHBP", "EPWA"])
1457 #time.sleep(5)
1458
1459 handler.updatePlane(callback, "HA-LON", const.PLANE_AWAY, "")
1460 time.sleep(3)
1461
1462#------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.