source: src/mlx/web.py@ 501:2fd9b3270f6d

xplane
Last change on this file since 501:2fd9b3270f6d was 496:0dadad5a93b8, checked in by István Váradi <ivaradi@…>, 12 years ago

Merged the default branch

File size: 33.4 KB
Line 
1
2import const
3import util
4
5import threading
6import sys
7import urllib
8import urllib2
9import hashlib
10import time
11import datetime
12import codecs
13import traceback
14import xml.sax
15import xmlrpclib
16
17#---------------------------------------------------------------------------------------
18
19## @package mlx.web
20#
21# Web interface.
22#
23# This module implements a thread that can perform (HTTP) requests
24# asynchronously. When the request is performed, a callback is called. The main
25# interface is the \ref Handler class. Each of its functions creates a \ref
26# Request subclass instance and puts it to the request queue. The handler
27# thread then takes the requests one by one, and executes them.
28#
29# This module also defines some data classes the contents of which are
30# retrieved or sent via HTTP. \ref BookedFlight contains data of a flight
31# booked on the MAVA website, \ref Fleet and \ref Plane represents the MAVA
32# fleet and the gates at Ferihegy and \ref NOTAM is a NOTAM.
33
34#---------------------------------------------------------------------------------------
35
36def readline(f):
37 """Read a line from the given file.
38
39 The line is stripped and empty lines are discarded."""
40 while True:
41 line = f.readline()
42 if not line: return ""
43 line = line.strip()
44 if line:
45 return line
46
47#---------------------------------------------------------------------------------------
48
49class BookedFlight(object):
50 """A flight that was booked."""
51 TYPECODE2TYPE = { "736" : const.AIRCRAFT_B736,
52 "73G" : const.AIRCRAFT_B737,
53 "738" : const.AIRCRAFT_B738,
54 "73H" : const.AIRCRAFT_B738C,
55 "733" : const.AIRCRAFT_B733,
56 "734" : const.AIRCRAFT_B734,
57 "735" : const.AIRCRAFT_B735,
58 "DH4" : const.AIRCRAFT_DH8D,
59 "762" : const.AIRCRAFT_B762,
60 "763" : const.AIRCRAFT_B763,
61 "CR2" : const.AIRCRAFT_CRJ2,
62 "F70" : const.AIRCRAFT_F70,
63 "LI2" : const.AIRCRAFT_DC3,
64 "TU3" : const.AIRCRAFT_T134,
65 "TU5" : const.AIRCRAFT_T154,
66 "YK4" : const.AIRCRAFT_YK40,
67 "146" : const.AIRCRAFT_B462 }
68
69 TYPE2TYPECODE = { const.AIRCRAFT_B736 : "736",
70 const.AIRCRAFT_B737 : "73G",
71 const.AIRCRAFT_B738 : "738",
72 const.AIRCRAFT_B738C : "73H",
73 const.AIRCRAFT_B733 : "733",
74 const.AIRCRAFT_B734 : "734",
75 const.AIRCRAFT_B735 : "735",
76 const.AIRCRAFT_DH8D : "DH4",
77 const.AIRCRAFT_B762 : "762",
78 const.AIRCRAFT_B763 : "763",
79 const.AIRCRAFT_CRJ2 : "CR2",
80 const.AIRCRAFT_F70 : "F70",
81 const.AIRCRAFT_DC3 : "LI2",
82 const.AIRCRAFT_T134 : "TU3",
83 const.AIRCRAFT_T154 : "TU5",
84 const.AIRCRAFT_YK40 : "YK4",
85 const.AIRCRAFT_B462 : "146" }
86
87 @staticmethod
88 def getDateTime(date, time):
89 """Get a datetime object from the given textual date and time."""
90 return datetime.datetime.strptime(date + " " + time,
91 "%Y-%m-%d %H:%M:%S")
92
93 def __init__(self, id = None):
94 """Construct a booked flight with the given ID."""
95 self.id = id
96
97 def readFromWeb(self, f):
98 """Read the data of the flight from the web via the given file
99 object."""
100 self.callsign = readline(f)
101
102 date = readline(f)
103 print "web.BookedFlight.readFromWeb: date:", date
104 if date=="0000-00-00": date = "0001-01-01"
105
106 self.departureICAO = readline(f)
107 self.arrivalICAO = readline(f)
108
109 self._readAircraftType(f)
110 self.tailNumber = readline(f)
111 self.numPassengers = int(readline(f))
112 self.numCrew = int(readline(f))
113 self.bagWeight = int(readline(f))
114 self.cargoWeight = int(readline(f))
115 self.mailWeight = int(readline(f))
116 self.route = readline(f)
117
118 departureTime = readline(f)
119 self.departureTime = BookedFlight.getDateTime(date, departureTime)
120
121 arrivalTime = readline(f)
122 self.arrivalTime = BookedFlight.getDateTime(date, arrivalTime)
123 if self.arrivalTime<self.departureTime:
124 self.arrivalTime += datetime.timedelta(days = 1)
125
126 if not readline(f)==".NEXT.":
127 raise Exception("Invalid line in flight data")
128
129 def readFromFile(self, f):
130 """Read the data of the flight from a file via the given file
131 object."""
132 date = None
133 departureTime = None
134 arrivalTime = None
135
136 line = f.readline()
137 lineNumber = 0
138 while line:
139 lineNumber += 1
140 line = line.strip()
141
142 hashIndex = line.find("#")
143 if hashIndex>=0: line = line[:hashIndex]
144 if line:
145 equalIndex = line.find("=")
146 lineOK = equalIndex>0
147
148 if lineOK:
149 key = line[:equalIndex].strip()
150 value = line[equalIndex+1:].strip().replace("\:", ":")
151
152 lineOK = key and value
153
154 if lineOK:
155 if key=="callsign": self.callsign = value
156 elif key=="date": date = value
157 elif key=="dep_airport": self.departureICAO = value
158 elif key=="dest_airport": self.arrivalICAO = value
159 elif key=="planecode": self.aircraftType = \
160 self._decodeAircraftType(value)
161 elif key=="planetype": self.aircraftTypeName = value
162 elif key=="tail_nr": self.tailNumber = value
163 elif key=="passenger": self.numPassengers = int(value)
164 elif key=="crew": self.numCrew = int(value)
165 elif key=="bag": self.bagWeight = int(value)
166 elif key=="cargo": self.cargoWeight = int(value)
167 elif key=="mail": self.mailWeight = int(value)
168 elif key=="flight_route": self.route = value
169 elif key=="departure_time": departureTime = value
170 elif key=="arrival_time": arrivalTime = value
171 elif key=="foglalas_id":
172 self.id = None if value=="0" else value
173 else: lineOK = False
174
175 if not lineOK:
176 print "web.BookedFlight.readFromFile: line %d is invalid" % \
177 (lineNumber,)
178
179 line = f.readline()
180
181 if date is not None:
182 if departureTime is not None:
183 self.departureTime = BookedFlight.getDateTime(date,
184 departureTime)
185 if arrivalTime is not None:
186 self.arrivalTime = BookedFlight.getDateTime(date,
187 arrivalTime)
188
189 d = dir(self)
190 for attribute in ["callsign", "departureICAO", "arrivalICAO",
191 "aircraftType", "tailNumber",
192 "numPassengers", "numCrew",
193 "bagWeight", "cargoWeight", "mailWeight",
194 "route", "departureTime", "arrivalTime"]:
195 if attribute not in d:
196 raise Exception("Attribute %s could not be read" % (attribute,))
197
198 if "aircraftTypeName" not in d:
199 self.aircraftTypeName = \
200 BookedFlight.TYPE2TYPECODE[self.aircraftType]
201
202 def writeIntoFile(self, f):
203 """Write the flight into a file."""
204 print >> f, "callsign=%s" % (self.callsign,)
205 date = self.departureTime.date()
206 print >> f, "date=%04d-%02d-%0d" % (date.year, date.month, date.day)
207 print >> f, "dep_airport=%s" % (self.departureICAO,)
208 print >> f, "dest_airport=%s" % (self.arrivalICAO,)
209 print >> f, "planecode=%s" % \
210 (BookedFlight.TYPE2TYPECODE[self.aircraftType],)
211 print >> f, "planetype=%s" % (self.aircraftTypeName,)
212 print >> f, "tail_nr=%s" % (self.tailNumber,)
213 print >> f, "passenger=%d" % (self.numPassengers,)
214 print >> f, "crew=%d" % (self.numCrew,)
215 print >> f, "bag=%d" % (self.bagWeight,)
216 print >> f, "cargo=%d" % (self.cargoWeight,)
217 print >> f, "mail=%d" % (self.mailWeight,)
218 print >> f, "flight_route=%s" % (self.route,)
219 departureTime = self.departureTime
220 print >> f, "departure_time=%02d\\:%02d\\:%02d" % \
221 (departureTime.hour, departureTime.minute, departureTime.second)
222 arrivalTime = self.arrivalTime
223 print >> f, "arrival_time=%02d\\:%02d\\:%02d" % \
224 (arrivalTime.hour, arrivalTime.minute, arrivalTime.second)
225 print >> f, "foglalas_id=%s" % ("0" if self.id is None else self.id,)
226
227 def _readAircraftType(self, f):
228 """Read the aircraft type from the given file."""
229 line = readline(f)
230 typeCode = line[:3]
231 self.aircraftType = self._decodeAircraftType(typeCode)
232 self.aircraftTypeName = line[3:]
233
234 def _decodeAircraftType(self, typeCode):
235 """Decode the aircraft type from the given typeCode."""
236 if typeCode in self.TYPECODE2TYPE:
237 return self.TYPECODE2TYPE[typeCode]
238 else:
239 raise Exception("Invalid aircraft type code: '" + typeCode + "'")
240
241 def __repr__(self):
242 """Get a representation of the flight."""
243 s = "<Flight: %s-%s, %s, %s-%s," % (self.departureICAO,
244 self.arrivalICAO,
245 self.route,
246 self.departureTime, self.arrivalTime)
247 s += " %d %s," % (self.aircraftType, self.tailNumber)
248 s += " pax=%d, crew=%d, bag=%d, cargo=%d, mail=%d" % \
249 (self.numPassengers, self.numCrew,
250 self.bagWeight, self.cargoWeight, self.mailWeight)
251 s += ">"
252 return s
253
254#------------------------------------------------------------------------------
255
256class Plane(object):
257 """Information about an airplane in the fleet."""
258 def __init__(self, s):
259 """Build a plane info based on the given string.
260
261 The string consists of three, space-separated fields.
262 The first field is the tail number, the second field is the gate
263 number, the third field is the plane's status as a character."""
264 try:
265 words = s.split(" ")
266 tailNumber = words[0]
267 self.tailNumber = tailNumber
268
269 status = words[2] if len(words)>2 else None
270 self.status = const.PLANE_HOME if status=="H" else \
271 const.PLANE_AWAY if status=="A" else \
272 const.PLANE_PARKING if status=="P" else \
273 const.PLANE_UNKNOWN
274
275 gateNumber = words[1] if len(words)>1 else ""
276 self.gateNumber = gateNumber if gateNumber else None
277
278 except:
279 print >> sys.stderr, "Plane string is invalid: '" + s + "'"
280 self.tailNumber = None
281
282 def __repr__(self):
283 """Get the representation of the plane object."""
284 s = "<Plane: %s %s" % (self.tailNumber,
285 "home" if self.status==const.PLANE_HOME else \
286 "away" if self.status==const.PLANE_AWAY else \
287 "parking" if self.status==const.PLANE_PARKING \
288 else "unknown")
289 if self.gateNumber is not None:
290 s += " (gate " + self.gateNumber + ")"
291 s += ">"
292 return s
293
294
295#------------------------------------------------------------------------------
296
297class Fleet(object):
298 """Information about the whole fleet."""
299 def __init__(self, f):
300 """Construct the fleet information by reading the given file object."""
301 self._planes = {}
302 while True:
303 line = readline(f)
304 if not line or line == "#END": break
305
306 plane = Plane(line)
307 if plane.tailNumber is not None:
308 self._planes[plane.tailNumber] = plane
309
310 def isGateConflicting(self, plane):
311 """Check if the gate of the given plane conflicts with another plane's
312 position."""
313 for p in self._planes.itervalues():
314 if p.tailNumber!=plane.tailNumber and \
315 p.status==const.PLANE_HOME and \
316 p.gateNumber==plane.gateNumber:
317 return True
318
319 return False
320
321 def getOccupiedGateNumbers(self):
322 """Get a set containing the numbers of the gates occupied by planes."""
323 gateNumbers = set()
324 for p in self._planes.itervalues():
325 if p.status==const.PLANE_HOME and p.gateNumber:
326 gateNumbers.add(p.gateNumber)
327 return gateNumbers
328
329 def updatePlane(self, tailNumber, status, gateNumber = None):
330 """Update the status of the given plane."""
331 if tailNumber in self._planes:
332 plane = self._planes[tailNumber]
333 plane.status = status
334 plane.gateNumber = gateNumber
335
336 def __iter__(self):
337 """Get an iterator over the planes."""
338 for plane in self._planes.itervalues():
339 yield plane
340
341 def __getitem__(self, tailNumber):
342 """Get the plane with the given tail number.
343
344 If the plane is not in the fleet, None is returned."""
345 return self._planes[tailNumber] if tailNumber in self._planes else None
346
347 def __repr__(self):
348 """Get the representation of the fleet object."""
349 return self._planes.__repr__()
350
351#------------------------------------------------------------------------------
352
353class NOTAM(object):
354 """A NOTAM for an airport."""
355 def __init__(self, begin, notice, end = None, permanent = False,
356 repeatCycle = None):
357 """Construct the NOTAM."""
358 self.begin = begin
359 self.notice = notice
360 self.end = end
361 self.permanent = permanent
362 self.repeatCycle = None
363
364 def __repr__(self):
365 """Get the representation of the NOTAM."""
366 s = "<NOTAM " + str(self.begin)
367 if self.end:
368 s += " - " + str(self.end)
369 elif self.permanent:
370 s += " - PERMANENT"
371 if self.repeatCycle:
372 s += " (" + self.repeatCycle + ")"
373 s += ": " + self.notice
374 s += ">"
375 return s
376
377#------------------------------------------------------------------------------
378
379class NOTAMHandler(xml.sax.handler.ContentHandler):
380 """A handler for the NOTAM database."""
381 def __init__(self, airportICAOs):
382 """Construct the handler for the airports with the given ICAO code."""
383 self._notams = {}
384 for icao in airportICAOs:
385 self._notams[icao] = []
386
387 def startElement(self, name, attrs):
388 """Start an element."""
389 if name!="notam" or \
390 "A" not in attrs or not attrs["A"] or \
391 "B" not in attrs or not attrs["B"] or \
392 "E" not in attrs or not attrs["E"]:
393 return
394
395 icao = attrs["A"]
396 if icao not in self._notams:
397 return
398
399 begin = datetime.datetime.strptime(attrs["B"], "%Y-%m-%d %H:%M:%S")
400
401 c = attrs["C"] if "C" in attrs else None
402 end = datetime.datetime.strptime(c, "%Y-%m-%d %H:%M:%S") if c else None
403
404 permanent = attrs["C_flag"]=="PERM" if "C_flag" in attrs else False
405
406 repeatCycle = attrs["D"] if "D" in attrs else None
407
408 self._notams[icao].append(NOTAM(begin, attrs["E"], end = end,
409 permanent = permanent,
410 repeatCycle = repeatCycle))
411
412 def get(self, icao):
413 """Get the NOTAMs for the given ICAO code."""
414 return self._notams[icao] if icao in self._notams else []
415
416#------------------------------------------------------------------------------
417
418class Result(object):
419 """A result object.
420
421 An instance of this filled with the appropriate data is passed to the
422 callback function on each request."""
423
424 def __repr__(self):
425 """Get a representation of the result."""
426 s = "<Result:"
427 for (key, value) in self.__dict__.iteritems():
428 s += " " + key + "=" + unicode(value)
429 s += ">"
430 return s
431
432#------------------------------------------------------------------------------
433
434class Request(object):
435 """Base class for requests.
436
437 It handles any exceptions and the calling of the callback.
438
439 If an exception occurs during processing, the callback is called with
440 the two parameters: a boolean value of False, and the exception object.
441
442 If no exception occurs, the callback is called with True and the return
443 value of the run() function.
444
445 If the callback function throws an exception, that is caught and logged
446 to the debug log."""
447 def __init__(self, callback):
448 """Construct the request."""
449 self._callback = callback
450
451 def perform(self):
452 """Perform the request.
453
454 The object's run() function is called. If it throws an exception,
455 the callback is called with False, and the exception. Otherwise the
456 callback is called with True and the return value of the run()
457 function. Any exceptions thrown by the callback are caught and
458 reported."""
459 try:
460 result = self.run()
461 returned = True
462 except Exception, e:
463 traceback.print_exc()
464 result = e
465 returned = False
466
467 try:
468 self._callback(returned, result)
469 except Exception, e:
470 print >> sys.stderr, "web.Handler.Request.perform: callback throwed an exception: " + util.utf2unicode(str(e))
471 traceback.print_exc()
472
473#------------------------------------------------------------------------------
474
475class Login(Request):
476 """A login request."""
477 iso88592decoder = codecs.getdecoder("iso-8859-2")
478
479 def __init__(self, callback, pilotID, password, entranceExam):
480 """Construct the login request with the given pilot ID and
481 password."""
482 super(Login, self).__init__(callback)
483
484 self._pilotID = pilotID
485 self._password = password
486 self._entranceExam = entranceExam
487
488 def run(self):
489 """Perform the login request."""
490 md5 = hashlib.md5()
491 md5.update(self._pilotID)
492 pilotID = md5.hexdigest()
493
494 md5 = hashlib.md5()
495 md5.update(self._password)
496 password = md5.hexdigest()
497
498 if self._entranceExam:
499 url = "http://www.virtualairlines.hu/ellenorzo/getflightplan.php?pid=%s" % \
500 (pilotID,)
501 else:
502 url = "http://www.virtualairlines.hu/leker2.php?pid=%s&psw=%s" % \
503 (pilotID, password)
504
505 result = Result()
506 result.entranceExam = self._entranceExam
507
508 f = urllib2.urlopen(url, timeout = 10.0)
509
510 status = readline(f)
511 if self._entranceExam:
512 result.loggedIn = status != "#NOEXAM"
513 else:
514 result.loggedIn = status == ".OK."
515
516 if result.loggedIn:
517 result.pilotID = self._pilotID
518 result.password = self._password
519 result.flights = []
520 # FIXME: this may not be the correct behaviour
521 # for an entrance exam, but the website returns
522 # an error
523 if self._entranceExam:
524 result.pilotName = result.pilotID
525 result.exams = ""
526 else:
527 result.pilotName = self.iso88592decoder(readline(f))[0]
528 result.exams = readline(f)
529
530 while True:
531 line = readline(f)
532 if not line or line == "#ENDPIREP": break
533
534 flight = BookedFlight(line)
535 flight.readFromWeb(f)
536 result.flights.append(flight)
537
538 result.flights.sort(cmp = lambda flight1, flight2:
539 cmp(flight1.departureTime,
540 flight2.departureTime))
541
542 f.close()
543
544 return result
545
546#------------------------------------------------------------------------------
547
548class GetFleet(Request):
549 """Request to get the fleet from the website."""
550
551 def __init__(self, callback):
552 """Construct the fleet request."""
553 super(GetFleet, self).__init__(callback)
554
555 def run(self):
556 """Perform the login request."""
557 url = "http://www.virtualairlines.hu/onlinegates_get.php"
558
559 f = urllib2.urlopen(url, timeout = 10.0)
560 result = Result()
561 result.fleet = Fleet(f)
562 f.close()
563
564 return result
565
566#------------------------------------------------------------------------------
567
568class UpdatePlane(Request):
569 """Update the status of one of the planes in the fleet."""
570 def __init__(self, callback, tailNumber, status, gateNumber = None):
571 """Construct the request."""
572 super(UpdatePlane, self).__init__(callback)
573 self._tailNumber = tailNumber
574 self._status = status
575 self._gateNumber = gateNumber
576
577 def run(self):
578 """Perform the plane update."""
579 url = "http://www.virtualairlines.hu/onlinegates_set.php"
580
581 status = "H" if self._status==const.PLANE_HOME else \
582 "A" if self._status==const.PLANE_AWAY else \
583 "P" if self._status==const.PLANE_PARKING else ""
584
585 gateNumber = self._gateNumber if self._gateNumber else ""
586
587 data = urllib.urlencode([("lajstrom", self._tailNumber),
588 ("status", status),
589 ("kapu", gateNumber)])
590
591 f = urllib2.urlopen(url, data, timeout = 10.0)
592 line = readline(f)
593
594 result = Result()
595 result.success = line == "OK"
596
597 return result
598
599#------------------------------------------------------------------------------
600
601class GetNOTAMs(Request):
602 """Get the NOTAMs from EURoutePro and select the ones we are interested
603 in."""
604 def __init__(self, callback, departureICAO, arrivalICAO):
605 """Construct the request for the given airports."""
606 super(GetNOTAMs, self).__init__(callback)
607 self._departureICAO = departureICAO
608 self._arrivalICAO = arrivalICAO
609
610 def run(self):
611 """Perform the retrieval of the NOTAMs."""
612 xmlParser = xml.sax.make_parser()
613 notamHandler = NOTAMHandler([self._departureICAO, self._arrivalICAO])
614 xmlParser.setContentHandler(notamHandler)
615
616 url = "http://notams.euroutepro.com/notams.xml"
617
618 f = urllib2.urlopen(url, timeout = 10.0)
619 try:
620 xmlParser.parse(f)
621 finally:
622 f.close()
623
624 result = Result()
625 result.departureNOTAMs = notamHandler.get(self._departureICAO)
626 result.arrivalNOTAMs = notamHandler.get(self._arrivalICAO)
627
628 return result
629
630#------------------------------------------------------------------------------
631
632class GetMETARs(Request):
633 """Get the METARs from the NOAA website for certain airport ICAOs."""
634
635 def __init__(self, callback, airports):
636 """Construct the request for the given airports."""
637 super(GetMETARs, self).__init__(callback)
638 self._airports = airports
639
640 def run(self):
641 """Perform the retrieval opf the METARs."""
642 url = "http://www.aviationweather.gov/adds/dataserver_current/httpparam?"
643 data = urllib.urlencode([ ("dataSource" , "metars"),
644 ("requestType", "retrieve"),
645 ("format", "csv"),
646 ("stationString", " ".join(self._airports)),
647 ("hoursBeforeNow", "24"),
648 ("mostRecentForEachStation", "constraint")])
649 url += data
650 f = urllib2.urlopen(url, timeout = 10.0)
651 try:
652 result = Result()
653 result.metars = {}
654 for line in iter(f.readline, ""):
655 if len(line)>5 and line[4]==' ':
656 icao = line[0:4]
657 if icao in self._airports:
658 result.metars[icao] = line.strip().split(",")[0]
659 finally:
660 f.close()
661
662 return result
663
664#------------------------------------------------------------------------------
665
666class SendPIREP(Request):
667 """A request to send a PIREP to the MAVA website."""
668 _flightTypes = { const.FLIGHTTYPE_SCHEDULED : "SCHEDULED",
669 const.FLIGHTTYPE_OLDTIMER : "OT",
670 const.FLIGHTTYPE_VIP : "VIP",
671 const.FLIGHTTYPE_CHARTER : "CHARTER" }
672
673 _latin2Encoder = codecs.getencoder("iso-8859-2")
674
675 def __init__(self, callback, pirep):
676 """Construct the sending of the PIREP."""
677 super(SendPIREP, self).__init__(callback)
678 self._pirep = pirep
679
680 def run(self):
681 """Perform the sending of the PIREP."""
682 url = "http://www.virtualairlines.hu/malevacars.php"
683 #url = "http://localhost:15000"
684
685 pirep = self._pirep
686
687 data = {}
688 data["acarsdata"] = SendPIREP._latin2Encoder(pirep.getACARSText())[0]
689
690 bookedFlight = pirep.bookedFlight
691 data["foglalas_id"] = bookedFlight.id
692 data["repdate"] = bookedFlight.departureTime.date().strftime("%Y-%m-%d")
693 data["fltnum"] = bookedFlight.callsign
694 data["depap"] = bookedFlight.departureICAO
695 data["arrap"] = bookedFlight.arrivalICAO
696 data["pass"] = str(pirep.numPassengers)
697 data["crew"] = str(pirep.numCrew)
698 data["cargo"] = str(pirep.cargoWeight)
699 data["bag"] = str(pirep.bagWeight)
700 data["mail"] = str(pirep.mailWeight)
701
702 data["flttype"] = SendPIREP._flightTypes[pirep.flightType]
703 data["onoff"] = "1" if pirep.online else "0"
704 data["bt_dep"] = util.getTimestampString(pirep.blockTimeStart)
705 data["bt_arr"] = util.getTimestampString(pirep.blockTimeEnd)
706 data["bt_dur"] = util.getTimeIntervalString(pirep.blockTimeEnd -
707 pirep.blockTimeStart)
708 data["ft_dep"] = util.getTimestampString(pirep.flightTimeStart)
709 data["ft_arr"] = util.getTimestampString(pirep.flightTimeEnd)
710 data["ft_dur"] = util.getTimeIntervalString(pirep.flightTimeEnd -
711 pirep.flightTimeStart)
712 data["timecomm"] = pirep.getTimeComment()
713 data["fuel"] = "%.2f" % (pirep.fuelUsed,)
714 data["dep_rwy"] = pirep.departureRunway
715 data["arr_rwy"] = pirep.arrivalRunway
716 data["wea_dep"] = pirep.departureMETAR
717 data["wea_arr"] = pirep.arrivalMETAR
718 data["alt"] = "FL%.0f" % (pirep.filedCruiseAltitude/100.0,)
719 if pirep.filedCruiseAltitude!=pirep.cruiseAltitude:
720 data["mod_alt"] = "FL%.0f" % (pirep.cruiseAltitude/100.0,)
721 else:
722 data["mod_alt"] = ""
723 data["sid"] = pirep.sid
724 data["navroute"] = pirep.route
725 data["star"] = pirep.getSTAR()
726 data["aprtype"] = pirep.approachType
727 data["diff"] = "2"
728 data["comment"] = SendPIREP._latin2Encoder(pirep.comments)[0]
729 data["flightdefect"] = SendPIREP._latin2Encoder(pirep.flightDefects)[0]
730 data["kritika"] = pirep.getRatingText()
731 data["flightrating"] = "%.1f" % (max(0.0, pirep.rating),)
732 data["distance"] = "%.3f" % (pirep.flownDistance,)
733 data["insdate"] = datetime.date.today().strftime("%Y-%m-%d")
734
735 postData = urllib.urlencode(data)
736 f = urllib2.urlopen(url, postData, timeout = 10.0)
737 try:
738 result = Result()
739 line = f.readline().strip()
740 print "PIREP result from website:", line
741 result.success = line=="OK"
742 result.alreadyFlown = line=="MARVOLT"
743 result.notAvailable = line=="NOMORE"
744 finally:
745 f.close()
746
747 return result
748#------------------------------------------------------------------------------
749
750class SendACARS(Request):
751 """A request to send an ACARS to the MAVA website."""
752 _latin2Encoder = codecs.getencoder("iso-8859-2")
753
754 def __init__(self, callback, acars):
755 """Construct the request for the given PIREP."""
756 super(SendACARS, self).__init__(callback)
757 self._acars = acars
758
759 def run(self):
760 """Perform the sending of the ACARS."""
761 print "Sending the online ACARS"
762
763 url = "http://www.virtualairlines.hu/acars2/acarsonline.php"
764
765 acars = self._acars
766 bookedFlight = acars.bookedFlight
767
768 data = {}
769 data["pid"] = acars.pid
770 data["pilot"] = SendACARS._latin2Encoder(acars.pilotName)[0]
771
772 data["pass"] = str(bookedFlight.numPassengers)
773 data["callsign"] = bookedFlight.callsign
774 data["airplane"] = bookedFlight.aircraftTypeName
775 data["from"] = bookedFlight.departureICAO
776 data["to"] = bookedFlight.arrivalICAO
777 data["lajstrom"] = bookedFlight.tailNumber
778
779 data["block_time"] = acars.getBlockTimeText()
780 data["longitude"] = str(acars.state.longitude)
781 data["latitude"] = str(acars.state.latitude)
782 data["altitude"] = str(acars.state.altitude)
783 data["speed"] = str(acars.state.groundSpeed)
784
785 data["event"] = acars.getEventText()
786
787 f = urllib2.urlopen(url, urllib.urlencode(data), timeout = 10.0)
788 try:
789 result = Result()
790 finally:
791 f.close()
792
793 return result
794
795#------------------------------------------------------------------------------
796
797class SendBugReport(Request):
798 """A request to send a bug report to the project homepage."""
799 _latin2Encoder = codecs.getencoder("iso-8859-2")
800
801 def __init__(self, callback, summary, description, email):
802 """Construct the request for the given bug report."""
803 super(SendBugReport, self).__init__(callback)
804 self._summary = summary
805 self._description = description
806 self._email = email
807
808 def run(self):
809 """Perform the sending of the bug report."""
810 serverProxy = xmlrpclib.ServerProxy("http://mlx.varadiistvan.hu/rpc")
811
812 result = Result()
813 result.success = False
814
815 attributes = {}
816 if self._email:
817 attributes["reporter"] = self._email
818
819 result.ticketID = serverProxy.ticket.create(self._summary, self._description,
820 attributes, True)
821 print "Created ticket with ID:", result.ticketID
822 result.success = True
823
824 return result
825
826#------------------------------------------------------------------------------
827
828class Handler(threading.Thread):
829 """The handler for the web services.
830
831 It can process one request at a time. The results are passed to a callback
832 function."""
833 def __init__(self):
834 """Construct the handler."""
835 super(Handler, self).__init__()
836
837 self._requests = []
838 self._requestCondition = threading.Condition()
839
840 self.daemon = True
841
842 def login(self, callback, pilotID, password, entranceExam = False):
843 """Enqueue a login request."""
844 self._addRequest(Login(callback, pilotID, password, entranceExam))
845
846 def getFleet(self, callback):
847 """Enqueue a fleet retrieval request."""
848 self._addRequest(GetFleet(callback))
849
850 def updatePlane(self, callback, tailNumber, status, gateNumber = None):
851 """Update the status of the given plane."""
852 self._addRequest(UpdatePlane(callback, tailNumber, status, gateNumber))
853
854 def getNOTAMs(self, callback, departureICAO, arrivalICAO):
855 """Get the NOTAMs for the given two airports."""
856 self._addRequest(GetNOTAMs(callback, departureICAO, arrivalICAO))
857
858 def getMETARs(self, callback, airports):
859 """Get the METARs for the given airports."""
860 self._addRequest(GetMETARs(callback, airports))
861
862 def sendPIREP(self, callback, pirep):
863 """Send the given PIREP."""
864 self._addRequest(SendPIREP(callback, pirep))
865
866 def sendACARS(self, callback, acars):
867 """Send the given ACARS"""
868 self._addRequest(SendACARS(callback, acars))
869
870 def sendBugReport(self, callback, summary, description, email):
871 """Send a bug report with the given data."""
872 self._addRequest(SendBugReport(callback, summary, description, email))
873
874 def run(self):
875 """Process the requests."""
876 while True:
877 with self._requestCondition:
878 while not self._requests:
879 self._requestCondition.wait()
880 request = self._requests[0]
881 del self._requests[0]
882
883 request.perform()
884
885 def _addRequest(self, request):
886 """Add the given request to the queue."""
887 with self._requestCondition:
888 self._requests.append(request)
889 self._requestCondition.notify()
890
891#------------------------------------------------------------------------------
892
893if __name__ == "__main__":
894 import time
895
896 def callback(returned, result):
897 print returned, unicode(result)
898
899 handler = Handler()
900 handler.start()
901
902 #handler.login(callback, "P096", "V5fwj")
903 #handler.getFleet(callback)
904 # Plane: HA-LEG home (gate 67)
905 #handler.updatePlane(callback, "HA-LQC", const.PLANE_AWAY, "72")
906 #time.sleep(3)
907 #handler.getFleet(callback)
908 #time.sleep(3)
909
910 #handler.getNOTAMs(callback, "LHBP", "EPWA")
911 #handler.getMETARs(callback, ["LHBP", "EPWA"])
912 #time.sleep(5)
913
914 handler.updatePlane(callback, "HA-LON", const.PLANE_AWAY, "")
915 time.sleep(3)
916
917#------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.