source: src/mlx/web.py@ 206:35d44ec6e2be

Last change on this file since 206:35d44ec6e2be was 205:8b389911b3d9, checked in by István Váradi <ivaradi@…>, 13 years ago

Fixed flights that include midnight

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