source: src/mlx/web.py@ 312:77203c978bf5

Last change on this file since 312:77203c978bf5 was 312:77203c978bf5, checked in by István Váradi <ivaradi@…>, 12 years ago

Removed the second instance of SendPIREP

File size: 32.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
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 retrieval opf the METARs."""
679 url = "http://www.virtualairlines.hu/malevacars.php"
680
681 pirep = self._pirep
682
683 data = {}
684 data["acarsdata"] = pirep.getACARSText()
685
686 bookedFlight = pirep.bookedFlight
687 data["foglalas_id"] = bookedFlight.id
688 data["repdate"] = bookedFlight.departureTime.date().strftime("%Y-%m-%d")
689 data["fltnum"] = bookedFlight.callsign
690 data["depap"] = bookedFlight.departureICAO
691 data["arrap"] = bookedFlight.arrivalICAO
692 data["pass"] = str(pirep.numPassengers)
693 data["crew"] = str(pirep.numCrew)
694 data["cargo"] = str(pirep.cargoWeight)
695 data["bag"] = str(pirep.bagWeight)
696 data["mail"] = str(pirep.mailWeight)
697
698 data["flttype"] = SendPIREP._flightTypes[pirep.flightType]
699 data["onoff"] = "1" if pirep.online else "0"
700 data["bt_dep"] = util.getTimestampString(pirep.blockTimeStart)
701 data["bt_arr"] = util.getTimestampString(pirep.blockTimeEnd)
702 data["bt_dur"] = util.getTimeIntervalString(pirep.blockTimeEnd -
703 pirep.blockTimeStart)
704 data["ft_dep"] = util.getTimestampString(pirep.flightTimeStart)
705 data["ft_arr"] = util.getTimestampString(pirep.flightTimeEnd)
706 data["ft_dur"] = util.getTimeIntervalString(pirep.flightTimeEnd -
707 pirep.flightTimeStart)
708 data["timecomm"] = pirep.getTimeComment()
709 data["fuel"] = "%.0f" % (pirep.fuelUsed,)
710 data["dep_rwy"] = pirep.departureRunway
711 data["arr_rwy"] = pirep.arrivalRunway
712 data["wea_dep"] = pirep.departureMETAR
713 data["wea_arr"] = pirep.arrivalMETAR
714 data["alt"] = "FL%.0f" % (pirep.filedCruiseAltitude/100.0,)
715 if pirep.filedCruiseAltitude!=pirep.cruiseAltitude:
716 data["mod_alt"] = "FL%.0f" % (pirep.cruiseAltitude/100.0,)
717 else:
718 data["mod_alt"] = ""
719 data["sid"] = pirep.sid
720 data["navroute"] = pirep.route
721 data["star"] = pirep.getSTAR()
722 data["aprtype"] = pirep.approachType
723 data["diff"] = "2"
724 data["comment"] = SendPIREP._latin2Encoder(pirep.comments)[0]
725 data["flightdefect"] = SendPIREP._latin2Encoder(pirep.flightDefects)[0]
726 data["kritika"] = pirep.getRatingText()
727 data["flightrating"] = "%.1f" % (max(0.0, pirep.rating),)
728 data["distance"] = "%.3f" % (pirep.flownDistance,)
729 data["insdate"] = datetime.date.today().strftime("%Y-%m-%d")
730
731 f = urllib2.urlopen(url, urllib.urlencode(data), timeout = 10.0)
732 try:
733 result = Result()
734 line = f.readline().strip()
735 print "PIREP result from website:", line
736 result.success = line=="OK"
737 result.alreadyFlown = line=="MARVOLT"
738 result.notAvailable = line=="NOMORE"
739 finally:
740 f.close()
741
742 return result
743#------------------------------------------------------------------------------
744
745class SendACARS(Request):
746 """A request to send an ACARS to the MAVA website."""
747 _latin2Encoder = codecs.getencoder("iso-8859-2")
748
749 def __init__(self, callback, acars):
750 """Construct the request for the given PIREP."""
751 super(SendACARS, self).__init__(callback)
752 self._acars = acars
753
754 def run(self):
755 """Perform the sending of the ACARS."""
756 print "Sending the online ACARS"
757
758 url = "http://www.virtualairlines.hu/acars2/acarsonline.php"
759
760 acars = self._acars
761 bookedFlight = acars.bookedFlight
762
763 data = {}
764 data["pid"] = acars.pid
765 data["pilot"] = SendACARS._latin2Encoder(acars.pilotName)[0]
766
767 data["pass"] = str(bookedFlight.numPassengers)
768 data["callsign"] = bookedFlight.callsign
769 data["airplane"] = bookedFlight.aircraftTypeName
770 data["from"] = bookedFlight.departureICAO
771 data["to"] = bookedFlight.arrivalICAO
772 data["lajstrom"] = bookedFlight.tailNumber
773
774 data["block_time"] = acars.getBlockTimeText()
775 data["longitude"] = str(acars.state.longitude)
776 data["latitude"] = str(acars.state.latitude)
777 data["altitude"] = str(acars.state.altitude)
778 data["speed"] = str(acars.state.groundSpeed)
779
780 data["event"] = acars.getEventText()
781
782 f = urllib2.urlopen(url, urllib.urlencode(data), timeout = 10.0)
783 try:
784 result = Result()
785 finally:
786 f.close()
787
788 return result
789
790#------------------------------------------------------------------------------
791
792class Handler(threading.Thread):
793 """The handler for the web services.
794
795 It can process one request at a time. The results are passed to a callback
796 function."""
797 def __init__(self):
798 """Construct the handler."""
799 super(Handler, self).__init__()
800
801 self._requests = []
802 self._requestCondition = threading.Condition()
803
804 self.daemon = True
805
806 def login(self, callback, pilotID, password, entranceExam = False):
807 """Enqueue a login request."""
808 self._addRequest(Login(callback, pilotID, password, entranceExam))
809
810 def getFleet(self, callback):
811 """Enqueue a fleet retrieval request."""
812 self._addRequest(GetFleet(callback))
813
814 def updatePlane(self, callback, tailNumber, status, gateNumber = None):
815 """Update the status of the given plane."""
816 self._addRequest(UpdatePlane(callback, tailNumber, status, gateNumber))
817
818 def getNOTAMs(self, callback, departureICAO, arrivalICAO):
819 """Get the NOTAMs for the given two airports."""
820 self._addRequest(GetNOTAMs(callback, departureICAO, arrivalICAO))
821
822 def getMETARs(self, callback, airports):
823 """Get the METARs for the given airports."""
824 self._addRequest(GetMETARs(callback, airports))
825
826 def sendPIREP(self, callback, pirep):
827 """Send the given PIREP."""
828 self._addRequest(SendPIREP(callback, pirep))
829
830 def sendACARS(self, callback, acars):
831 """Send the given ACARS"""
832 self._addRequest(SendACARS(callback, acars))
833
834 def run(self):
835 """Process the requests."""
836 while True:
837 with self._requestCondition:
838 while not self._requests:
839 self._requestCondition.wait()
840 request = self._requests[0]
841 del self._requests[0]
842
843 request.perform()
844
845 def _addRequest(self, request):
846 """Add the given request to the queue."""
847 with self._requestCondition:
848 self._requests.append(request)
849 self._requestCondition.notify()
850
851#------------------------------------------------------------------------------
852
853if __name__ == "__main__":
854 import time
855
856 def callback(returned, result):
857 print returned, unicode(result)
858
859 handler = Handler()
860 handler.start()
861
862 #handler.login(callback, "P096", "V5fwj")
863 #handler.getFleet(callback)
864 # Plane: HA-LEG home (gate 67)
865 #handler.updatePlane(callback, "HA-LQC", const.PLANE_AWAY, "72")
866 #time.sleep(3)
867 #handler.getFleet(callback)
868 #time.sleep(3)
869
870 #handler.getNOTAMs(callback, "LHBP", "EPWA")
871 #handler.getMETARs(callback, ["LHBP", "EPWA"])
872 #time.sleep(5)
873
874 handler.updatePlane(callback, "HA-LON", const.PLANE_AWAY, "")
875 time.sleep(3)
876
877#------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.