source: src/mlx/web.py@ 302:a4e4cda5d89a

Last change on this file since 302:a4e4cda5d89a was 298:24c67ec5cdca, checked in by István Váradi <ivaradi@…>, 12 years ago

Documented the non-GUI modules

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