source: src/mlx/web.py@ 388:61c304ff3d63

Last change on this file since 388:61c304ff3d63 was 326:cee8a7512e56, checked in by István Váradi <ivaradi@…>, 12 years ago

The ACARS text is converted to latin-2, and normal round parentheses are used instead of square brackets (#135)

File size: 32.5 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=="planetype": self.aircraftTypeName = value
159 elif key=="tail_nr": self.tailNumber = value
160 elif key=="passenger": self.numPassengers = int(value)
161 elif key=="crew": self.numCrew = int(value)
162 elif key=="bag": self.bagWeight = int(value)
163 elif key=="cargo": self.cargoWeight = int(value)
164 elif key=="mail": self.mailWeight = int(value)
165 elif key=="flight_route": self.route = value
166 elif key=="departure_time": departureTime = value
167 elif key=="arrival_time": arrivalTime = value
168 elif key=="foglalas_id":
169 self.id = None if value=="0" else value
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 if "aircraftTypeName" not in d:
196 self.aircraftTypeName = \
197 BookedFlight.TYPE2TYPECODE[self.aircraftType]
198
199 def writeIntoFile(self, f):
200 """Write the flight into a file."""
201 print >> f, "callsign=%s" % (self.callsign,)
202 date = self.departureTime.date()
203 print >> f, "date=%04d-%02d-%0d" % (date.year, date.month, date.day)
204 print >> f, "dep_airport=%s" % (self.departureICAO,)
205 print >> f, "dest_airport=%s" % (self.arrivalICAO,)
206 print >> f, "planecode=%s" % \
207 (BookedFlight.TYPE2TYPECODE[self.aircraftType],)
208 print >> f, "planetype=%s" % (self.aircraftTypeName,)
209 print >> f, "tail_nr=%s" % (self.tailNumber,)
210 print >> f, "passenger=%d" % (self.numPassengers,)
211 print >> f, "crew=%d" % (self.numCrew,)
212 print >> f, "bag=%d" % (self.bagWeight,)
213 print >> f, "cargo=%d" % (self.cargoWeight,)
214 print >> f, "mail=%d" % (self.mailWeight,)
215 print >> f, "flight_route=%s" % (self.route,)
216 departureTime = self.departureTime
217 print >> f, "departure_time=%02d\\:%02d\\:%02d" % \
218 (departureTime.hour, departureTime.minute, departureTime.second)
219 arrivalTime = self.arrivalTime
220 print >> f, "arrival_time=%02d\\:%02d\\:%02d" % \
221 (arrivalTime.hour, arrivalTime.minute, arrivalTime.second)
222 print >> f, "foglalas_id=%s" % ("0" if self.id is None else self.id,)
223
224 def _readAircraftType(self, f):
225 """Read the aircraft type from the given file."""
226 line = readline(f)
227 typeCode = line[:3]
228 self.aircraftType = self._decodeAircraftType(typeCode)
229 self.aircraftTypeName = line[3:]
230
231 def _decodeAircraftType(self, typeCode):
232 """Decode the aircraft type from the given typeCode."""
233 if typeCode in self.TYPECODE2TYPE:
234 return self.TYPECODE2TYPE[typeCode]
235 else:
236 raise Exception("Invalid aircraft type code: '" + typeCode + "'")
237
238 def __repr__(self):
239 """Get a representation of the flight."""
240 s = "<Flight: %s-%s, %s, %s-%s," % (self.departureICAO,
241 self.arrivalICAO,
242 self.route,
243 self.departureTime, self.arrivalTime)
244 s += " %d %s," % (self.aircraftType, self.tailNumber)
245 s += " pax=%d, crew=%d, bag=%d, cargo=%d, mail=%d" % \
246 (self.numPassengers, self.numCrew,
247 self.bagWeight, self.cargoWeight, self.mailWeight)
248 s += ">"
249 return s
250
251#------------------------------------------------------------------------------
252
253class Plane(object):
254 """Information about an airplane in the fleet."""
255 def __init__(self, s):
256 """Build a plane info based on the given string.
257
258 The string consists of three, space-separated fields.
259 The first field is the tail number, the second field is the gate
260 number, the third field is the plane's status as a character."""
261 try:
262 words = s.split(" ")
263 tailNumber = words[0]
264 self.tailNumber = tailNumber
265
266 status = words[2] if len(words)>2 else None
267 self.status = const.PLANE_HOME if status=="H" else \
268 const.PLANE_AWAY if status=="A" else \
269 const.PLANE_PARKING if status=="P" else \
270 const.PLANE_UNKNOWN
271
272 gateNumber = words[1] if len(words)>1 else ""
273 self.gateNumber = gateNumber if gateNumber else None
274
275 except:
276 print >> sys.stderr, "Plane string is invalid: '" + s + "'"
277 self.tailNumber = None
278
279 def __repr__(self):
280 """Get the representation of the plane object."""
281 s = "<Plane: %s %s" % (self.tailNumber,
282 "home" if self.status==const.PLANE_HOME else \
283 "away" if self.status==const.PLANE_AWAY else \
284 "parking" if self.status==const.PLANE_PARKING \
285 else "unknown")
286 if self.gateNumber is not None:
287 s += " (gate " + self.gateNumber + ")"
288 s += ">"
289 return s
290
291
292#------------------------------------------------------------------------------
293
294class Fleet(object):
295 """Information about the whole fleet."""
296 def __init__(self, f):
297 """Construct the fleet information by reading the given file object."""
298 self._planes = {}
299 while True:
300 line = readline(f)
301 if not line or line == "#END": break
302
303 plane = Plane(line)
304 if plane.tailNumber is not None:
305 self._planes[plane.tailNumber] = plane
306
307 def isGateConflicting(self, plane):
308 """Check if the gate of the given plane conflicts with another plane's
309 position."""
310 for p in self._planes.itervalues():
311 if p.tailNumber!=plane.tailNumber and \
312 p.status==const.PLANE_HOME and \
313 p.gateNumber==plane.gateNumber:
314 return True
315
316 return False
317
318 def getOccupiedGateNumbers(self):
319 """Get a set containing the numbers of the gates occupied by planes."""
320 gateNumbers = set()
321 for p in self._planes.itervalues():
322 if p.status==const.PLANE_HOME and p.gateNumber:
323 gateNumbers.add(p.gateNumber)
324 return gateNumbers
325
326 def updatePlane(self, tailNumber, status, gateNumber = None):
327 """Update the status of the given plane."""
328 if tailNumber in self._planes:
329 plane = self._planes[tailNumber]
330 plane.status = status
331 plane.gateNumber = gateNumber
332
333 def __iter__(self):
334 """Get an iterator over the planes."""
335 for plane in self._planes.itervalues():
336 yield plane
337
338 def __getitem__(self, tailNumber):
339 """Get the plane with the given tail number.
340
341 If the plane is not in the fleet, None is returned."""
342 return self._planes[tailNumber] if tailNumber in self._planes else None
343
344 def __repr__(self):
345 """Get the representation of the fleet object."""
346 return self._planes.__repr__()
347
348#------------------------------------------------------------------------------
349
350class NOTAM(object):
351 """A NOTAM for an airport."""
352 def __init__(self, begin, notice, end = None, permanent = False,
353 repeatCycle = None):
354 """Construct the NOTAM."""
355 self.begin = begin
356 self.notice = notice
357 self.end = end
358 self.permanent = permanent
359 self.repeatCycle = None
360
361 def __repr__(self):
362 """Get the representation of the NOTAM."""
363 s = "<NOTAM " + str(self.begin)
364 if self.end:
365 s += " - " + str(self.end)
366 elif self.permanent:
367 s += " - PERMANENT"
368 if self.repeatCycle:
369 s += " (" + self.repeatCycle + ")"
370 s += ": " + self.notice
371 s += ">"
372 return s
373
374#------------------------------------------------------------------------------
375
376class NOTAMHandler(xml.sax.handler.ContentHandler):
377 """A handler for the NOTAM database."""
378 def __init__(self, airportICAOs):
379 """Construct the handler for the airports with the given ICAO code."""
380 self._notams = {}
381 for icao in airportICAOs:
382 self._notams[icao] = []
383
384 def startElement(self, name, attrs):
385 """Start an element."""
386 if name!="notam" or \
387 "A" not in attrs or not attrs["A"] or \
388 "B" not in attrs or not attrs["B"] or \
389 "E" not in attrs or not attrs["E"]:
390 return
391
392 icao = attrs["A"]
393 if icao not in self._notams:
394 return
395
396 begin = datetime.datetime.strptime(attrs["B"], "%Y-%m-%d %H:%M:%S")
397
398 c = attrs["C"] if "C" in attrs else None
399 end = datetime.datetime.strptime(c, "%Y-%m-%d %H:%M:%S") if c else None
400
401 permanent = attrs["C_flag"]=="PERM" if "C_flag" in attrs else False
402
403 repeatCycle = attrs["D"] if "D" in attrs else None
404
405 self._notams[icao].append(NOTAM(begin, attrs["E"], end = end,
406 permanent = permanent,
407 repeatCycle = repeatCycle))
408
409 def get(self, icao):
410 """Get the NOTAMs for the given ICAO code."""
411 return self._notams[icao] if icao in self._notams else []
412
413#------------------------------------------------------------------------------
414
415class Result(object):
416 """A result object.
417
418 An instance of this filled with the appropriate data is passed to the
419 callback function on each request."""
420
421 def __repr__(self):
422 """Get a representation of the result."""
423 s = "<Result:"
424 for (key, value) in self.__dict__.iteritems():
425 s += " " + key + "=" + unicode(value)
426 s += ">"
427 return s
428
429#------------------------------------------------------------------------------
430
431class Request(object):
432 """Base class for requests.
433
434 It handles any exceptions and the calling of the callback.
435
436 If an exception occurs during processing, the callback is called with
437 the two parameters: a boolean value of False, and the exception object.
438
439 If no exception occurs, the callback is called with True and the return
440 value of the run() function.
441
442 If the callback function throws an exception, that is caught and logged
443 to the debug log."""
444 def __init__(self, callback):
445 """Construct the request."""
446 self._callback = callback
447
448 def perform(self):
449 """Perform the request.
450
451 The object's run() function is called. If it throws an exception,
452 the callback is called with False, and the exception. Otherwise the
453 callback is called with True and the return value of the run()
454 function. Any exceptions thrown by the callback are caught and
455 reported."""
456 try:
457 result = self.run()
458 returned = True
459 except Exception, e:
460 traceback.print_exc()
461 result = e
462 returned = False
463
464 try:
465 self._callback(returned, result)
466 except Exception, e:
467 print >> sys.stderr, "web.Handler.Request.perform: callback throwed an exception: " + str(e)
468 traceback.print_exc()
469
470#------------------------------------------------------------------------------
471
472class Login(Request):
473 """A login request."""
474 iso88592decoder = codecs.getdecoder("iso-8859-2")
475
476 def __init__(self, callback, pilotID, password, entranceExam):
477 """Construct the login request with the given pilot ID and
478 password."""
479 super(Login, self).__init__(callback)
480
481 self._pilotID = pilotID
482 self._password = password
483 self._entranceExam = entranceExam
484
485 def run(self):
486 """Perform the login request."""
487 md5 = hashlib.md5()
488 md5.update(self._pilotID)
489 pilotID = md5.hexdigest()
490
491 md5 = hashlib.md5()
492 md5.update(self._password)
493 password = md5.hexdigest()
494
495 if self._entranceExam:
496 url = "http://www.virtualairlines.hu/ellenorzo/getflightplan.php?pid=%s" % \
497 (pilotID,)
498 else:
499 url = "http://www.virtualairlines.hu/leker2.php?pid=%s&psw=%s" % \
500 (pilotID, password)
501
502 result = Result()
503 result.entranceExam = self._entranceExam
504
505 f = urllib2.urlopen(url, timeout = 10.0)
506
507 status = readline(f)
508 if self._entranceExam:
509 result.loggedIn = status != "#NOEXAM"
510 else:
511 result.loggedIn = status == ".OK."
512
513 if result.loggedIn:
514 result.pilotID = self._pilotID
515 result.password = self._password
516 result.flights = []
517 # FIXME: this may not be the correct behaviour
518 # for an entrance exam, but the website returns
519 # an error
520 if self._entranceExam:
521 result.pilotName = result.pilotID
522 result.exams = ""
523 else:
524 result.pilotName = self.iso88592decoder(readline(f))[0]
525 result.exams = readline(f)
526
527 while True:
528 line = readline(f)
529 if not line or line == "#ENDPIREP": break
530
531 flight = BookedFlight(line)
532 flight.readFromWeb(f)
533 result.flights.append(flight)
534
535 result.flights.sort(cmp = lambda flight1, flight2:
536 cmp(flight1.departureTime,
537 flight2.departureTime))
538
539 f.close()
540
541 return result
542
543#------------------------------------------------------------------------------
544
545class GetFleet(Request):
546 """Request to get the fleet from the website."""
547
548 def __init__(self, callback):
549 """Construct the fleet request."""
550 super(GetFleet, self).__init__(callback)
551
552 def run(self):
553 """Perform the login request."""
554 url = "http://www.virtualairlines.hu/onlinegates_get.php"
555
556 f = urllib2.urlopen(url, timeout = 10.0)
557 result = Result()
558 result.fleet = Fleet(f)
559 f.close()
560
561 return result
562
563#------------------------------------------------------------------------------
564
565class UpdatePlane(Request):
566 """Update the status of one of the planes in the fleet."""
567 def __init__(self, callback, tailNumber, status, gateNumber = None):
568 """Construct the request."""
569 super(UpdatePlane, self).__init__(callback)
570 self._tailNumber = tailNumber
571 self._status = status
572 self._gateNumber = gateNumber
573
574 def run(self):
575 """Perform the plane update."""
576 url = "http://www.virtualairlines.hu/onlinegates_set.php"
577
578 status = "H" if self._status==const.PLANE_HOME else \
579 "A" if self._status==const.PLANE_AWAY else \
580 "P" if self._status==const.PLANE_PARKING else ""
581
582 gateNumber = self._gateNumber if self._gateNumber else ""
583
584 data = urllib.urlencode([("lajstrom", self._tailNumber),
585 ("status", status),
586 ("kapu", gateNumber)])
587
588 f = urllib2.urlopen(url, data, timeout = 10.0)
589 line = readline(f)
590
591 result = Result()
592 result.success = line == "OK"
593
594 return result
595
596#------------------------------------------------------------------------------
597
598class GetNOTAMs(Request):
599 """Get the NOTAMs from EURoutePro and select the ones we are interested
600 in."""
601 def __init__(self, callback, departureICAO, arrivalICAO):
602 """Construct the request for the given airports."""
603 super(GetNOTAMs, self).__init__(callback)
604 self._departureICAO = departureICAO
605 self._arrivalICAO = arrivalICAO
606
607 def run(self):
608 """Perform the retrieval of the NOTAMs."""
609 xmlParser = xml.sax.make_parser()
610 notamHandler = NOTAMHandler([self._departureICAO, self._arrivalICAO])
611 xmlParser.setContentHandler(notamHandler)
612
613 url = "http://notams.euroutepro.com/notams.xml"
614
615 f = urllib2.urlopen(url, timeout = 10.0)
616 try:
617 xmlParser.parse(f)
618 finally:
619 f.close()
620
621 result = Result()
622 result.departureNOTAMs = notamHandler.get(self._departureICAO)
623 result.arrivalNOTAMs = notamHandler.get(self._arrivalICAO)
624
625 return result
626
627#------------------------------------------------------------------------------
628
629class GetMETARs(Request):
630 """Get the METARs from the NOAA website for certain airport ICAOs."""
631
632 def __init__(self, callback, airports):
633 """Construct the request for the given airports."""
634 super(GetMETARs, self).__init__(callback)
635 self._airports = airports
636
637 def run(self):
638 """Perform the retrieval opf the METARs."""
639 url = "http://www.aviationweather.gov/adds/dataserver_current/httpparam?"
640 data = urllib.urlencode([ ("dataSource" , "metars"),
641 ("requestType", "retrieve"),
642 ("format", "csv"),
643 ("stationString", " ".join(self._airports)),
644 ("hoursBeforeNow", "24"),
645 ("mostRecentForEachStation", "constraint")])
646 url += data
647 f = urllib2.urlopen(url, timeout = 10.0)
648 try:
649 result = Result()
650 result.metars = {}
651 for line in iter(f.readline, ""):
652 if len(line)>5 and line[4]==' ':
653 icao = line[0:4]
654 if icao in self._airports:
655 result.metars[icao] = line.strip().split(",")[0]
656 finally:
657 f.close()
658
659 return result
660
661#------------------------------------------------------------------------------
662
663class SendPIREP(Request):
664 """A request to send a PIREP to the MAVA website."""
665 _flightTypes = { const.FLIGHTTYPE_SCHEDULED : "SCHEDULED",
666 const.FLIGHTTYPE_OLDTIMER : "OT",
667 const.FLIGHTTYPE_VIP : "VIP",
668 const.FLIGHTTYPE_CHARTER : "CHARTER" }
669
670 _latin2Encoder = codecs.getencoder("iso-8859-2")
671
672 def __init__(self, callback, pirep):
673 """Construct the sending of the PIREP."""
674 super(SendPIREP, self).__init__(callback)
675 self._pirep = pirep
676
677 def run(self):
678 """Perform the sending of the PIREP."""
679 url = "http://www.virtualairlines.hu/malevacars.php"
680 #url = "http://localhost:15000"
681
682 pirep = self._pirep
683
684 data = {}
685 data["acarsdata"] = SendPIREP._latin2Encoder(pirep.getACARSText())[0]
686
687 bookedFlight = pirep.bookedFlight
688 data["foglalas_id"] = bookedFlight.id
689 data["repdate"] = bookedFlight.departureTime.date().strftime("%Y-%m-%d")
690 data["fltnum"] = bookedFlight.callsign
691 data["depap"] = bookedFlight.departureICAO
692 data["arrap"] = bookedFlight.arrivalICAO
693 data["pass"] = str(pirep.numPassengers)
694 data["crew"] = str(pirep.numCrew)
695 data["cargo"] = str(pirep.cargoWeight)
696 data["bag"] = str(pirep.bagWeight)
697 data["mail"] = str(pirep.mailWeight)
698
699 data["flttype"] = SendPIREP._flightTypes[pirep.flightType]
700 data["onoff"] = "1" if pirep.online else "0"
701 data["bt_dep"] = util.getTimestampString(pirep.blockTimeStart)
702 data["bt_arr"] = util.getTimestampString(pirep.blockTimeEnd)
703 data["bt_dur"] = util.getTimeIntervalString(pirep.blockTimeEnd -
704 pirep.blockTimeStart)
705 data["ft_dep"] = util.getTimestampString(pirep.flightTimeStart)
706 data["ft_arr"] = util.getTimestampString(pirep.flightTimeEnd)
707 data["ft_dur"] = util.getTimeIntervalString(pirep.flightTimeEnd -
708 pirep.flightTimeStart)
709 data["timecomm"] = pirep.getTimeComment()
710 data["fuel"] = "%.2f" % (pirep.fuelUsed,)
711 data["dep_rwy"] = pirep.departureRunway
712 data["arr_rwy"] = pirep.arrivalRunway
713 data["wea_dep"] = pirep.departureMETAR
714 data["wea_arr"] = pirep.arrivalMETAR
715 data["alt"] = "FL%.0f" % (pirep.filedCruiseAltitude/100.0,)
716 if pirep.filedCruiseAltitude!=pirep.cruiseAltitude:
717 data["mod_alt"] = "FL%.0f" % (pirep.cruiseAltitude/100.0,)
718 else:
719 data["mod_alt"] = ""
720 data["sid"] = pirep.sid
721 data["navroute"] = pirep.route
722 data["star"] = pirep.getSTAR()
723 data["aprtype"] = pirep.approachType
724 data["diff"] = "2"
725 data["comment"] = SendPIREP._latin2Encoder(pirep.comments)[0]
726 data["flightdefect"] = SendPIREP._latin2Encoder(pirep.flightDefects)[0]
727 data["kritika"] = pirep.getRatingText()
728 data["flightrating"] = "%.1f" % (max(0.0, pirep.rating),)
729 data["distance"] = "%.3f" % (pirep.flownDistance,)
730 data["insdate"] = datetime.date.today().strftime("%Y-%m-%d")
731
732 postData = urllib.urlencode(data)
733 f = urllib2.urlopen(url, postData, timeout = 10.0)
734 try:
735 result = Result()
736 line = f.readline().strip()
737 print "PIREP result from website:", line
738 result.success = line=="OK"
739 result.alreadyFlown = line=="MARVOLT"
740 result.notAvailable = line=="NOMORE"
741 finally:
742 f.close()
743
744 return result
745#------------------------------------------------------------------------------
746
747class SendACARS(Request):
748 """A request to send an ACARS to the MAVA website."""
749 _latin2Encoder = codecs.getencoder("iso-8859-2")
750
751 def __init__(self, callback, acars):
752 """Construct the request for the given PIREP."""
753 super(SendACARS, self).__init__(callback)
754 self._acars = acars
755
756 def run(self):
757 """Perform the sending of the ACARS."""
758 print "Sending the online ACARS"
759
760 url = "http://www.virtualairlines.hu/acars2/acarsonline.php"
761
762 acars = self._acars
763 bookedFlight = acars.bookedFlight
764
765 data = {}
766 data["pid"] = acars.pid
767 data["pilot"] = SendACARS._latin2Encoder(acars.pilotName)[0]
768
769 data["pass"] = str(bookedFlight.numPassengers)
770 data["callsign"] = bookedFlight.callsign
771 data["airplane"] = bookedFlight.aircraftTypeName
772 data["from"] = bookedFlight.departureICAO
773 data["to"] = bookedFlight.arrivalICAO
774 data["lajstrom"] = bookedFlight.tailNumber
775
776 data["block_time"] = acars.getBlockTimeText()
777 data["longitude"] = str(acars.state.longitude)
778 data["latitude"] = str(acars.state.latitude)
779 data["altitude"] = str(acars.state.altitude)
780 data["speed"] = str(acars.state.groundSpeed)
781
782 data["event"] = acars.getEventText()
783
784 f = urllib2.urlopen(url, urllib.urlencode(data), timeout = 10.0)
785 try:
786 result = Result()
787 finally:
788 f.close()
789
790 return result
791
792#------------------------------------------------------------------------------
793
794class Handler(threading.Thread):
795 """The handler for the web services.
796
797 It can process one request at a time. The results are passed to a callback
798 function."""
799 def __init__(self):
800 """Construct the handler."""
801 super(Handler, self).__init__()
802
803 self._requests = []
804 self._requestCondition = threading.Condition()
805
806 self.daemon = True
807
808 def login(self, callback, pilotID, password, entranceExam = False):
809 """Enqueue a login request."""
810 self._addRequest(Login(callback, pilotID, password, entranceExam))
811
812 def getFleet(self, callback):
813 """Enqueue a fleet retrieval request."""
814 self._addRequest(GetFleet(callback))
815
816 def updatePlane(self, callback, tailNumber, status, gateNumber = None):
817 """Update the status of the given plane."""
818 self._addRequest(UpdatePlane(callback, tailNumber, status, gateNumber))
819
820 def getNOTAMs(self, callback, departureICAO, arrivalICAO):
821 """Get the NOTAMs for the given two airports."""
822 self._addRequest(GetNOTAMs(callback, departureICAO, arrivalICAO))
823
824 def getMETARs(self, callback, airports):
825 """Get the METARs for the given airports."""
826 self._addRequest(GetMETARs(callback, airports))
827
828 def sendPIREP(self, callback, pirep):
829 """Send the given PIREP."""
830 self._addRequest(SendPIREP(callback, pirep))
831
832 def sendACARS(self, callback, acars):
833 """Send the given ACARS"""
834 self._addRequest(SendACARS(callback, acars))
835
836 def run(self):
837 """Process the requests."""
838 while True:
839 with self._requestCondition:
840 while not self._requests:
841 self._requestCondition.wait()
842 request = self._requests[0]
843 del self._requests[0]
844
845 request.perform()
846
847 def _addRequest(self, request):
848 """Add the given request to the queue."""
849 with self._requestCondition:
850 self._requests.append(request)
851 self._requestCondition.notify()
852
853#------------------------------------------------------------------------------
854
855if __name__ == "__main__":
856 import time
857
858 def callback(returned, result):
859 print returned, unicode(result)
860
861 handler = Handler()
862 handler.start()
863
864 #handler.login(callback, "P096", "V5fwj")
865 #handler.getFleet(callback)
866 # Plane: HA-LEG home (gate 67)
867 #handler.updatePlane(callback, "HA-LQC", const.PLANE_AWAY, "72")
868 #time.sleep(3)
869 #handler.getFleet(callback)
870 #time.sleep(3)
871
872 #handler.getNOTAMs(callback, "LHBP", "EPWA")
873 #handler.getMETARs(callback, ["LHBP", "EPWA"])
874 #time.sleep(5)
875
876 handler.updatePlane(callback, "HA-LON", const.PLANE_AWAY, "")
877 time.sleep(3)
878
879#------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.