source: src/mlx/web.py@ 51:f0f99ac21935

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

Fleet retrieval and gate selection works, started new connection handling and the fsuipc simulator

File size: 13.9 KB
Line 
1# Interface towards the websites used
2
3#------------------------------------------------------------------------------
4
5import const
6
7import threading
8import sys
9import urllib
10import urllib2
11import hashlib
12import datetime
13import codecs
14
15#---------------------------------------------------------------------------------------
16
17def readline(f):
18 """Read a line from the given file.
19
20 The line is stripped and empty lines are discarded."""
21 while True:
22 line = f.readline()
23 if not line: return ""
24 line = line.strip()
25 if line:
26 return line
27
28#---------------------------------------------------------------------------------------
29
30class BookedFlight(object):
31 """A flight that was booked."""
32 TYPECODE2TYPE = { "736" : const.AIRCRAFT_B736,
33 "73G" : const.AIRCRAFT_B737,
34 "738" : const.AIRCRAFT_B738,
35 "733" : const.AIRCRAFT_B733,
36 "734" : const.AIRCRAFT_B734,
37 "735" : const.AIRCRAFT_B735,
38 "DH4" : const.AIRCRAFT_DH8D,
39 "762" : const.AIRCRAFT_B762,
40 "763" : const.AIRCRAFT_B763,
41 "CR2" : const.AIRCRAFT_CRJ2,
42 "F70" : const.AIRCRAFT_F70,
43 "LI2" : const.AIRCRAFT_DC3,
44 "TU3" : const.AIRCRAFT_T134,
45 "TU5" : const.AIRCRAFT_T154,
46 "YK4" : const.AIRCRAFT_YK40 }
47
48 def __init__(self, id, f):
49 """Construct a booked flight with the given ID.
50
51 The rest of the data is read from the given file."""
52
53 self.id = id
54 self.callsign = readline(f)
55
56 date = readline(f)
57
58 self.departureICAO = readline(f)
59 self.arrivalICAO = readline(f)
60
61 self._readAircraftType(f)
62 self.tailNumber = readline(f)
63 self.numPassengers = int(readline(f))
64 self.numCrew = int(readline(f))
65 self.bagWeight = int(readline(f))
66 self.cargoWeight = int(readline(f))
67 self.mailWeight = int(readline(f))
68 self.route = readline(f)
69
70 departureTime = readline(f)
71 self.departureTime = datetime.datetime.strptime(date + " " + departureTime,
72 "%Y-%m-%d %H:%M:%S")
73
74 arrivalTime = readline(f)
75 self.arrivalTime = datetime.datetime.strptime(date + " " + arrivalTime,
76 "%Y-%m-%d %H:%M:%S")
77
78 if not readline(f)==".NEXT.":
79 raise Exception("Invalid line in flight data")
80
81 def _readAircraftType(self, f):
82 """Read the aircraft type from the given file."""
83 line = readline(f)
84 typeCode = line[:3]
85 if typeCode in self.TYPECODE2TYPE:
86 self.aircraftType = self.TYPECODE2TYPE[typeCode]
87 else:
88 raise Exception("Invalid aircraft type code: '" + typeCode + "'")
89
90 def __repr__(self):
91 """Get a representation of the flight."""
92 s = "<Flight: %s-%s, %s, %s-%s," % (self.departureICAO,
93 self.arrivalICAO,
94 self.route,
95 self.departureTime, self.arrivalTime)
96 s += " %d %s," % (self.aircraftType, self.tailNumber)
97 s += " pax=%d, crew=%d, bag=%d, cargo=%d, mail=%d" % \
98 (self.numPassengers, self.numCrew,
99 self.bagWeight, self.cargoWeight, self.mailWeight)
100 s += ">"
101 return s
102
103#------------------------------------------------------------------------------
104
105class Plane(object):
106 """Information about an airplane in the fleet."""
107 def __init__(self, s):
108 """Build a plane info based on the given string.
109
110 The string consists of three, space-separated fields.
111 The first field is the tail number, the second field is the gate
112 number, the third field is the plane's status as a character."""
113 try:
114 words = s.split(" ")
115 tailNumber = words[0]
116 self.tailNumber = tailNumber
117
118 status = words[2] if len(words)>2 else None
119 self.status = const.PLANE_HOME if status=="H" else \
120 const.PLANE_AWAY if status=="A" else \
121 const.PLANE_PARKING if status=="P" else \
122 const.PLANE_UNKNOWN
123
124 gateNumber = words[1] if len(words)>1 else ""
125 self.gateNumber = gateNumber if gateNumber else None
126
127 except:
128 print >> sys.stderr, "Plane string is invalid: '" + s + "'"
129 self.tailNumber = None
130
131 def __repr__(self):
132 """Get the representation of the plane object."""
133 s = "<Plane: %s %s" % (self.tailNumber,
134 "home" if self.status==const.PLANE_HOME else \
135 "away" if self.status==const.PLANE_AWAY else \
136 "parking" if self.status==const.PLANE_PARKING \
137 else "unknown")
138 if self.gateNumber is not None:
139 s += " (gate " + self.gateNumber + ")"
140 s += ">"
141 return s
142
143
144#------------------------------------------------------------------------------
145
146class Fleet(object):
147 """Information about the whole fleet."""
148 def __init__(self, f):
149 """Construct the fleet information by reading the given file object."""
150 self._planes = {}
151 while True:
152 line = readline(f)
153 if not line or line == "#END": break
154
155 plane = Plane(line)
156 if plane.tailNumber is not None:
157 self._planes[plane.tailNumber] = plane
158
159 def isGateConflicting(self, plane):
160 """Check if the gate of the given plane conflicts with another plane's
161 position."""
162 for p in self._planes.itervalues():
163 if p.tailNumber!=plane.tailNumber and \
164 p.status==const.PLANE_HOME and \
165 p.gateNumber==plane.gateNumber:
166 return True
167
168 return False
169
170 def getOccupiedGateNumbers(self):
171 """Get a set containing the numbers of the gates occupied by planes."""
172 gateNumbers = set()
173 for p in self._planes.itervalues():
174 if p.status==const.PLANE_HOME and p.gateNumber:
175 gateNumbers.add(p.gateNumber)
176 return gateNumbers
177
178 def __getitem__(self, tailNumber):
179 """Get the plane with the given tail number.
180
181 If the plane is not in the fleet, None is returned."""
182 return self._planes[tailNumber] if tailNumber in self._planes else None
183
184 def __repr__(self):
185 """Get the representation of the fleet object."""
186 return self._planes.__repr__()
187
188#------------------------------------------------------------------------------
189
190class Result(object):
191 """A result object.
192
193 An instance of this filled with the appropriate data is passed to the
194 callback function on each request."""
195
196 def __repr__(self):
197 """Get a representation of the result."""
198 s = "<Result:"
199 for (key, value) in self.__dict__.iteritems():
200 s += " " + key + "=" + unicode(value)
201 s += ">"
202 return s
203
204#------------------------------------------------------------------------------
205
206class Request(object):
207 """Base class for requests.
208
209 It handles any exceptions and the calling of the callback.
210
211 If an exception occurs during processing, the callback is called with
212 the two parameters: a boolean value of False, and the exception object.
213
214 If no exception occurs, the callback is called with True and the return
215 value of the run() function.
216
217 If the callback function throws an exception, that is caught and logged
218 to the debug log."""
219 def __init__(self, callback):
220 """Construct the request."""
221 self._callback = callback
222
223 def perform(self):
224 """Perform the request.
225
226 The object's run() function is called. If it throws an exception,
227 the callback is called with False, and the exception. Otherwise the
228 callback is called with True and the return value of the run()
229 function. Any exceptions thrown by the callback are caught and
230 reported."""
231 try:
232 result = self.run()
233 returned = True
234 except Exception, e:
235 result = e
236 returned = False
237
238 try:
239 self._callback(returned, result)
240 except Exception, e:
241 print >> sys.stderr, "web.Handler.Request.perform: callback throwed an exception: " + str(e)
242
243#------------------------------------------------------------------------------
244
245class Login(Request):
246 """A login request."""
247 iso88592decoder = codecs.getdecoder("iso-8859-2")
248
249 def __init__(self, callback, pilotID, password):
250 """Construct the login request with the given pilot ID and
251 password."""
252 super(Login, self).__init__(callback)
253
254 self._pilotID = pilotID
255 self._password = password
256
257 def run(self):
258 """Perform the login request."""
259 md5 = hashlib.md5()
260 md5.update(self._pilotID)
261 pilotID = md5.hexdigest()
262
263 md5 = hashlib.md5()
264 md5.update(self._password)
265 password = md5.hexdigest()
266
267 url = "http://www.virtualairlines.hu/leker2.php?pid=%s&psw=%s" % \
268 (pilotID, password)
269
270 result = Result()
271
272 f = urllib2.urlopen(url)
273
274 status = readline(f)
275 result.loggedIn = status == ".OK."
276
277 if result.loggedIn:
278 result.pilotName = self.iso88592decoder(readline(f))[0]
279 result.exams = readline(f)
280 result.flights = []
281
282 while True:
283 line = readline(f)
284 if not line or line == "#ENDPIREP": break
285
286 flight = BookedFlight(line, f)
287 result.flights.append(flight)
288
289 result.flights.sort(cmp = lambda flight1, flight2:
290 cmp(flight1.departureTime,
291 flight2.departureTime))
292
293 f.close()
294
295 return result
296
297#------------------------------------------------------------------------------
298
299class GetFleet(Request):
300 """Request to get the fleet from the website."""
301
302 def __init__(self, callback):
303 """Construct the fleet request."""
304 super(GetFleet, self).__init__(callback)
305
306 def run(self):
307 """Perform the login request."""
308 url = "http://www.virtualairlines.hu/onlinegates_get.php"
309
310 f = urllib2.urlopen(url)
311 result = Result()
312 result.fleet = Fleet(f)
313 f.close()
314
315 return result
316
317#------------------------------------------------------------------------------
318
319class UpdatePlane(Request):
320 """Update the status of one of the planes in the fleet."""
321 def __init__(self, callback, tailNumber, status, gateNumber = None):
322 """Construct the request."""
323 super(UpdatePlane, self).__init__(callback)
324 self._tailNumber = tailNumber
325 self._status = status
326 self._gateNumber = gateNumber
327
328 def run(self):
329 """Perform the plane update."""
330 url = "http://www.virtualairlines.hu/onlinegates_set.php"
331
332 status = "H" if self._status==const.PLANE_HOME else \
333 "A" if self._status==const.PLANE_AWAY else \
334 "P" if self._status==const.PLANE_PARKING else ""
335
336 gateNumber = self._gateNumber if self._gateNumber else ""
337
338 data = urllib.urlencode([("lajstrom", self._tailNumber),
339 ("status", status),
340 ("kapu", gateNumber)])
341
342 f = urllib2.urlopen(url, data)
343 line = readline(f)
344
345 result = Result()
346 result.success = line == "OK"
347
348 return result
349
350#------------------------------------------------------------------------------
351
352class Handler(threading.Thread):
353 """The handler for the web services.
354
355 It can process one request at a time. The results are passed to a callback
356 function."""
357 def __init__(self):
358 """Construct the handler."""
359 super(Handler, self).__init__()
360
361 self._requests = []
362 self._requestCondition = threading.Condition()
363
364 self.daemon = True
365
366 def login(self, callback, pilotID, password):
367 """Enqueue a login request."""
368 self._addRequest(Login(callback, pilotID, password))
369
370 def getFleet(self, callback):
371 """Enqueue a fleet retrieval request."""
372 self._addRequest(GetFleet(callback))
373
374 def updatePlane(self, callback, tailNumber, status, gateNumber = None):
375 """Update the status of the given plane."""
376 self._addRequest(UpdatePlane(callback, tailNumber, status, gateNumber))
377
378 def run(self):
379 """Process the requests."""
380 while True:
381 with self._requestCondition:
382 while not self._requests:
383 self._requestCondition.wait()
384 request = self._requests[0]
385 del self._requests[0]
386
387 request.perform()
388
389 def _addRequest(self, request):
390 """Add the given request to the queue."""
391 with self._requestCondition:
392 self._requests.append(request)
393 self._requestCondition.notify()
394
395#------------------------------------------------------------------------------
396
397if __name__ == "__main__":
398 import time
399
400 def callback(returned, result):
401 print returned, unicode(result)
402
403 handler = Handler()
404 handler.start()
405
406 #handler.login(callback, "P096", "V5fwj")
407 #handler.getFleet(callback)
408 # Plane: HA-LEG home (gate 67)
409 handler.updatePlane(callback, "HA-LQC", const.PLANE_AWAY, "72")
410 time.sleep(3)
411 handler.getFleet(callback)
412 time.sleep(3)
413
414#------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.